diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 68a6e692a46..1f8fa6f835f 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -14,6 +14,7 @@ Fixes # (issue) - [ ] I have performed a self-review of my own code - [ ] I have run [clang-format](https://docs.openmc.org/en/latest/devguide/styleguide.html#automatic-formatting) (version 15) on any C++ source files (if applicable) +- [ ] I have run [black](https://black.readthedocs.io/en/stable/index.html) on any Python source files (if applicable) - [ ] I have followed the [style guidelines](https://docs.openmc.org/en/latest/devguide/styleguide.html#python) for Python source files (if applicable) - [ ] I have made corresponding changes to the documentation (if applicable) - [ ] I have added tests that prove my fix is effective or that my feature works (if applicable) diff --git a/.github/workflows/format-check.yml b/.github/workflows/format-check.yml index cef14ca2c8c..92b3989449d 100644 --- a/.github/workflows/format-check.yml +++ b/.github/workflows/format-check.yml @@ -1,4 +1,4 @@ -name: C++ Format Check +name: Format Check on: # allow workflow to be run manually @@ -30,3 +30,16 @@ jobs: - name: Failure Check if: steps.linter.outputs.checks-failed > 0 run: echo "Some files failed the formatting check! See job summary and file annotations for more info" && exit 1 + + python-format-test: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: 3.12 + - run: pip install .[format] + name: install black + - run: black --check openmc/ tests/ + name: Check if anything needs to be black formatted diff --git a/README.md b/README.md index 6539ec3c7c5..43f92d18789 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ [![dockerhub-publish-develop-dagmc](https://github.com/openmc-dev/openmc/workflows/dockerhub-publish-develop-dagmc/badge.svg)](https://github.com/openmc-dev/openmc/actions?query=workflow%3Adockerhub-publish-develop-dagmc) [![dockerhub-publish-develop](https://github.com/openmc-dev/openmc/workflows/dockerhub-publish-develop/badge.svg)](https://github.com/openmc-dev/openmc/actions?query=workflow%3Adockerhub-publish-develop) [![conda-pacakge](https://anaconda.org/conda-forge/openmc/badges/version.svg)](https://anaconda.org/conda-forge/openmc) +[![black-style](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) The OpenMC project aims to provide a fully-featured Monte Carlo particle transport code based on modern methods. It is a constructive solid geometry, diff --git a/docs/source/devguide/styleguide.rst b/docs/source/devguide/styleguide.rst index b266f5f0222..3428b692a00 100644 --- a/docs/source/devguide/styleguide.rst +++ b/docs/source/devguide/styleguide.rst @@ -142,11 +142,11 @@ Doxygen commands, e.g., ``\brief`` instead of ``@brief``. Python ------ -Style for Python code should follow PEP8_. +Style for Python code should follow PEP8_, and be formatted with black_. Docstrings for functions and methods should follow numpydoc_ style. -Python code should work with Python 3.8+. +Python code should work with Python 3.11+. Use of third-party Python packages should be limited to numpy_, scipy_, matplotlib_, pandas_, and h5py_. Use of other third-party packages must be @@ -167,3 +167,4 @@ represent a filesystem path should work with both strings and Path_ objects. .. _pathlib: https://docs.python.org/3/library/pathlib.html .. _os: https://docs.python.org/3/library/os.html .. _Path: https://docs.python.org/3/library/pathlib.html#pathlib.Path +.. _black: https://black.readthedocs.io/en/stable/index.html diff --git a/openmc/_xml.py b/openmc/_xml.py index b40ecb258a9..4a7cb97428f 100644 --- a/openmc/_xml.py +++ b/openmc/_xml.py @@ -14,7 +14,7 @@ def clean_indentation(element, level=0, spaces_per_level=2, trailing_indent=True Whether or not to add indentation after closing the element """ - i = "\n" + level*spaces_per_level*" " + i = "\n" + level * spaces_per_level * " " # ensure there's always some tail for the element passed in if not element.tail: @@ -22,7 +22,7 @@ def clean_indentation(element, level=0, spaces_per_level=2, trailing_indent=True if len(element): if not element.text or not element.text.strip(): - element.text = i + spaces_per_level*" " + element.text = i + spaces_per_level * " " if trailing_indent and (not element.tail or not element.tail.strip()): element.tail = i for sub_element in element: @@ -30,7 +30,7 @@ def clean_indentation(element, level=0, spaces_per_level=2, trailing_indent=True # call. Any child element of the topmost element should add # indentation at the end to ensure its parent's indentation is # correct. - clean_indentation(sub_element, level+1, spaces_per_level) + clean_indentation(sub_element, level + 1, spaces_per_level) if not sub_element.tail or not sub_element.tail.strip(): sub_element.tail = i else: @@ -82,7 +82,7 @@ def reorder_attributes(root): def get_elem_tuple(elem, name, dtype=int): - '''Helper function to get a tuple of values from an elem + """Helper function to get a tuple of values from an elem Parameters ---------- @@ -97,7 +97,7 @@ def get_elem_tuple(elem, name, dtype=int): ------- tuple of dtype Data read from the tuple - ''' + """ subelem = elem.find(name) if subelem is not None: return tuple([dtype(x) for x in subelem.text.split()]) diff --git a/openmc/arithmetic.py b/openmc/arithmetic.py index 821014e36df..150903c71ac 100644 --- a/openmc/arithmetic.py +++ b/openmc/arithmetic.py @@ -10,10 +10,10 @@ # Acceptable tally arithmetic binary operations -_TALLY_ARITHMETIC_OPS = {'+', '-', '*', '/', '^'} +_TALLY_ARITHMETIC_OPS = {"+", "-", "*", "/", "^"} # Acceptable tally aggregation operations -_TALLY_AGGREGATE_OPS = {'sum', 'avg'} +_TALLY_AGGREGATE_OPS = {"sum", "avg"} class CrossScore: @@ -54,7 +54,7 @@ def __eq__(self, other): return str(other) == str(self) def __repr__(self): - return f'({self.left_score} {self.binary_op} {self.right_score})' + return f"({self.left_score} {self.binary_op} {self.right_score})" @property def left_score(self): @@ -62,8 +62,7 @@ def left_score(self): @left_score.setter def left_score(self, left_score): - cv.check_type('left_score', left_score, - (str, CrossScore, AggregateScore)) + cv.check_type("left_score", left_score, (str, CrossScore, AggregateScore)) self._left_score = left_score @property @@ -72,8 +71,7 @@ def right_score(self): @right_score.setter def right_score(self, right_score): - cv.check_type('right_score', right_score, - (str, CrossScore, AggregateScore)) + cv.check_type("right_score", right_score, (str, CrossScore, AggregateScore)) self._right_score = right_score @property @@ -82,8 +80,8 @@ def binary_op(self): @binary_op.setter def binary_op(self, binary_op): - cv.check_type('binary_op', binary_op, str) - cv.check_value('binary_op', binary_op, _TALLY_ARITHMETIC_OPS) + cv.check_type("binary_op", binary_op, str) + cv.check_value("binary_op", binary_op, _TALLY_ARITHMETIC_OPS) self._binary_op = binary_op @@ -133,8 +131,11 @@ def left_nuclide(self): @left_nuclide.setter def left_nuclide(self, left_nuclide): - cv.check_type('left_nuclide', left_nuclide, - (openmc.Nuclide, CrossNuclide, AggregateNuclide)) + cv.check_type( + "left_nuclide", + left_nuclide, + (openmc.Nuclide, CrossNuclide, AggregateNuclide), + ) self._left_nuclide = left_nuclide @property @@ -143,8 +144,11 @@ def right_nuclide(self): @right_nuclide.setter def right_nuclide(self, right_nuclide): - cv.check_type('right_nuclide', right_nuclide, - (openmc.Nuclide, CrossNuclide, AggregateNuclide)) + cv.check_type( + "right_nuclide", + right_nuclide, + (openmc.Nuclide, CrossNuclide, AggregateNuclide), + ) self._right_nuclide = right_nuclide @property @@ -153,30 +157,30 @@ def binary_op(self): @binary_op.setter def binary_op(self, binary_op): - cv.check_type('binary_op', binary_op, str) - cv.check_value('binary_op', binary_op, _TALLY_ARITHMETIC_OPS) + cv.check_type("binary_op", binary_op, str) + cv.check_value("binary_op", binary_op, _TALLY_ARITHMETIC_OPS) self._binary_op = binary_op @property def name(self): - string = '' + string = "" # If the Summary was linked, the left nuclide is a Nuclide object if isinstance(self.left_nuclide, openmc.Nuclide): - string += '(' + self.left_nuclide.name + string += "(" + self.left_nuclide.name # If the Summary was not linked, the left nuclide is the ZAID else: - string += '(' + str(self.left_nuclide) + string += "(" + str(self.left_nuclide) - string += ' ' + self.binary_op + ' ' + string += " " + self.binary_op + " " # If the Summary was linked, the right nuclide is a Nuclide object if isinstance(self.right_nuclide, openmc.Nuclide): - string += self.right_nuclide.name + ')' + string += self.right_nuclide.name + ")" # If the Summary was not linked, the right nuclide is the ZAID else: - string += str(self.right_nuclide) + ')' + string += str(self.right_nuclide) + ")" return string @@ -226,15 +230,15 @@ def __eq__(self, other): return str(other) == str(self) def __repr__(self): - filter_bins = '({} {} {})'.format(self.left_filter.bins, - self.binary_op, - self.right_filter.bins) + filter_bins = "({} {} {})".format( + self.left_filter.bins, self.binary_op, self.right_filter.bins + ) parts = [ - 'CrossFilter', - '{: <16}=\t{}'.format('\tType', self.type), - '{: <16}=\t{}'.format('\tBins', filter_bins) + "CrossFilter", + "{: <16}=\t{}".format("\tType", self.type), + "{: <16}=\t{}".format("\tBins", filter_bins), ] - return '\n'.join(parts) + return "\n".join(parts) @property def left_filter(self): @@ -242,8 +246,9 @@ def left_filter(self): @left_filter.setter def left_filter(self, left_filter): - cv.check_type('left_filter', left_filter, - (openmc.Filter, CrossFilter, AggregateFilter)) + cv.check_type( + "left_filter", left_filter, (openmc.Filter, CrossFilter, AggregateFilter) + ) self._left_filter = left_filter @property @@ -252,8 +257,9 @@ def right_filter(self): @right_filter.setter def right_filter(self, right_filter): - cv.check_type('right_filter', right_filter, - (openmc.Filter, CrossFilter, AggregateFilter)) + cv.check_type( + "right_filter", right_filter, (openmc.Filter, CrossFilter, AggregateFilter) + ) self._right_filter = right_filter @property @@ -262,15 +268,15 @@ def binary_op(self): @binary_op.setter def binary_op(self, binary_op): - cv.check_type('binary_op', binary_op, str) - cv.check_value('binary_op', binary_op, _TALLY_ARITHMETIC_OPS) + cv.check_type("binary_op", binary_op, str) + cv.check_value("binary_op", binary_op, _TALLY_ARITHMETIC_OPS) self._binary_op = binary_op @property def type(self): left_type = self.left_filter.type right_type = self.right_filter.type - return f'({left_type} {self.binary_op} {right_type})' + return f"({left_type} {self.binary_op} {right_type})" @property def bins(self): @@ -359,7 +365,7 @@ def get_pandas_dataframe(self, data_size, summary=None): right_df = self.right_filter.get_pandas_dataframe(data_size, summary) left_df = left_df.astype(str) right_df = right_df.astype(str) - df = f'({left_df} {self.binary_op} {right_df})' + df = f"({left_df} {self.binary_op} {right_df})" return df @@ -403,8 +409,8 @@ def __eq__(self, other): return str(other) == str(self) def __repr__(self): - string = ', '.join(map(str, self.scores)) - string = f'{self.aggregate_op}({string})' + string = ", ".join(map(str, self.scores)) + string = f"{self.aggregate_op}({string})" return string @property @@ -413,7 +419,7 @@ def scores(self): @scores.setter def scores(self, scores): - cv.check_iterable_type('scores', scores, str) + cv.check_iterable_type("scores", scores, str) self._scores = scores @property @@ -422,15 +428,15 @@ def aggregate_op(self): @aggregate_op.setter def aggregate_op(self, aggregate_op): - cv.check_type('aggregate_op', aggregate_op, (str, CrossScore)) - cv.check_value('aggregate_op', aggregate_op, _TALLY_AGGREGATE_OPS) + cv.check_type("aggregate_op", aggregate_op, (str, CrossScore)) + cv.check_value("aggregate_op", aggregate_op, _TALLY_AGGREGATE_OPS) self._aggregate_op = aggregate_op @property def name(self): # Append each score in the aggregate to the string - string = '(' + ', '.join(self.scores) + ')' + string = "(" + ", ".join(self.scores) + ")" return string @@ -475,10 +481,12 @@ def __eq__(self, other): def __repr__(self): # Append each nuclide in the aggregate to the string - string = f'{self.aggregate_op}(' - names = [nuclide.name if isinstance(nuclide, openmc.Nuclide) - else str(nuclide) for nuclide in self.nuclides] - string += ', '.join(map(str, names)) + ')' + string = f"{self.aggregate_op}(" + names = [ + nuclide.name if isinstance(nuclide, openmc.Nuclide) else str(nuclide) + for nuclide in self.nuclides + ] + string += ", ".join(map(str, names)) + ")" return string @property @@ -487,7 +495,7 @@ def nuclides(self): @nuclides.setter def nuclides(self, nuclides): - cv.check_iterable_type('nuclides', nuclides, (str, CrossNuclide)) + cv.check_iterable_type("nuclides", nuclides, (str, CrossNuclide)) self._nuclides = nuclides @property @@ -496,17 +504,19 @@ def aggregate_op(self): @aggregate_op.setter def aggregate_op(self, aggregate_op): - cv.check_type('aggregate_op', aggregate_op, str) - cv.check_value('aggregate_op', aggregate_op, _TALLY_AGGREGATE_OPS) + cv.check_type("aggregate_op", aggregate_op, str) + cv.check_value("aggregate_op", aggregate_op, _TALLY_AGGREGATE_OPS) self._aggregate_op = aggregate_op @property def name(self): # Append each nuclide in the aggregate to the string - names = [nuclide.name if isinstance(nuclide, openmc.Nuclide) - else str(nuclide) for nuclide in self.nuclides] - string = '(' + ', '.join(map(str, names)) + ')' + names = [ + nuclide.name if isinstance(nuclide, openmc.Nuclide) else str(nuclide) + for nuclide in self.nuclides + ] + string = "(" + ", ".join(map(str, names)) + ")" return string @@ -542,7 +552,7 @@ class AggregateFilter: def __init__(self, aggregate_filter, bins=None, aggregate_op=None): - self._type = f'{aggregate_op}({aggregate_filter.short_name.lower()})' + self._type = f"{aggregate_op}({aggregate_filter.short_name.lower()})" self._bins = None self._aggregate_filter = None @@ -562,10 +572,13 @@ def __eq__(self, other): def __gt__(self, other): if self.type != other.type: - if self.aggregate_filter.type in _FILTER_TYPES and \ - other.aggregate_filter.type in _FILTER_TYPES: - delta = _FILTER_TYPES.index(self.aggregate_filter.type) - \ - _FILTER_TYPES.index(other.aggregate_filter.type) + if ( + self.aggregate_filter.type in _FILTER_TYPES + and other.aggregate_filter.type in _FILTER_TYPES + ): + delta = _FILTER_TYPES.index( + self.aggregate_filter.type + ) - _FILTER_TYPES.index(other.aggregate_filter.type) return delta > 0 else: return False @@ -577,11 +590,11 @@ def __lt__(self, other): def __repr__(self): parts = [ - 'AggregateFilter', - '{: <16}=\t{}'.format('\tType', self.type), - '{: <16}=\t{}'.format('\tBins', self.bins) + "AggregateFilter", + "{: <16}=\t{}".format("\tType", self.type), + "{: <16}=\t{}".format("\tBins", self.bins), ] - return '\n'.join(parts) + return "\n".join(parts) @property def aggregate_filter(self): @@ -589,8 +602,9 @@ def aggregate_filter(self): @aggregate_filter.setter def aggregate_filter(self, aggregate_filter): - cv.check_type('aggregate_filter', aggregate_filter, - (openmc.Filter, CrossFilter)) + cv.check_type( + "aggregate_filter", aggregate_filter, (openmc.Filter, CrossFilter) + ) self._aggregate_filter = aggregate_filter @property @@ -599,8 +613,8 @@ def aggregate_op(self): @aggregate_op.setter def aggregate_op(self, aggregate_op): - cv.check_type('aggregate_op', aggregate_op, str) - cv.check_value('aggregate_op', aggregate_op, _TALLY_AGGREGATE_OPS) + cv.check_type("aggregate_op", aggregate_op, str) + cv.check_value("aggregate_op", aggregate_op, _TALLY_AGGREGATE_OPS) self._aggregate_op = aggregate_op @property @@ -610,8 +624,10 @@ def type(self): @type.setter def type(self, filter_type): if filter_type not in _FILTER_TYPES: - msg = f'Unable to set AggregateFilter type to "{filter_type}" ' \ - 'since it is not one of the supported types' + msg = ( + f'Unable to set AggregateFilter type to "{filter_type}" ' + "since it is not one of the supported types" + ) raise ValueError(msg) self._type = filter_type @@ -622,7 +638,7 @@ def bins(self): @bins.setter def bins(self, bins): - cv.check_iterable_type('bins', bins, Iterable) + cv.check_iterable_type("bins", bins, Iterable) self._bins = list(map(tuple, bins)) @property @@ -662,8 +678,10 @@ def get_bin_index(self, filter_bin): """ if filter_bin not in self.bins: - msg = ('Unable to get the bin index for AggregateFilter since ' - f'"{filter_bin}" is not one of the bins') + msg = ( + "Unable to get the bin index for AggregateFilter since " + f'"{filter_bin}" is not one of the bins' + ) raise ValueError(msg) else: return self.bins.index(filter_bin) @@ -769,7 +787,7 @@ def merge(self, other): merged_bins = self.bins + other.bins # Sort energy bin edges - if 'energy' in self.type: + if "energy" in self.type: merged_bins = sorted(merged_bins) # Assign merged bins to merged filter diff --git a/openmc/bounding_box.py b/openmc/bounding_box.py index f0dc06a4a04..2777da2bc3e 100644 --- a/openmc/bounding_box.py +++ b/openmc/bounding_box.py @@ -43,7 +43,8 @@ def __init__(self, lower_left: Iterable[float], upper_right: Iterable[float]): def __repr__(self) -> str: return "BoundingBox(lower_left={}, upper_right={})".format( tuple(float(x) for x in self.lower_left), - tuple(float(x) for x in self.upper_right)) + tuple(float(x) for x in self.upper_right), + ) def __getitem__(self, key) -> np.ndarray: return self._bounds[key] @@ -124,7 +125,7 @@ def lower_left(self) -> np.ndarray: @lower_left.setter def lower_left(self, llc): - check_length('lower_left', llc, 3, 3) + check_length("lower_left", llc, 3, 3) self[0] = llc @property @@ -133,7 +134,7 @@ def upper_right(self) -> np.ndarray: @upper_right.setter def upper_right(self, urc): - check_length('upper_right', urc, 3, 3) + check_length("upper_right", urc, 3, 3) self[1] = urc @property diff --git a/openmc/cell.py b/openmc/cell.py index fe3939bbe39..612c5f0179e 100644 --- a/openmc/cell.py +++ b/openmc/cell.py @@ -99,7 +99,7 @@ class Cell(IDManagerMixin): next_id = 1 used_ids = set() - def __init__(self, cell_id=None, name='', fill=None, region=None): + def __init__(self, cell_id=None, name="", fill=None, region=None): # Initialize Cell class attributes self.id = cell_id self.name = name @@ -121,27 +121,27 @@ def __contains__(self, point): return point in self.region def __repr__(self): - string = 'Cell\n' - string += '{: <16}=\t{}\n'.format('\tID', self.id) - string += '{: <16}=\t{}\n'.format('\tName', self.name) - - if self.fill_type == 'material': - string += '{: <16}=\tMaterial {}\n'.format('\tFill', self.fill.id) - elif self.fill_type == 'void': - string += '{: <16}=\tNone\n'.format('\tFill') - elif self.fill_type == 'distribmat': - string += '{: <16}=\t{}\n'.format('\tFill', list(map( - lambda m: m if m is None else m.id, self.fill))) + string = "Cell\n" + string += "{: <16}=\t{}\n".format("\tID", self.id) + string += "{: <16}=\t{}\n".format("\tName", self.name) + + if self.fill_type == "material": + string += "{: <16}=\tMaterial {}\n".format("\tFill", self.fill.id) + elif self.fill_type == "void": + string += "{: <16}=\tNone\n".format("\tFill") + elif self.fill_type == "distribmat": + string += "{: <16}=\t{}\n".format( + "\tFill", list(map(lambda m: m if m is None else m.id, self.fill)) + ) else: - string += '{: <16}=\t{}\n'.format('\tFill', self.fill.id) + string += "{: <16}=\t{}\n".format("\tFill", self.fill.id) - string += '{: <16}=\t{}\n'.format('\tRegion', self.region) - string += '{: <16}=\t{}\n'.format('\tRotation', self.rotation) - if self.fill_type == 'material': - string += '\t{0: <15}=\t{1}\n'.format('Temperature', - self.temperature) - string += '{: <16}=\t{}\n'.format('\tTranslation', self.translation) - string += '{: <16}=\t{}\n'.format('\tVolume', self.volume) + string += "{: <16}=\t{}\n".format("\tRegion", self.region) + string += "{: <16}=\t{}\n".format("\tRotation", self.rotation) + if self.fill_type == "material": + string += "\t{0: <15}=\t{1}\n".format("Temperature", self.temperature) + string += "{: <16}=\t{}\n".format("\tTranslation", self.translation) + string += "{: <16}=\t{}\n".format("\tVolume", self.volume) return string @@ -152,10 +152,10 @@ def name(self): @name.setter def name(self, name): if name is not None: - cv.check_type('cell name', name, str) + cv.check_type("cell name", name, str) self._name = name else: - self._name = '' + self._name = "" @property def fill(self): @@ -167,12 +167,15 @@ def fill(self, fill): if isinstance(fill, Iterable): for i, f in enumerate(fill): if f is not None: - cv.check_type('cell.fill[i]', f, openmc.Material) - - elif not isinstance(fill, (openmc.Material, openmc.Lattice, - openmc.UniverseBase)): - msg = (f'Unable to set Cell ID="{self._id}" to use a ' - f'non-Material or Universe fill "{fill}"') + cv.check_type("cell.fill[i]", f, openmc.Material) + + elif not isinstance( + fill, (openmc.Material, openmc.Lattice, openmc.UniverseBase) + ): + msg = ( + f'Unable to set Cell ID="{self._id}" to use a ' + f'non-Material or Universe fill "{fill}"' + ) raise ValueError(msg) self._fill = fill @@ -183,15 +186,15 @@ def fill(self, fill): @property def fill_type(self): if isinstance(self.fill, openmc.Material): - return 'material' + return "material" elif isinstance(self.fill, openmc.UniverseBase): - return 'universe' + return "universe" elif isinstance(self.fill, openmc.Lattice): - return 'lattice' + return "lattice" elif isinstance(self.fill, Iterable): - return 'distribmat' + return "distribmat" else: - return 'void' + return "void" @property def region(self): @@ -200,7 +203,7 @@ def region(self): @region.setter def region(self, region): if region is not None: - cv.check_type('cell region', region, Region) + cv.check_type("cell region", region, Region) self._region = region @property @@ -209,7 +212,7 @@ def rotation(self): @rotation.setter def rotation(self, rotation): - cv.check_length('cell rotation', rotation, 3) + cv.check_length("cell rotation", rotation, 3) self._rotation = np.asarray(rotation) # Save rotation matrix -- the reason we do this instead of having it be @@ -219,14 +222,17 @@ def rotation(self, rotation): # User specified rotation matrix directly self._rotation_matrix = self._rotation else: - phi, theta, psi = self.rotation*(-pi/180.) + phi, theta, psi = self.rotation * (-pi / 180.0) c3, s3 = cos(phi), sin(phi) c2, s2 = cos(theta), sin(theta) c1, s1 = cos(psi), sin(psi) - self._rotation_matrix = np.array([ - [c1*c2, c1*s2*s3 - c3*s1, s1*s3 + c1*c3*s2], - [c2*s1, c1*c3 + s1*s2*s3, c3*s1*s2 - c1*s3], - [-s2, c2*s3, c2*c3]]) + self._rotation_matrix = np.array( + [ + [c1 * c2, c1 * s2 * s3 - c3 * s1, s1 * s3 + c1 * c3 * s2], + [c2 * s1, c1 * c3 + s1 * s2 * s3, c3 * s1 * s2 - c1 * s3], + [-s2, c2 * s3, c2 * c3], + ] + ) @property def rotation_matrix(self): @@ -239,19 +245,19 @@ def temperature(self): @temperature.setter def temperature(self, temperature): # Make sure temperatures are positive - cv.check_type('cell temperature', temperature, (Iterable, Real), none_ok=True) + cv.check_type("cell temperature", temperature, (Iterable, Real), none_ok=True) if isinstance(temperature, Iterable): - cv.check_type('cell temperature', temperature, Iterable, Real) + cv.check_type("cell temperature", temperature, Iterable, Real) for T in temperature: - cv.check_greater_than('cell temperature', T, 0.0, True) + cv.check_greater_than("cell temperature", T, 0.0, True) elif isinstance(temperature, Real): - cv.check_greater_than('cell temperature', temperature, 0.0, True) + cv.check_greater_than("cell temperature", temperature, 0.0, True) # If this cell is filled with a universe or lattice, propagate # temperatures to all cells contained. Otherwise, simply assign it. - if self.fill_type in ('universe', 'lattice'): + if self.fill_type in ("universe", "lattice"): for c in self.get_all_cells().values(): - if c.fill_type == 'material': + if c.fill_type == "material": c._temperature = temperature else: self._temperature = temperature @@ -262,8 +268,8 @@ def translation(self): @translation.setter def translation(self, translation): - cv.check_type('cell translation', translation, Iterable, Real) - cv.check_length('cell translation', translation, 3) + cv.check_type("cell translation", translation, Iterable, Real) + cv.check_length("cell translation", translation, 3) self._translation = np.asarray(translation) @property @@ -273,8 +279,8 @@ def volume(self): @volume.setter def volume(self, volume): if volume is not None: - cv.check_type('cell volume', volume, (Real, UFloat)) - cv.check_greater_than('cell volume', volume, 0.0, equality=True) + cv.check_type("cell volume", volume, (Real, UFloat)) + cv.check_greater_than("cell volume", volume, 0.0, equality=True) self._volume = volume @@ -286,32 +292,38 @@ def volume(self, volume): def atoms(self): if self._atoms is None: if self._volume is None: - msg = ('Cannot calculate atom content because no volume ' - 'is set. Use Cell.volume to provide it or perform ' - 'a stochastic volume calculation.') + msg = ( + "Cannot calculate atom content because no volume " + "is set. Use Cell.volume to provide it or perform " + "a stochastic volume calculation." + ) raise ValueError(msg) - elif self.fill_type == 'void': - msg = ('Cell is filled with void. It contains no atoms. ' - 'Material must be set to calculate atom content.') + elif self.fill_type == "void": + msg = ( + "Cell is filled with void. It contains no atoms. " + "Material must be set to calculate atom content." + ) raise ValueError(msg) - elif self.fill_type in ['lattice', 'universe']: - msg = ('Universe and Lattice cells can contain multiple ' - 'materials in diffrent proportions. Atom content must ' - 'be calculated with stochastic volume calculation.') + elif self.fill_type in ["lattice", "universe"]: + msg = ( + "Universe and Lattice cells can contain multiple " + "materials in diffrent proportions. Atom content must " + "be calculated with stochastic volume calculation." + ) raise ValueError(msg) - elif self.fill_type == 'material': + elif self.fill_type == "material": # Get atomic densities self._atoms = self._fill.get_nuclide_atom_densities() # Convert to total number of atoms for key, atom_per_bcm in self._atoms.items(): - atom = atom_per_bcm * self._volume * 1.0e+24 + atom = atom_per_bcm * self._volume * 1.0e24 self._atoms[key] = atom - elif self.fill_type == 'distribmat': + elif self.fill_type == "distribmat": # Assumes that volume is total volume of all instances # Also assumes that all instances have the same volume partial_volume = self.volume / len(self.fill) @@ -322,11 +334,11 @@ def atoms(self): # we need to append new atoms to any existing value # hence it is necessary to ask for default. atom = self._atoms.setdefault(key, 0) - atom += atom_per_bcm * partial_volume * 1.0e+24 + atom += atom_per_bcm * partial_volume * 1.0e24 self._atoms[key] = atom else: - msg = f'Unrecognized fill_type: {self.fill_type}' + msg = f"Unrecognized fill_type: {self.fill_type}" raise ValueError(msg) return self._atoms @@ -334,8 +346,10 @@ def atoms(self): @property def paths(self): if self._paths is None: - raise ValueError('Cell instance paths have not been determined. ' - 'Call the Geometry.determine_paths() method.') + raise ValueError( + "Cell instance paths have not been determined. " + "Call the Geometry.determine_paths() method." + ) return self._paths @property @@ -349,8 +363,9 @@ def bounding_box(self): def num_instances(self): if self._num_instances is None: raise ValueError( - 'Number of cell instances have not been determined. Call the ' - 'Geometry.determine_paths() method.') + "Number of cell instances have not been determined. Call the " + "Geometry.determine_paths() method." + ) return self._num_instances def add_volume_information(self, volume_calc): @@ -362,14 +377,14 @@ def add_volume_information(self, volume_calc): Results from a stochastic volume calculation """ - if volume_calc.domain_type == 'cell': + if volume_calc.domain_type == "cell": if self.id in volume_calc.volumes: self._volume = volume_calc.volumes[self.id].n self._atoms = volume_calc.atoms[self.id] else: - raise ValueError('No volume information found for this cell.') + raise ValueError("No volume information found for this cell.") else: - raise ValueError('No volume information found for this cell.') + raise ValueError("No volume information found for this cell.") def get_nuclides(self): """Returns all nuclides in the cell @@ -380,7 +395,7 @@ def get_nuclides(self): List of nuclide names """ - return self.fill.get_nuclides() if self.fill_type != 'void' else [] + return self.fill.get_nuclides() if self.fill_type != "void" else [] def get_nuclide_densities(self): """Return all nuclides contained in the cell and their densities @@ -395,23 +410,24 @@ def get_nuclide_densities(self): nuclides = {} - if self.fill_type == 'material': + if self.fill_type == "material": nuclides.update(self.fill.get_nuclide_densities()) - elif self.fill_type == 'void': + elif self.fill_type == "void": pass else: if self._atoms is not None: volume = self.volume for name, atoms in self._atoms.items(): nuclide = openmc.Nuclide(name) - density = 1.0e-24 * atoms.n/volume # density in atoms/b-cm + density = 1.0e-24 * atoms.n / volume # density in atoms/b-cm nuclides[name] = (nuclide, density) else: raise RuntimeError( - 'Volume information is needed to calculate microscopic ' - f'cross sections for cell {self.id}. This can be done by ' - 'running a stochastic volume calculation via the ' - 'openmc.VolumeCalculation object') + "Volume information is needed to calculate microscopic " + f"cross sections for cell {self.id}. This can be done by " + "running a stochastic volume calculation via the " + "openmc.VolumeCalculation object" + ) return nuclides @@ -433,7 +449,7 @@ def get_all_cells(self, memo=None): memo.add(self) cells = {} - if self.fill_type in ('universe', 'lattice'): + if self.fill_type in ("universe", "lattice"): cells.update(self.fill.get_all_cells(memo)) return cells @@ -449,9 +465,9 @@ def get_all_materials(self, memo=None): """ materials = {} - if self.fill_type == 'material': + if self.fill_type == "material": materials[self.fill.id] = self.fill - elif self.fill_type == 'distribmat': + elif self.fill_type == "distribmat": for m in self.fill: if m is not None: materials[m.id] = m @@ -481,10 +497,10 @@ def get_all_universes(self, memo=None): memo.add(self) universes = {} - if self.fill_type == 'universe': + if self.fill_type == "universe": universes[self.fill.id] = self.fill universes.update(self.fill.get_all_universes(memo)) - elif self.fill_type == 'lattice': + elif self.fill_type == "lattice": universes.update(self.fill.get_all_universes(memo)) return universes @@ -540,20 +556,21 @@ def clone(self, clone_materials=True, clone_regions=True, memo=None): else: clone.region = self.region if self.fill is not None: - if self.fill_type == 'distribmat': + if self.fill_type == "distribmat": if not clone_materials: clone.fill = self.fill else: - clone.fill = [fill.clone(memo) if fill is not None else - None for fill in self.fill] - elif self.fill_type == 'material': + clone.fill = [ + fill.clone(memo) if fill is not None else None + for fill in self.fill + ] + elif self.fill_type == "material": if not clone_materials: clone.fill = self.fill else: clone.fill = self.fill.clone(memo) else: - clone.fill = self.fill.clone(clone_materials, - clone_regions, memo) + clone.fill = self.fill.clone(clone_materials, clone_regions, memo) # Memoize the clone memo[self] = clone @@ -650,24 +667,26 @@ def create_xml_subelement(self, xml_element, memo=None): if len(self._name) > 0: element.set("name", str(self.name)) - if self.fill_type == 'void': + if self.fill_type == "void": element.set("material", "void") - elif self.fill_type == 'material': + elif self.fill_type == "material": element.set("material", str(self.fill.id)) - elif self.fill_type == 'distribmat': - element.set("material", ' '.join(['void' if m is None else str(m.id) - for m in self.fill])) + elif self.fill_type == "distribmat": + element.set( + "material", + " ".join(["void" if m is None else str(m.id) for m in self.fill]), + ) - elif self.fill_type in ('universe', 'lattice'): + elif self.fill_type in ("universe", "lattice"): element.set("fill", str(self.fill.id)) self.fill.create_xml_subelement(xml_element, memo) if self.region is not None: # Set the region attribute with the region specification region = str(self.region) - if region.startswith('('): + if region.startswith("("): region = region[1:-1] if len(region) > 0: element.set("region", region) @@ -698,16 +717,15 @@ def create_surface_elements(node, element, memo=None): if self.temperature is not None: if isinstance(self.temperature, Iterable): - element.set("temperature", ' '.join( - str(t) for t in self.temperature)) + element.set("temperature", " ".join(str(t) for t in self.temperature)) else: element.set("temperature", str(self.temperature)) if self.translation is not None: - element.set("translation", ' '.join(map(str, self.translation))) + element.set("translation", " ".join(map(str, self.translation))) if self.rotation is not None: - element.set("rotation", ' '.join(map(str, self.rotation.ravel()))) + element.set("rotation", " ".join(map(str, self.rotation.ravel()))) if self.volume is not None: element.set("volume", str(self.volume)) @@ -737,12 +755,12 @@ def from_xml_element(cls, elem, surfaces, materials, get_universe): Cell instance """ - cell_id = int(get_text(elem, 'id')) - name = get_text(elem, 'name') + cell_id = int(get_text(elem, "id")) + name = get_text(elem, "name") c = cls(cell_id, name) # Assign material/distributed materials or fill - mat_text = get_text(elem, 'material') + mat_text = get_text(elem, "material") if mat_text is not None: mat_ids = mat_text.split() if len(mat_ids) > 1: @@ -750,33 +768,33 @@ def from_xml_element(cls, elem, surfaces, materials, get_universe): else: c.fill = materials[mat_ids[0]] else: - fill_id = int(get_text(elem, 'fill')) + fill_id = int(get_text(elem, "fill")) c.fill = get_universe(fill_id) # Assign region - region = get_text(elem, 'region') + region = get_text(elem, "region") if region is not None: c.region = Region.from_expression(region, surfaces) # Check for other attributes - t = get_text(elem, 'temperature') + t = get_text(elem, "temperature") if t is not None: - if ' ' in t: + if " " in t: c.temperature = [float(t_i) for t_i in t.split()] else: c.temperature = float(t) - v = get_text(elem, 'volume') + v = get_text(elem, "volume") if v is not None: c.volume = float(v) - for key in ('temperature', 'rotation', 'translation'): + for key in ("temperature", "rotation", "translation"): value = get_text(elem, key) if value is not None: values = [float(x) for x in value.split()] - if key == 'rotation' and len(values) == 9: + if key == "rotation" and len(values) == 9: values = np.array(values).reshape(3, 3) setattr(c, key, values) # Add this cell to appropriate universe - univ_id = int(get_text(elem, 'universe', 0)) + univ_id = int(get_text(elem, "universe", 0)) get_universe(univ_id).add_cell(c) return c diff --git a/openmc/checkvalue.py b/openmc/checkvalue.py index 4fa205b14f7..287d2223b6b 100644 --- a/openmc/checkvalue.py +++ b/openmc/checkvalue.py @@ -32,19 +32,26 @@ def check_type(name, value, expected_type, expected_iter_type=None, *, none_ok=F if not isinstance(value, expected_type): if isinstance(expected_type, Iterable): - msg = 'Unable to set "{}" to "{}" which is not one of the ' \ - 'following types: "{}"'.format(name, value, ', '.join( - [t.__name__ for t in expected_type])) + msg = ( + 'Unable to set "{}" to "{}" which is not one of the ' + 'following types: "{}"'.format( + name, value, ", ".join([t.__name__ for t in expected_type]) + ) + ) else: - msg = (f'Unable to set "{name}" to "{value}" which is not of type "' - f'{expected_type.__name__}"') + msg = ( + f'Unable to set "{name}" to "{value}" which is not of type "' + f'{expected_type.__name__}"' + ) raise TypeError(msg) if expected_iter_type: if isinstance(value, np.ndarray): if not issubclass(value.dtype.type, expected_iter_type): - msg = (f'Unable to set "{name}" to "{value}" since each item ' - f'must be of type "{expected_iter_type.__name__}"') + msg = ( + f'Unable to set "{name}" to "{value}" since each item ' + f'must be of type "{expected_iter_type.__name__}"' + ) raise TypeError(msg) else: return @@ -52,13 +59,19 @@ def check_type(name, value, expected_type, expected_iter_type=None, *, none_ok=F for item in value: if not isinstance(item, expected_iter_type): if isinstance(expected_iter_type, Iterable): - msg = 'Unable to set "{}" to "{}" since each item must be ' \ - 'one of the following types: "{}"'.format( - name, value, ', '.join([t.__name__ for t in - expected_iter_type])) + msg = ( + 'Unable to set "{}" to "{}" since each item must be ' + 'one of the following types: "{}"'.format( + name, + value, + ", ".join([t.__name__ for t in expected_iter_type]), + ) + ) else: - msg = (f'Unable to set "{name}" to "{value}" since each ' - f'item must be of type "{expected_iter_type.__name__}"') + msg = ( + f'Unable to set "{name}" to "{value}" since each ' + f'item must be of type "{expected_iter_type.__name__}"' + ) raise TypeError(msg) @@ -97,7 +110,7 @@ def check_iterable_type(name, value, expected_type, min_depth=1, max_depth=1): # Get a string representation of the current index in case we raise an # exception. - form = '[' + '{:d}, ' * (len(index)-1) + '{:d}]' + form = "[" + "{:d}, " * (len(index) - 1) + "{:d}]" ind_str = form.format(*index) # What is the current item we are looking at? @@ -108,8 +121,10 @@ def check_iterable_type(name, value, expected_type, min_depth=1, max_depth=1): if isinstance(current_item, expected_type): # Is this deep enough? if len(tree) < min_depth: - msg = (f'Error setting "{name}": The item at {ind_str} does not ' - f'meet the minimum depth of {min_depth}') + msg = ( + f'Error setting "{name}": The item at {ind_str} does not ' + f"meet the minimum depth of {min_depth}" + ) raise TypeError(msg) # This item is okay. Move on to the next item. @@ -125,16 +140,20 @@ def check_iterable_type(name, value, expected_type, min_depth=1, max_depth=1): # But first, have we exceeded the max depth? if len(tree) > max_depth: - msg = (f'Error setting {name}: Found an iterable at ' - f'{ind_str}, items in that iterable exceed the ' - f'maximum depth of {max_depth}') + msg = ( + f"Error setting {name}: Found an iterable at " + f"{ind_str}, items in that iterable exceed the " + f"maximum depth of {max_depth}" + ) raise TypeError(msg) else: # This item is completely unexpected. - msg = (f'Error setting {name}: Items must be of type ' - f'"{expected_type.__name__}", but item at {ind_str} is ' - f'of type "{type(current_item).__name__}"') + msg = ( + f"Error setting {name}: Items must be of type " + f'"{expected_type.__name__}", but item at {ind_str} is ' + f'of type "{type(current_item).__name__}"' + ) raise TypeError(msg) @@ -157,16 +176,22 @@ def check_length(name, value, length_min, length_max=None): if length_max is None: if len(value) < length_min: - msg = (f'Unable to set "{name}" to "{value}" since it must be at ' - f'least of length "{length_min}"') + msg = ( + f'Unable to set "{name}" to "{value}" since it must be at ' + f'least of length "{length_min}"' + ) raise ValueError(msg) elif not length_min <= len(value) <= length_max: if length_min == length_max: - msg = (f'Unable to set "{name}" to "{value}" since it must be of ' - f'length "{length_min}"') + msg = ( + f'Unable to set "{name}" to "{value}" since it must be of ' + f'length "{length_min}"' + ) else: - msg = (f'Unable to set "{name}" to "{value}" since it must have ' - f'length between "{length_min}" and "{length_max}"') + msg = ( + f'Unable to set "{name}" to "{value}" since it must have ' + f'length between "{length_min}" and "{length_max}"' + ) raise ValueError(msg) @@ -185,12 +210,16 @@ def check_increasing(name: str, value, equality: bool = False): """ if equality: if not np.all(np.diff(value) >= 0.0): - raise ValueError(f'Unable to set "{name}" to "{value}" since its ' - 'elements must be increasing.') + raise ValueError( + f'Unable to set "{name}" to "{value}" since its ' + "elements must be increasing." + ) elif not equality: if not np.all(np.diff(value) > 0.0): - raise ValueError(f'Unable to set "{name}" to "{value}" since its ' - 'elements must be strictly increasing.') + raise ValueError( + f'Unable to set "{name}" to "{value}" since its ' + "elements must be strictly increasing." + ) def check_value(name, value, accepted_values): @@ -208,8 +237,10 @@ def check_value(name, value, accepted_values): """ if value not in accepted_values: - msg = (f'Unable to set "{name}" to "{value}" since it is not in ' - f'"{accepted_values}"') + msg = ( + f'Unable to set "{name}" to "{value}" since it is not in ' + f'"{accepted_values}"' + ) raise ValueError(msg) @@ -231,13 +262,17 @@ def check_less_than(name, value, maximum, equality=False): if equality: if value > maximum: - msg = (f'Unable to set "{name}" to "{value}" since it is greater ' - f'than "{maximum}"') + msg = ( + f'Unable to set "{name}" to "{value}" since it is greater ' + f'than "{maximum}"' + ) raise ValueError(msg) else: if value >= maximum: - msg = (f'Unable to set "{name}" to "{value}" since it is greater ' - f'than or equal to "{maximum}"') + msg = ( + f'Unable to set "{name}" to "{value}" since it is greater ' + f'than or equal to "{maximum}"' + ) raise ValueError(msg) @@ -259,13 +294,17 @@ def check_greater_than(name, value, minimum, equality=False): if equality: if value < minimum: - msg = (f'Unable to set "{name}" to "{value}" since it is less than ' - f'"{minimum}"') + msg = ( + f'Unable to set "{name}" to "{value}" since it is less than ' + f'"{minimum}"' + ) raise ValueError(msg) else: if value <= minimum: - msg = (f'Unable to set "{name}" to "{value}" since it is less than ' - f'or equal to "{minimum}"') + msg = ( + f'Unable to set "{name}" to "{value}" since it is less than ' + f'or equal to "{minimum}"' + ) raise ValueError(msg) @@ -283,24 +322,29 @@ def check_filetype_version(obj, expected_type, expected_version): """ try: - this_filetype = obj.attrs['filetype'].decode() - this_version = obj.attrs['version'] + this_filetype = obj.attrs["filetype"].decode() + this_version = obj.attrs["version"] # Check filetype if this_filetype != expected_type: - raise IOError(f'{obj.filename} is not a {expected_type} file.') + raise IOError(f"{obj.filename} is not a {expected_type} file.") # Check version if this_version[0] != expected_version: - raise IOError('{} file has a version of {} which is not ' - 'consistent with the version expected by OpenMC, {}' - .format(this_filetype, - '.'.join(str(v) for v in this_version), - expected_version)) + raise IOError( + "{} file has a version of {} which is not " + "consistent with the version expected by OpenMC, {}".format( + this_filetype, + ".".join(str(v) for v in this_version), + expected_version, + ) + ) except AttributeError: - raise IOError(f'Could not read {obj.filename} file. This most likely ' - 'means the file was produced by a different version of ' - 'OpenMC than the one you are using.') + raise IOError( + f"Could not read {obj.filename} file. This most likely " + "means the file was produced by a different version of " + "OpenMC than the one you are using." + ) class CheckedList(list): @@ -334,8 +378,7 @@ def __radd__(self, other): return self + other def __iadd__(self, other): - check_type('CheckedList add operand', other, Iterable, - self.expected_type) + check_type("CheckedList add operand", other, Iterable, self.expected_type) for item in other: self.append(item) return self diff --git a/openmc/cmfd.py b/openmc/cmfd.py index eff6a151fbb..32b21523f1c 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -22,23 +22,29 @@ from scipy import sparse import openmc.lib -from .checkvalue import (check_type, check_length, check_value, - check_greater_than, check_less_than) +from .checkvalue import ( + check_type, + check_length, + check_value, + check_greater_than, + check_less_than, +) from .exceptions import OpenMCError # See if mpi4py module can be imported, define have_mpi global variable try: from mpi4py import MPI + have_mpi = True except ImportError: have_mpi = False # Maximum/minimum neutron energies _ENERGY_MAX_NEUTRON = np.inf -_ENERGY_MIN_NEUTRON = 0. +_ENERGY_MIN_NEUTRON = 0.0 # Tolerance for detecting zero flux values -_TINY_BIT = 1.e-8 +_TINY_BIT = 1.0e-8 # For non-accelerated regions on coarse mesh overlay _CMFD_NOACCEL = -1 @@ -48,9 +54,18 @@ # Map that returns index of current direction in numpy current matrix _CURRENTS = { - 'out_left': 0, 'in_left': 1, 'out_right': 2, 'in_right': 3, - 'out_back': 4, 'in_back': 5, 'out_front': 6, 'in_front': 7, - 'out_bottom': 8, 'in_bottom': 9, 'out_top': 10, 'in_top': 11 + "out_left": 0, + "in_left": 1, + "out_right": 2, + "in_right": 3, + "out_back": 4, + "in_back": 5, + "out_front": 6, + "in_front": 7, + "out_bottom": 8, + "in_bottom": 9, + "out_top": 10, + "in_top": 11, } @@ -117,21 +132,31 @@ def __init__(self): self._energy = None self._albedo = None self._map = None - self._mesh_type = 'regular' + 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")) + 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): @@ -178,81 +203,81 @@ def grid(self): @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) + 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') + 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) + 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') + self._display_mesh_warning("regular", "CMFD mesh upper_right") @dimension.setter def dimension(self, dimension): - check_type('CMFD mesh dimension', dimension, Iterable, Integral) - check_length('CMFD mesh dimension', dimension, 2, 3) + check_type("CMFD mesh dimension", dimension, Iterable, Integral) + check_length("CMFD mesh dimension", dimension, 2, 3) for d in dimension: - check_greater_than('CMFD mesh dimension', d, 0) + check_greater_than("CMFD mesh dimension", d, 0) self._dimension = dimension @width.setter def width(self, width): - check_type('CMFD mesh width', width, Iterable, Real) - check_length('CMFD mesh width', width, 2, 3) + check_type("CMFD mesh width", width, Iterable, Real) + check_length("CMFD mesh width", width, 2, 3) for w in width: - check_greater_than('CMFD mesh width', w, 0) + check_greater_than("CMFD mesh width", w, 0) self._width = width - self._display_mesh_warning('regular', 'CMFD mesh width') + self._display_mesh_warning("regular", "CMFD mesh width") @energy.setter def energy(self, energy): - check_type('CMFD mesh energy', energy, Iterable, Real) + check_type("CMFD mesh energy", energy, Iterable, Real) for e in energy: - check_greater_than('CMFD mesh energy', e, 0, True) + check_greater_than("CMFD mesh energy", e, 0, True) self._energy = energy @albedo.setter def albedo(self, albedo): - check_type('CMFD mesh albedo', albedo, Iterable, Real) - check_length('CMFD mesh albedo', albedo, 6) + check_type("CMFD mesh albedo", albedo, Iterable, Real) + check_length("CMFD mesh albedo", albedo, 6) for a in albedo: - check_greater_than('CMFD mesh albedo', a, 0, True) - check_less_than('CMFD mesh albedo', a, 1, True) + check_greater_than("CMFD mesh albedo", a, 0, True) + check_less_than("CMFD mesh albedo", a, 1, True) self._albedo = albedo @map.setter def map(self, mesh_map): - check_type('CMFD mesh map', mesh_map, Iterable, Integral) + check_type("CMFD mesh map", mesh_map, Iterable, Integral) for m in mesh_map: - check_value('CMFD mesh map', m, [0, 1]) + 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']) + 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'] + dims = ["x", "y", "z"] - check_length('CMFD mesh grid', grid, grid_length) + check_length("CMFD mesh grid", grid, grid_length) for i in range(grid_length): - check_type(f'CMFD mesh {dims[i]}-grid', grid[i], Iterable, - Real) - check_greater_than(f'CMFD mesh {dims[i]}-grid length', - len(grid[i]), 1) + check_type(f"CMFD mesh {dims[i]}-grid", grid[i], Iterable, Real) + check_greater_than(f"CMFD mesh {dims[i]}-grid length", len(grid[i]), 1) self._grid = [np.array(g) for g in grid] - self._display_mesh_warning('rectilinear', 'CMFD mesh 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 = (f'Setting {variable_label} if mesh type is not set to ' - f'{mesh_type} will have no effect') + warn_msg = ( + f"Setting {variable_label} if mesh type is not set to " + f"{mesh_type} will have no effect" + ) warnings.warn(warn_msg, RuntimeWarning) @@ -378,23 +403,27 @@ def __init__(self): self._tally_begin = 1 self._solver_begin = 1 self._ref_d = np.array([]) - self._display = {'balance': False, 'dominance': False, - 'entropy': False, 'source': False} + self._display = { + "balance": False, + "dominance": False, + "entropy": False, + "source": False, + } self._downscatter = False self._feedback = False - self._cmfd_ktol = 1.e-8 + self._cmfd_ktol = 1.0e-8 self._mesh = None - self._norm = 1. + self._norm = 1.0 self._power_monitor = False self._run_adjoint = False - self._w_shift = 1.e6 - self._stol = 1.e-8 + self._w_shift = 1.0e6 + self._stol = 1.0e-8 self._reset = [] self._write_matrices = False self._spectral = 0.0 - self._gauss_seidel_tolerance = [1.e-10, 1.e-5] - self._adjoint_type = 'physical' - self._window_type = 'none' + self._gauss_seidel_tolerance = [1.0e-10, 1.0e-5] + self._adjoint_type = "physical" + self._window_type = "none" self._window_size = 10 self._intracomm = None self._use_all_threads = False @@ -590,176 +619,194 @@ def k_cmfd(self): @tally_begin.setter def tally_begin(self, begin): - check_type('CMFD tally begin batch', begin, Integral) - check_greater_than('CMFD tally begin batch', begin, 0) + check_type("CMFD tally begin batch", begin, Integral) + check_greater_than("CMFD tally begin batch", begin, 0) self._tally_begin = begin @solver_begin.setter def solver_begin(self, begin): - check_type('CMFD feedback begin batch', begin, Integral) - check_greater_than('CMFD feedback begin batch', begin, 0) + check_type("CMFD feedback begin batch", begin, Integral) + check_greater_than("CMFD feedback begin batch", begin, 0) self._solver_begin = begin @ref_d.setter def ref_d(self, diff_params): - check_type('Reference diffusion params', diff_params, - Iterable, Real) + check_type("Reference diffusion params", diff_params, Iterable, Real) self._ref_d = np.array(diff_params) @display.setter def display(self, display): - check_type('display', display, Mapping) + check_type("display", display, Mapping) for key, value in display.items(): - check_value('display key', key, - ('balance', 'entropy', 'dominance', 'source')) + check_value( + "display key", key, ("balance", "entropy", "dominance", "source") + ) check_type(f"display['{key}']", value, bool) self._display[key] = value @downscatter.setter def downscatter(self, downscatter): - check_type('CMFD downscatter', downscatter, bool) + check_type("CMFD downscatter", downscatter, bool) self._downscatter = downscatter @feedback.setter def feedback(self, feedback): - check_type('CMFD feedback', feedback, bool) + check_type("CMFD feedback", feedback, bool) self._feedback = feedback @cmfd_ktol.setter def cmfd_ktol(self, cmfd_ktol): - check_type('CMFD eigenvalue tolerance', cmfd_ktol, Real) + check_type("CMFD eigenvalue tolerance", cmfd_ktol, Real) self._cmfd_ktol = cmfd_ktol @mesh.setter def mesh(self, cmfd_mesh): - check_type('CMFD mesh', cmfd_mesh, CMFDMesh) + check_type("CMFD mesh", cmfd_mesh, CMFDMesh) - if cmfd_mesh.mesh_type == 'regular': + 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') + 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 regular mesh requires lower left ' - 'coordinates to be specified') + 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 regular mesh') + 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 regular mesh requires either upper right ' - 'coordinates or width to be specified') + 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_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_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': + 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') + 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)] self._mesh = cmfd_mesh @norm.setter def norm(self, norm): - check_type('CMFD norm', norm, Real) + check_type("CMFD norm", norm, Real) self._norm = norm @adjoint_type.setter def adjoint_type(self, adjoint_type): - check_type('CMFD adjoint type', adjoint_type, str) - check_value('CMFD adjoint type', adjoint_type, - ['math', 'physical']) + check_type("CMFD adjoint type", adjoint_type, str) + check_value("CMFD adjoint type", adjoint_type, ["math", "physical"]) self._adjoint_type = adjoint_type @window_type.setter def window_type(self, window_type): - check_type('CMFD window type', window_type, str) - check_value('CMFD window type', window_type, - ['none', 'rolling', 'expanding']) + check_type("CMFD window type", window_type, str) + check_value("CMFD window type", window_type, ["none", "rolling", "expanding"]) self._window_type = window_type @window_size.setter def window_size(self, window_size): - check_type('CMFD window size', window_size, Integral) - check_greater_than('CMFD window size', window_size, 0) - if self._window_type != 'rolling': - warn_msg = 'Window size will have no effect on CMFD simulation ' \ - 'unless window type is set to "rolling".' + check_type("CMFD window size", window_size, Integral) + check_greater_than("CMFD window size", window_size, 0) + if self._window_type != "rolling": + warn_msg = ( + "Window size will have no effect on CMFD simulation " + 'unless window type is set to "rolling".' + ) warnings.warn(warn_msg, RuntimeWarning) self._window_size = window_size @power_monitor.setter def power_monitor(self, power_monitor): - check_type('CMFD power monitor', power_monitor, bool) + check_type("CMFD power monitor", power_monitor, bool) self._power_monitor = power_monitor @run_adjoint.setter def run_adjoint(self, run_adjoint): - check_type('CMFD run adjoint', run_adjoint, bool) + check_type("CMFD run adjoint", run_adjoint, bool) self._run_adjoint = run_adjoint @w_shift.setter def w_shift(self, w_shift): - check_type('CMFD Wielandt shift', w_shift, Real) + check_type("CMFD Wielandt shift", w_shift, Real) self._w_shift = w_shift @stol.setter def stol(self, stol): - check_type('CMFD fission source tolerance', stol, Real) + check_type("CMFD fission source tolerance", stol, Real) self._stol = stol @spectral.setter def spectral(self, spectral): - check_type('CMFD spectral radius', spectral, Real) + check_type("CMFD spectral radius", spectral, Real) self._spectral = spectral @reset.setter def reset(self, reset): - check_type('tally reset batches', reset, Iterable, Integral) + check_type("tally reset batches", reset, Iterable, Integral) self._reset = reset @write_matrices.setter def write_matrices(self, write_matrices): - check_type('CMFD write matrices', write_matrices, bool) + check_type("CMFD write matrices", write_matrices, bool) self._write_matrices = write_matrices @gauss_seidel_tolerance.setter def gauss_seidel_tolerance(self, gauss_seidel_tolerance): - check_type('CMFD Gauss-Seidel tolerance', gauss_seidel_tolerance, - Iterable, Real) - check_length('Gauss-Seidel tolerance', gauss_seidel_tolerance, 2) + check_type( + "CMFD Gauss-Seidel tolerance", gauss_seidel_tolerance, Iterable, Real + ) + check_length("Gauss-Seidel tolerance", gauss_seidel_tolerance, 2) self._gauss_seidel_tolerance = gauss_seidel_tolerance @use_all_threads.setter def use_all_threads(self, use_all_threads): - check_type('CMFD use all threads', use_all_threads, bool) + check_type("CMFD use all threads", use_all_threads, bool) self._use_all_threads = use_all_threads def run(self, **kwargs): @@ -781,7 +828,7 @@ def run(self, **kwargs): @contextmanager def run_in_memory(self, **kwargs): - """ Context manager for running CMFD functions with OpenMC shared + """Context manager for running CMFD functions with OpenMC shared library functions. This function can be used with a 'with' statement to ensure the @@ -802,8 +849,8 @@ def run_in_memory(self, **kwargs): """ # Store intracomm for part of CMFD routine where MPI reduce and # broadcast calls are made - if 'intracomm' in kwargs and kwargs['intracomm'] is not None: - self._intracomm = kwargs['intracomm'] + if "intracomm" in kwargs and kwargs["intracomm"] is not None: + self._intracomm = kwargs["intracomm"] elif have_mpi: self._intracomm = MPI.COMM_WORLD @@ -814,7 +861,7 @@ def run_in_memory(self, **kwargs): self.finalize() def iter_batches(self): - """ Iterator over batches. + """Iterator over batches. This function returns a generator-iterator that allows Python code to be run between batches when running an OpenMC simulation with CMFD. @@ -829,7 +876,7 @@ def iter_batches(self): yield def init(self): - """ Initialize CMFDRun instance by setting up CMFD parameters and + """Initialize CMFDRun instance by setting up CMFD parameters and calling :func:`openmc.lib.simulation_init` """ @@ -858,7 +905,7 @@ def init(self): openmc.lib.settings.cmfd_run = True def next_batch(self): - """ Run next batch for CMFDRun. + """Run next batch for CMFDRun. Returns ------- @@ -882,7 +929,7 @@ def next_batch(self): return status def finalize(self): - """ Finalize simulation by calling + """Finalize simulation by calling :func:`openmc.lib.simulation_finalize` and print out CMFD timing information. @@ -906,7 +953,7 @@ def statepoint_write(self, filename=None): if filename is None: batch_str_len = len(str(openmc.lib.settings.get_batches())) batch_str = str(openmc.lib.current_batch()).zfill(batch_str_len) - filename = f'statepoint.{batch_str}.h5' + filename = f"statepoint.{batch_str}.h5" # Call C API statepoint_write to save source distribution with CMFD # feedback @@ -925,105 +972,105 @@ def _write_cmfd_statepoint(self, filename): """ if openmc.lib.master(): - with h5py.File(filename, 'a') as f: - if 'cmfd' not in f: + with h5py.File(filename, "a") as f: + if "cmfd" not in f: if openmc.lib.settings.verbosity >= 5: - print(f' Writing CMFD data to {filename}...') + print(f" Writing CMFD data to {filename}...") sys.stdout.flush() cmfd_group = f.create_group("cmfd") - cmfd_group.attrs['cmfd_on'] = self._cmfd_on - cmfd_group.attrs['feedback'] = self._feedback - cmfd_group.attrs['solver_begin'] = self._solver_begin - cmfd_group.attrs['mesh_id'] = self._mesh_id - cmfd_group.attrs['tally_begin'] = self._tally_begin - cmfd_group.attrs['time_cmfd'] = self._time_cmfd - cmfd_group.attrs['time_cmfdbuild'] = self._time_cmfdbuild - cmfd_group.attrs['time_cmfdsolve'] = self._time_cmfdsolve - cmfd_group.attrs['window_size'] = self._window_size - cmfd_group.attrs['window_type'] = self._window_type - cmfd_group.create_dataset('k_cmfd', data=self._k_cmfd) - cmfd_group.create_dataset('dom', data=self._dom) - cmfd_group.create_dataset('src_cmp', data=self._src_cmp) - cmfd_group.create_dataset('balance', data=self._balance) - cmfd_group.create_dataset('entropy', data=self._entropy) - cmfd_group.create_dataset('reset', data=self._reset) - cmfd_group.create_dataset('albedo', data=self._albedo) - cmfd_group.create_dataset('coremap', data=self._coremap) - cmfd_group.create_dataset('egrid', data=self._egrid) - cmfd_group.create_dataset('indices', data=self._indices) - cmfd_group.create_dataset('tally_ids', - data=self._tally_ids) - cmfd_group.create_dataset('current_rate', - data=self._current_rate) - cmfd_group.create_dataset('flux_rate', - data=self._flux_rate) - cmfd_group.create_dataset('nfiss_rate', - data=self._nfiss_rate) - cmfd_group.create_dataset('openmc_src_rate', - data=self._openmc_src_rate) - cmfd_group.create_dataset('p1scatt_rate', - data=self._p1scatt_rate) - cmfd_group.create_dataset('scatt_rate', - data=self._scatt_rate) - cmfd_group.create_dataset('total_rate', - data=self._total_rate) + cmfd_group.attrs["cmfd_on"] = self._cmfd_on + cmfd_group.attrs["feedback"] = self._feedback + cmfd_group.attrs["solver_begin"] = self._solver_begin + cmfd_group.attrs["mesh_id"] = self._mesh_id + cmfd_group.attrs["tally_begin"] = self._tally_begin + cmfd_group.attrs["time_cmfd"] = self._time_cmfd + cmfd_group.attrs["time_cmfdbuild"] = self._time_cmfdbuild + cmfd_group.attrs["time_cmfdsolve"] = self._time_cmfdsolve + cmfd_group.attrs["window_size"] = self._window_size + cmfd_group.attrs["window_type"] = self._window_type + cmfd_group.create_dataset("k_cmfd", data=self._k_cmfd) + cmfd_group.create_dataset("dom", data=self._dom) + cmfd_group.create_dataset("src_cmp", data=self._src_cmp) + cmfd_group.create_dataset("balance", data=self._balance) + cmfd_group.create_dataset("entropy", data=self._entropy) + cmfd_group.create_dataset("reset", data=self._reset) + cmfd_group.create_dataset("albedo", data=self._albedo) + cmfd_group.create_dataset("coremap", data=self._coremap) + cmfd_group.create_dataset("egrid", data=self._egrid) + cmfd_group.create_dataset("indices", data=self._indices) + cmfd_group.create_dataset("tally_ids", data=self._tally_ids) + cmfd_group.create_dataset("current_rate", data=self._current_rate) + cmfd_group.create_dataset("flux_rate", data=self._flux_rate) + cmfd_group.create_dataset("nfiss_rate", data=self._nfiss_rate) + cmfd_group.create_dataset( + "openmc_src_rate", data=self._openmc_src_rate + ) + cmfd_group.create_dataset("p1scatt_rate", data=self._p1scatt_rate) + cmfd_group.create_dataset("scatt_rate", data=self._scatt_rate) + cmfd_group.create_dataset("total_rate", data=self._total_rate) elif openmc.settings.verbosity >= 5: - print(' CMFD data not written to statepoint file as it ' - 'already exists in {}'.format(filename), flush=True) + print( + " CMFD data not written to statepoint file as it " + "already exists in {}".format(filename), + flush=True, + ) def _initialize_linsolver(self): # Determine number of rows in CMFD matrix ng = self._indices[3] - n = self._mat_dim*ng + n = self._mat_dim * ng # Create temp loss matrix to pass row/col indices to C++ linear solver loss_row = self._loss_row loss_col = self._loss_col temp_data = np.ones(len(loss_row)) - temp_loss = sparse.csr_matrix((temp_data, (loss_row, loss_col)), - shape=(n, n)) + temp_loss = sparse.csr_matrix((temp_data, (loss_row, loss_col)), shape=(n, n)) temp_loss.sort_indices() # Pass coremap as 1-d array of 32-bit integers coremap = np.swapaxes(self._coremap, 0, 2).flatten().astype(np.int32) return openmc.lib._dll.openmc_initialize_linsolver( - temp_loss.indptr.astype(np.int32), len(temp_loss.indptr), - temp_loss.indices.astype(np.int32), len(temp_loss.indices), n, - self._spectral, coremap, self._use_all_threads + temp_loss.indptr.astype(np.int32), + len(temp_loss.indptr), + temp_loss.indices.astype(np.int32), + len(temp_loss.indices), + n, + self._spectral, + coremap, + self._use_all_threads, ) def _write_cmfd_output(self): """Write CMFD output to buffer at the end of each batch""" # Display CMFD k-effective - outstr = '{:>11s}CMFD k: {:0.5f}'.format('', self._k_cmfd[-1]) + outstr = "{:>11s}CMFD k: {:0.5f}".format("", self._k_cmfd[-1]) # Display value of additional fields based on display dict - outstr += '\n' - if self._display['dominance']: - outstr += ('{:>11s}Dom Rat: {:0.5f}\n' - .format('', self._dom[-1])) - if self._display['entropy']: - outstr += ('{:>11s}CMFD Ent: {:0.5f}\n' - .format('', self._entropy[-1])) - if self._display['source']: - outstr += ('{:>11s}RMS Src: {:0.5f}\n' - .format('', self._src_cmp[-1])) - if self._display['balance']: - outstr += ('{:>11s}RMS Bal: {:0.5f}\n' - .format('', self._balance[-1])) + outstr += "\n" + if self._display["dominance"]: + outstr += "{:>11s}Dom Rat: {:0.5f}\n".format("", self._dom[-1]) + if self._display["entropy"]: + outstr += "{:>11s}CMFD Ent: {:0.5f}\n".format("", self._entropy[-1]) + if self._display["source"]: + outstr += "{:>11s}RMS Src: {:0.5f}\n".format("", self._src_cmp[-1]) + if self._display["balance"]: + outstr += "{:>11s}RMS Bal: {:0.5f}\n".format("", self._balance[-1]) print(outstr) sys.stdout.flush() def _write_cmfd_timing_stats(self): """Write CMFD timing stats to buffer after finalizing simulation""" - outstr = ("=====================> " - "CMFD TIMING STATISTICS <====================\n\n" - " Time in CMFD = {:.5e} seconds\n" - " Building matrices = {:.5e} seconds\n" - " Solving matrices = {:.5e} seconds\n") - print(outstr.format(self._time_cmfd, self._time_cmfdbuild, - self._time_cmfdsolve)) + outstr = ( + "=====================> " + "CMFD TIMING STATISTICS <====================\n\n" + " Time in CMFD = {:.5e} seconds\n" + " Building matrices = {:.5e} seconds\n" + " Solving matrices = {:.5e} seconds\n" + ) + print( + outstr.format(self._time_cmfd, self._time_cmfdbuild, self._time_cmfdsolve) + ) sys.stdout.flush() def _configure_cmfd(self): @@ -1040,19 +1087,18 @@ def _configure_cmfd(self): def _initialize_cmfd(self): """Sets values of CMFD instance variables based on user input, - separating between variables that only exist on all processes - and those that only exist on the master process + separating between variables that only exist on all processes + and those that only exist on the master process """ # Print message to user and flush output to stdout if openmc.lib.settings.verbosity >= 7 and openmc.lib.master(): - print(' Configuring CMFD parameters for simulation') + print(" Configuring CMFD parameters for simulation") sys.stdout.flush() # Check if CMFD mesh is defined if self._mesh is None: - raise ValueError('No CMFD mesh has been specified for ' - 'simulation') + raise ValueError("No CMFD mesh has been specified for " "simulation") # Set spatial dimensions of CMFD object for i, n in enumerate(self._mesh.dimension): @@ -1060,7 +1106,7 @@ def _initialize_cmfd(self): # Check if in continuous energy mode if not openmc.lib.settings.run_CE: - raise OpenMCError('CMFD must be run in continuous energy mode') + raise OpenMCError("CMFD must be run in continuous energy mode") # Set number of energy groups if self._mesh.energy is not None: @@ -1075,8 +1121,7 @@ def _initialize_cmfd(self): # Get acceleration map, otherwise set all regions to be accelerated if self._mesh.map is not None: - check_length('CMFD coremap', self._mesh.map, - np.prod(self._indices[:3])) + check_length("CMFD coremap", self._mesh.map, np.prod(self._indices[:3])) if openmc.lib.master(): self._coremap = np.array(self._mesh.map) else: @@ -1085,8 +1130,7 @@ def _initialize_cmfd(self): # Check CMFD tallies accummulated before feedback turned on if self._feedback and self._solver_begin < self._tally_begin: - raise ValueError('Tally begin must be less than or equal to ' - 'CMFD begin') + raise ValueError("Tally begin must be less than or equal to " "CMFD begin") # Initialize parameters for CMFD tally windows self._set_tally_window() @@ -1103,7 +1147,7 @@ def _initialize_cmfd(self): if self._mesh.albedo is not None: self._albedo = np.array(self._mesh.albedo) else: - self._albedo = np.array([1., 1., 1., 1., 1., 1.]) + self._albedo = np.array([1.0, 1.0, 1.0, 1.0, 1.0, 1.0]) # Set up CMFD coremap self._set_coremap() @@ -1131,79 +1175,78 @@ def _reset_cmfd(self, filename): Filename of statepoint to read from """ - with h5py.File(filename, 'r') as f: - if 'cmfd' not in f: - raise OpenMCError('Could not find CMFD parameters in ', - f'file {filename}') + with h5py.File(filename, "r") as f: + if "cmfd" not in f: + raise OpenMCError( + "Could not find CMFD parameters in ", f"file {filename}" + ) else: # Overwrite CMFD values from statepoint - if (openmc.lib.master() and - openmc.lib.settings.verbosity >= 5): - print(f' Loading CMFD data from {filename}...') + if openmc.lib.master() and openmc.lib.settings.verbosity >= 5: + print(f" Loading CMFD data from {filename}...") sys.stdout.flush() - cmfd_group = f['cmfd'] + cmfd_group = f["cmfd"] # Define variables that exist on all processes - self._cmfd_on = cmfd_group.attrs['cmfd_on'] - self._feedback = cmfd_group.attrs['feedback'] - self._solver_begin = cmfd_group.attrs['solver_begin'] - self._tally_begin = cmfd_group.attrs['tally_begin'] - self._k_cmfd = list(cmfd_group['k_cmfd']) - self._dom = list(cmfd_group['dom']) - self._src_cmp = list(cmfd_group['src_cmp']) - self._balance = list(cmfd_group['balance']) - self._entropy = list(cmfd_group['entropy']) - self._reset = list(cmfd_group['reset']) - self._egrid = cmfd_group['egrid'][()] - self._indices = cmfd_group['indices'][()] - default_egrid = np.array([_ENERGY_MIN_NEUTRON, - _ENERGY_MAX_NEUTRON]) - self._energy_filters = not np.array_equal(self._egrid, - default_egrid) - self._window_size = cmfd_group.attrs['window_size'] - self._window_type = cmfd_group.attrs['window_type'] - self._reset_every = (self._window_type == 'expanding' or - self._window_type == 'rolling') + self._cmfd_on = cmfd_group.attrs["cmfd_on"] + self._feedback = cmfd_group.attrs["feedback"] + self._solver_begin = cmfd_group.attrs["solver_begin"] + self._tally_begin = cmfd_group.attrs["tally_begin"] + self._k_cmfd = list(cmfd_group["k_cmfd"]) + self._dom = list(cmfd_group["dom"]) + self._src_cmp = list(cmfd_group["src_cmp"]) + self._balance = list(cmfd_group["balance"]) + self._entropy = list(cmfd_group["entropy"]) + self._reset = list(cmfd_group["reset"]) + self._egrid = cmfd_group["egrid"][()] + self._indices = cmfd_group["indices"][()] + default_egrid = np.array([_ENERGY_MIN_NEUTRON, _ENERGY_MAX_NEUTRON]) + self._energy_filters = not np.array_equal(self._egrid, default_egrid) + self._window_size = cmfd_group.attrs["window_size"] + self._window_type = cmfd_group.attrs["window_type"] + self._reset_every = ( + self._window_type == "expanding" or self._window_type == "rolling" + ) # Overwrite CMFD mesh properties - cmfd_mesh_name = 'mesh ' + str(cmfd_group.attrs['mesh_id']) - cmfd_mesh = f['tallies']['meshes'][cmfd_mesh_name] - 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'][()] + cmfd_mesh_name = "mesh " + str(cmfd_group.attrs["mesh_id"]) + cmfd_mesh = f["tallies"]["meshes"][cmfd_mesh_name] + 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(): - self._time_cmfd = cmfd_group.attrs['time_cmfd'] - self._time_cmfdbuild = cmfd_group.attrs['time_cmfdbuild'] - self._time_cmfdsolve = cmfd_group.attrs['time_cmfdsolve'] - self._albedo = cmfd_group['albedo'][()] - self._coremap = cmfd_group['coremap'][()] - self._current_rate = cmfd_group['current_rate'][()] - self._flux_rate = cmfd_group['flux_rate'][()] - self._nfiss_rate = cmfd_group['nfiss_rate'][()] - self._openmc_src_rate = cmfd_group['openmc_src_rate'][()] - self._p1scatt_rate = cmfd_group['p1scatt_rate'][()] - self._scatt_rate = cmfd_group['scatt_rate'][()] - self._total_rate = cmfd_group['total_rate'][()] + self._time_cmfd = cmfd_group.attrs["time_cmfd"] + self._time_cmfdbuild = cmfd_group.attrs["time_cmfdbuild"] + self._time_cmfdsolve = cmfd_group.attrs["time_cmfdsolve"] + self._albedo = cmfd_group["albedo"][()] + self._coremap = cmfd_group["coremap"][()] + self._current_rate = cmfd_group["current_rate"][()] + self._flux_rate = cmfd_group["flux_rate"][()] + self._nfiss_rate = cmfd_group["nfiss_rate"][()] + self._openmc_src_rate = cmfd_group["openmc_src_rate"][()] + self._p1scatt_rate = cmfd_group["p1scatt_rate"][()] + self._scatt_rate = cmfd_group["scatt_rate"][()] + self._total_rate = cmfd_group["total_rate"][()] self._mat_dim = np.max(self._coremap) + 1 def _set_tally_window(self): """Sets parameters to handle different tally window options""" # Set parameters for expanding window - if self._window_type == 'expanding': + if self._window_type == "expanding": self._reset_every = True self._window_size = 1 # Set parameters for rolling window - elif self.window_type == 'rolling': + elif self.window_type == "rolling": self._reset_every = True # Set parameters for default case, with no window else: @@ -1221,8 +1264,7 @@ def _cmfd_init_batch(self): self._cmfd_on = True # Check to reset tallies - if ((len(self._reset) > 0 and current_batch in self._reset) - or self._reset_every): + if (len(self._reset) > 0 and current_batch in self._reset) or self._reset_every: self._cmfd_tally_reset() def _execute_cmfd(self): @@ -1274,9 +1316,12 @@ def _execute_cmfd(self): def _cmfd_tally_reset(self): """Resets all CMFD tallies in memory""" # Print message - if (openmc.lib.settings.verbosity >= 6 and openmc.lib.master() and - not self._reset_every): - print(' CMFD tallies reset') + if ( + openmc.lib.settings.verbosity >= 6 + and openmc.lib.master() + and not self._reset_every + ): + print(" CMFD tallies reset") sys.stdout.flush() # Reset CMFD tallies @@ -1285,9 +1330,7 @@ def _cmfd_tally_reset(self): tallies[tally_id].reset() def _set_up_cmfd(self): - """Configures CMFD object for a CMFD eigenvalue calculation - - """ + """Configures CMFD object for a CMFD eigenvalue calculation""" # Compute effective downscatter cross section if self._downscatter: self._compute_effective_downscatter() @@ -1320,7 +1363,7 @@ def _cmfd_solver_execute(self, adjoint=False): prod = self._build_prod_matrix(False) else: # Build adjoint matrices by running adjoint calculation - if self._adjoint_type == 'physical': + if self._adjoint_type == "physical": loss = self._build_loss_matrix(True) prod = self._build_prod_matrix(True) # Build adjoint matrices as transpose of non-adjoint matrices @@ -1331,11 +1374,11 @@ def _cmfd_solver_execute(self, adjoint=False): # Write out the matrices. if self._write_matrices: if not adjoint: - self._write_matrix(loss, 'loss') - self._write_matrix(prod, 'prod') + self._write_matrix(loss, "loss") + self._write_matrix(prod, "prod") else: - self._write_matrix(loss, 'adj_loss') - self._write_matrix(prod, 'adj_prod') + self._write_matrix(loss, "adj_loss") + self._write_matrix(prod, "adj_prod") # Stop timer for build time_stop_buildcmfd = time.time() @@ -1350,19 +1393,19 @@ def _cmfd_solver_execute(self, adjoint=False): # Save results, normalizing phi to sum to 1 if adjoint: self._adj_keff = keff - self._adj_phi = phi/np.sqrt(np.sum(phi*phi)) + self._adj_phi = phi / np.sqrt(np.sum(phi * phi)) else: self._keff = keff - self._phi = phi/np.sqrt(np.sum(phi*phi)) + self._phi = phi / np.sqrt(np.sum(phi * phi)) self._dom.append(dom) # Write out flux vector if self._write_matrices: if adjoint: - self._write_vector(self._adj_phi, 'adj_fluxvec') + self._write_vector(self._adj_phi, "adj_fluxvec") else: - self._write_vector(self._phi, 'fluxvec') + self._write_vector(self._phi, "fluxvec") def _write_vector(self, vector, base_filename): """Write a 1-D numpy array to file and also save it in .npy format. @@ -1380,7 +1423,7 @@ def _write_vector(self, vector, base_filename): """ # Write each element in vector to file - np.savetxt(f'{base_filename}.dat', vector, fmt='%.8f') + np.savetxt(f"{base_filename}.dat", vector, fmt="%.8f") # Save as numpy format np.save(base_filename, vector) @@ -1402,14 +1445,14 @@ def _write_matrix(self, matrix, base_filename): """ # Write row, col, and data of each entry in sparse matrix. This ignores # all zero-entries, and indices are written with zero-based indexing - with open(base_filename+'.dat', 'w') as fh: + with open(base_filename + ".dat", "w") as fh: for row in range(matrix.shape[0]): # Get all cols for particular row in matrix - cols = matrix.indices[matrix.indptr[row]:matrix.indptr[row+1]] + cols = matrix.indices[matrix.indptr[row] : matrix.indptr[row + 1]] # Get all data entries for particular row in matrix - data = matrix.data[matrix.indptr[row]:matrix.indptr[row+1]] + data = matrix.data[matrix.indptr[row] : matrix.indptr[row + 1]] for i in range(len(cols)): - fh.write(f'{row:3d}, {cols[i]:3d}, {data[i]:0.8f}\n') + fh.write(f"{row:3d}, {cols[i]:3d}, {data[i]:0.8f}\n") # Save matrix in scipy format sparse.save_npz(base_filename, matrix) @@ -1443,12 +1486,13 @@ def _calc_fission_source(self): # Loop over all groups and set CMFD flux based on indices of # coremap and values of phi for g in range(ng): - phi_g = phi[:,g] + phi_g = phi[:, g] cmfd_flux[idx + (g,)] = phi_g[self._coremap[idx]] # Compute fission source - cmfd_src = (np.sum(self._nfissxs[:,:,:,:,:] * - cmfd_flux[:,:,:,:,np.newaxis], axis=3)) + cmfd_src = np.sum( + self._nfissxs[:, :, :, :, :] * cmfd_flux[:, :, :, :, np.newaxis], axis=3 + ) # Normalize source such that it sums to 1.0 self._cmfd_src = cmfd_src / np.sum(cmfd_src) @@ -1456,113 +1500,116 @@ def _calc_fission_source(self): # Compute entropy if openmc.lib.settings.entropy_on: # Compute source times log_2(source) - source = self._cmfd_src[self._cmfd_src > 0] \ - * np.log(self._cmfd_src[self._cmfd_src > 0])/np.log(2) + source = ( + self._cmfd_src[self._cmfd_src > 0] + * np.log(self._cmfd_src[self._cmfd_src > 0]) + / np.log(2) + ) # Sum source and store self._entropy.append(-1.0 * np.sum(source)) # Normalize source so average is 1.0 - self._cmfd_src = self._cmfd_src/np.sum(self._cmfd_src) * self._norm + self._cmfd_src = self._cmfd_src / np.sum(self._cmfd_src) * self._norm # Calculate differences between normalized sources - self._src_cmp.append(np.sqrt(1.0 / self._norm - * np.sum((self._cmfd_src - self._openmc_src)**2))) + self._src_cmp.append( + np.sqrt(1.0 / self._norm * np.sum((self._cmfd_src - self._openmc_src) ** 2)) + ) def _build_loss_matrix(self, adjoint): # Extract spatial and energy indices and define matrix dimension ng = self._indices[3] - n = self._mat_dim*ng + n = self._mat_dim * ng # Define data entries used to build csr matrix data = np.array([]) - dtilde_left = self._dtilde[:,:,:,:,0] - dtilde_right = self._dtilde[:,:,:,:,1] - dtilde_back = self._dtilde[:,:,:,:,2] - dtilde_front = self._dtilde[:,:,:,:,3] - dtilde_bottom = self._dtilde[:,:,:,:,4] - dtilde_top = self._dtilde[:,:,:,:,5] - dhat_left = self._dhat[:,:,:,:,0] - dhat_right = self._dhat[:,:,:,:,1] - dhat_back = self._dhat[:,:,:,:,2] - dhat_front = self._dhat[:,:,:,:,3] - dhat_bottom = self._dhat[:,:,:,:,4] - dhat_top = self._dhat[:,:,:,:,5] - - dx = self._hxyz[:,:,:,np.newaxis,0] - dy = self._hxyz[:,:,:,np.newaxis,1] - dz = self._hxyz[:,:,:,np.newaxis,2] + dtilde_left = self._dtilde[:, :, :, :, 0] + dtilde_right = self._dtilde[:, :, :, :, 1] + dtilde_back = self._dtilde[:, :, :, :, 2] + dtilde_front = self._dtilde[:, :, :, :, 3] + dtilde_bottom = self._dtilde[:, :, :, :, 4] + dtilde_top = self._dtilde[:, :, :, :, 5] + dhat_left = self._dhat[:, :, :, :, 0] + dhat_right = self._dhat[:, :, :, :, 1] + dhat_back = self._dhat[:, :, :, :, 2] + dhat_front = self._dhat[:, :, :, :, 3] + dhat_bottom = self._dhat[:, :, :, :, 4] + dhat_top = self._dhat[:, :, :, :, 5] + + dx = self._hxyz[:, :, :, np.newaxis, 0] + dy = self._hxyz[:, :, :, np.newaxis, 1] + dz = self._hxyz[:, :, :, np.newaxis, 2] # Define net leakage coefficient for each surface in each matrix # element - jnet = (((dtilde_right + dhat_right)-(-1.0 * dtilde_left + dhat_left)) - / dx + - ((dtilde_front + dhat_front)-(-1.0 * dtilde_back + dhat_back)) - / dy + - ((dtilde_top + dhat_top)-(-1.0 * dtilde_bottom + dhat_bottom)) - / dz) + jnet = ( + ((dtilde_right + dhat_right) - (-1.0 * dtilde_left + dhat_left)) / dx + + ((dtilde_front + dhat_front) - (-1.0 * dtilde_back + dhat_back)) / dy + + ((dtilde_top + dhat_top) - (-1.0 * dtilde_bottom + dhat_bottom)) / dz + ) for g in range(ng): # Define leakage terms that relate terms to their neighbors to the # left - dtilde = self._dtilde[:,:,:,g,0][self._accel_neig_left_idxs] - dhat = self._dhat[:,:,:,g,0][self._accel_neig_left_idxs] - dx = self._hxyz[:,:,:,0][self._accel_neig_left_idxs] + dtilde = self._dtilde[:, :, :, g, 0][self._accel_neig_left_idxs] + dhat = self._dhat[:, :, :, g, 0][self._accel_neig_left_idxs] + dx = self._hxyz[:, :, :, 0][self._accel_neig_left_idxs] vals = (-1.0 * dtilde - dhat) / dx # Store data to add to CSR matrix data = np.append(data, vals) # Define leakage terms that relate terms to their neighbors to the # right - dtilde = self._dtilde[:,:,:,g,1][self._accel_neig_right_idxs] - dhat = self._dhat[:,:,:,g,1][self._accel_neig_right_idxs] - dx = self._hxyz[:,:,:,0][self._accel_neig_right_idxs] + dtilde = self._dtilde[:, :, :, g, 1][self._accel_neig_right_idxs] + dhat = self._dhat[:, :, :, g, 1][self._accel_neig_right_idxs] + dx = self._hxyz[:, :, :, 0][self._accel_neig_right_idxs] vals = (-1.0 * dtilde + dhat) / dx # Store data to add to CSR matrix data = np.append(data, vals) # Define leakage terms that relate terms to their neighbors in the # back - dtilde = self._dtilde[:,:,:,g,2][self._accel_neig_back_idxs] - dhat = self._dhat[:,:,:,g,2][self._accel_neig_back_idxs] - dy = self._hxyz[:,:,:,1][self._accel_neig_back_idxs] + dtilde = self._dtilde[:, :, :, g, 2][self._accel_neig_back_idxs] + dhat = self._dhat[:, :, :, g, 2][self._accel_neig_back_idxs] + dy = self._hxyz[:, :, :, 1][self._accel_neig_back_idxs] vals = (-1.0 * dtilde - dhat) / dy # Store data to add to CSR matrix data = np.append(data, vals) # Define leakage terms that relate terms to their neighbors in the # front - dtilde = self._dtilde[:,:,:,g,3][self._accel_neig_front_idxs] - dhat = self._dhat[:,:,:,g,3][self._accel_neig_front_idxs] - dy = self._hxyz[:,:,:,1][self._accel_neig_front_idxs] + dtilde = self._dtilde[:, :, :, g, 3][self._accel_neig_front_idxs] + dhat = self._dhat[:, :, :, g, 3][self._accel_neig_front_idxs] + dy = self._hxyz[:, :, :, 1][self._accel_neig_front_idxs] vals = (-1.0 * dtilde + dhat) / dy # Store data to add to CSR matrix data = np.append(data, vals) # Define leakage terms that relate terms to their neighbors to the # bottom - dtilde = self._dtilde[:,:,:,g,4][self._accel_neig_bot_idxs] - dhat = self._dhat[:,:,:,g,4][self._accel_neig_bot_idxs] - dz = self._hxyz[:,:,:,2][self._accel_neig_bot_idxs] + dtilde = self._dtilde[:, :, :, g, 4][self._accel_neig_bot_idxs] + dhat = self._dhat[:, :, :, g, 4][self._accel_neig_bot_idxs] + dz = self._hxyz[:, :, :, 2][self._accel_neig_bot_idxs] vals = (-1.0 * dtilde - dhat) / dz # Store data to add to CSR matrix data = np.append(data, vals) # Define leakage terms that relate terms to their neighbors to the # top - dtilde = self._dtilde[:,:,:,g,5][self._accel_neig_top_idxs] - dhat = self._dhat[:,:,:,g,5][self._accel_neig_top_idxs] - dz = self._hxyz[:,:,:,2][self._accel_neig_top_idxs] + dtilde = self._dtilde[:, :, :, g, 5][self._accel_neig_top_idxs] + dhat = self._dhat[:, :, :, g, 5][self._accel_neig_top_idxs] + dz = self._hxyz[:, :, :, 2][self._accel_neig_top_idxs] vals = (-1.0 * dtilde + dhat) / dz # Store data to add to CSR matrix data = np.append(data, vals) # Define terms that relate to loss of neutrons in a cell. These # correspond to all the diagonal entries of the loss matrix - jnet_g = jnet[:,:,:,g][self._accel_idxs] - total_xs = self._totalxs[:,:,:,g][self._accel_idxs] - scatt_xs = self._scattxs[:,:,:,g,g][self._accel_idxs] + jnet_g = jnet[:, :, :, g][self._accel_idxs] + total_xs = self._totalxs[:, :, :, g][self._accel_idxs] + scatt_xs = self._scattxs[:, :, :, g, g][self._accel_idxs] vals = jnet_g + total_xs - scatt_xs # Store data to add to CSR matrix data = np.append(data, vals) @@ -1574,10 +1621,10 @@ def _build_loss_matrix(self, adjoint): if h != g: # Get scattering macro xs, transposed if adjoint: - scatt_xs = self._scattxs[:,:,:,g,h][self._accel_idxs] + scatt_xs = self._scattxs[:, :, :, g, h][self._accel_idxs] # Get scattering macro xs else: - scatt_xs = self._scattxs[:,:,:,h,g][self._accel_idxs] + scatt_xs = self._scattxs[:, :, :, h, g][self._accel_idxs] vals = -1.0 * scatt_xs # Store data to add to CSR matrix data = np.append(data, vals) @@ -1592,7 +1639,7 @@ def _build_loss_matrix(self, adjoint): def _build_prod_matrix(self, adjoint): # Extract spatial and energy indices and define matrix dimension ng = self._indices[3] - n = self._mat_dim*ng + n = self._mat_dim * ng # Define rows, columns, and data used to build csr matrix data = np.array([]) @@ -1657,7 +1704,7 @@ def _execute_power_iter(self, loss, prod): k_o = k_n dw = self._w_shift k_s = k_o + dw - k_ln = 1.0/(1.0/k_n - 1.0/k_s) + k_ln = 1.0 / (1.0 / k_n - 1.0 / k_s) k_lo = k_ln # Set norms to 0 @@ -1668,14 +1715,15 @@ def _execute_power_iter(self, loss, prod): maxits = 10000 # Perform Wielandt shift - loss -= 1.0/k_s*prod + loss -= 1.0 / k_s * prod # Begin power iteration for i in range(maxits): # Check if reach max number of iterations if i == maxits - 1: - raise OpenMCError('Reached maximum iterations in CMFD power ' - 'iteration solver.') + raise OpenMCError( + "Reached maximum iterations in CMFD power " "iteration solver." + ) # Compute source vector s_o = prod.dot(phi_o) @@ -1684,8 +1732,7 @@ def _execute_power_iter(self, loss, prod): s_o /= k_lo # Compute new flux with C++ solver - innerits = openmc.lib._dll.openmc_run_linsolver(loss.data, s_o, - phi_n, toli) + innerits = openmc.lib._dll.openmc_run_linsolver(loss.data, s_o, phi_n, toli) # Compute new source vector s_n = prod.dot(phi_n) @@ -1694,14 +1741,13 @@ def _execute_power_iter(self, loss, prod): k_ln = np.sum(s_n) / np.sum(s_o) # Compute new eigenvalue - k_n = 1.0/(1.0/k_ln + 1.0/k_s) + k_n = 1.0 / (1.0 / k_ln + 1.0 / k_s) # Renormalize the old source s_o *= k_lo # Check convergence - iconv, norm_n = self._check_convergence(s_n, s_o, k_n, k_o, i+1, - innerits) + iconv, norm_n = self._check_convergence(s_n, s_o, k_n, k_o, i + 1, innerits) # If converged, calculate dominance ratio and break from loop if iconv: @@ -1715,7 +1761,7 @@ def _execute_power_iter(self, loss, prod): norm_o = norm_n # Update tolerance for inner iterations - toli = max(atoli, rtoli*norm_n) + toli = max(atoli, rtoli * norm_n) def _check_convergence(self, s_n, s_o, k_n, k_o, iteration, innerits): """Checks the convergence of the CMFD problem @@ -1748,22 +1794,26 @@ def _check_convergence(self, s_n, s_o, k_n, k_o, iteration, innerits): kerr = abs(k_o - k_n) / k_n # Calculate max error in source - with np.errstate(divide='ignore', invalid='ignore'): - serr = np.sqrt(np.sum(np.where(s_n > 0, ((s_n-s_o) / s_n)**2, 0)) - / len(s_n)) + with np.errstate(divide="ignore", invalid="ignore"): + serr = np.sqrt( + np.sum(np.where(s_n > 0, ((s_n - s_o) / s_n) ** 2, 0)) / len(s_n) + ) # Check for convergence iconv = kerr < self._cmfd_ktol and serr < self._stol # Print out to user if self._power_monitor and openmc.lib.master(): - print('{:8s}{:20s}{:25s}{:s}{:s}'.format( - ' {:d}:'.format(iteration), - 'k-eff: {:0.8f}'.format(k_n), - 'k-error: {:.5e}'.format(kerr), - 'src-error: {:.5e}'.format(serr), - ' {:d}'.format(innerits) - ), flush=True) + print( + "{:8s}{:20s}{:25s}{:s}{:s}".format( + " {:d}:".format(iteration), + "k-eff: {:0.8f}".format(k_n), + "k-error: {:.5e}".format(kerr), + "src-error: {:.5e}".format(serr), + " {:d}".format(innerits), + ), + flush=True, + ) return iconv, serr @@ -1780,8 +1830,9 @@ def _set_coremap(self): # Define coremap as cumulative sum over accelerated regions, # otherwise set value to _CMFD_NOACCEL - self._coremap = np.where(self._coremap == 0, _CMFD_NOACCEL, - np.cumsum(self._coremap) - 1) + self._coremap = np.where( + self._coremap == 0, _CMFD_NOACCEL, np.cumsum(self._coremap) - 1 + ) # Reshape coremap to three dimensional array # Indices of coremap in user input switched in x and z axes @@ -1797,20 +1848,22 @@ def _compute_xs(self): """ # Update window size for expanding window if necessary num_cmfd_batches = openmc.lib.current_batch() - self._tally_begin + 1 - if (self._window_type == 'expanding' and - num_cmfd_batches == self._window_size * 2): + if ( + self._window_type == "expanding" + and num_cmfd_batches == self._window_size * 2 + ): self._window_size *= 2 # Discard tallies from oldest batch if window limit reached tally_windows = self._flux_rate.shape[-1] + 1 if tally_windows > self._window_size: - self._flux_rate = self._flux_rate[...,1:] - self._total_rate = self._total_rate[...,1:] - self._p1scatt_rate = self._p1scatt_rate[...,1:] - self._scatt_rate = self._scatt_rate[...,1:] - self._nfiss_rate = self._nfiss_rate[...,1:] - self._current_rate = self._current_rate[...,1:] - self._openmc_src_rate = self._openmc_src_rate[...,1:] + self._flux_rate = self._flux_rate[..., 1:] + self._total_rate = self._total_rate[..., 1:] + self._p1scatt_rate = self._p1scatt_rate[..., 1:] + self._scatt_rate = self._scatt_rate[..., 1:] + self._nfiss_rate = self._nfiss_rate[..., 1:] + self._current_rate = self._current_rate[..., 1:] + self._openmc_src_rate = self._openmc_src_rate[..., 1:] tally_windows -= 1 # Extract spatial and energy indices @@ -1824,7 +1877,7 @@ def _compute_xs(self): # Get flux from CMFD tally 0 tally_id = self._tally_ids[0] - flux = tallies[tally_id].results[:,0,1] + flux = tallies[tally_id].results[:, 0, 1] # Define target tally reshape dimensions. This defines how openmc # tallies are ordered by dimension @@ -1842,50 +1895,54 @@ def _compute_xs(self): self._flux_rate = np.append(self._flux_rate, reshape_flux, axis=4) # Compute flux as aggregate of banked flux_rate over tally window - self._flux = np.where(is_accel[..., np.newaxis], - np.sum(self._flux_rate, axis=4), 0.0) + self._flux = np.where( + is_accel[..., np.newaxis], np.sum(self._flux_rate, axis=4), 0.0 + ) # Detect zero flux, abort if located and cmfd is on - zero_flux = np.logical_and(self._flux < _TINY_BIT, - is_accel[..., np.newaxis]) + zero_flux = np.logical_and(self._flux < _TINY_BIT, is_accel[..., np.newaxis]) if np.any(zero_flux) and self._cmfd_on: # Get index of first zero flux in flux array idx = np.argwhere(zero_flux)[0] # Throw error message (one-based indexing) # Index of group is flipped - err_message = 'Detected zero flux without coremap overlay' + \ - ' at mesh: (' + \ - ', '.join(str(i+1) for i in idx[:-1]) + \ - ') in group ' + str(ng-idx[-1]) + err_message = ( + "Detected zero flux without coremap overlay" + + " at mesh: (" + + ", ".join(str(i + 1) for i in idx[:-1]) + + ") in group " + + str(ng - idx[-1]) + ) raise OpenMCError(err_message) # Get total reaction rate (rr) from CMFD tally 0 - totalrr = tallies[tally_id].results[:,1,1] + totalrr = tallies[tally_id].results[:, 1, 1] # Reshape total reaction rate array to target shape. Swap x and z axes # so that shape is now [nx, ny, nz, ng, 1] - reshape_totalrr = np.swapaxes(totalrr.reshape(target_tally_shape), - 0, 2) + reshape_totalrr = np.swapaxes(totalrr.reshape(target_tally_shape), 0, 2) # Total reaction rate is flipped in energy axis as tally results are # given in reverse order of energy group reshape_totalrr = np.flip(reshape_totalrr, axis=3) # Bank total reaction rate to total_rate - self._total_rate = np.append(self._total_rate, reshape_totalrr, - axis=4) + self._total_rate = np.append(self._total_rate, reshape_totalrr, axis=4) # Compute total xs as aggregate of banked total_rate over tally window # divided by flux - self._totalxs = np.divide(np.sum(self._total_rate, axis=4), - self._flux, where=self._flux > 0, - out=np.zeros_like(self._totalxs)) + self._totalxs = np.divide( + np.sum(self._total_rate, axis=4), + self._flux, + where=self._flux > 0, + out=np.zeros_like(self._totalxs), + ) # Get scattering rr from CMFD tally 1 # flux is repeated to account for extra dimensionality of scattering xs tally_id = self._tally_ids[1] - scattrr = tallies[tally_id].results[:,0,1] + scattrr = tallies[tally_id].results[:, 0, 1] # Define target tally reshape dimensions for xs with incoming # and outgoing energies @@ -1893,8 +1950,7 @@ def _compute_xs(self): # Reshape scattrr array to target shape. Swap x and z axes so that # shape is now [nx, ny, nz, ng, ng, 1] - reshape_scattrr = np.swapaxes(scattrr.reshape(target_tally_shape), - 0, 2) + reshape_scattrr = np.swapaxes(scattrr.reshape(target_tally_shape), 0, 2) # Scattering rr is flipped in both incoming and outgoing energy axes # as tally results are given in reverse order of energy group @@ -1902,25 +1958,26 @@ def _compute_xs(self): reshape_scattrr = np.flip(reshape_scattrr, axis=4) # Bank scattering rr to scatt_rate - self._scatt_rate = np.append(self._scatt_rate, reshape_scattrr, - axis=5) + self._scatt_rate = np.append(self._scatt_rate, reshape_scattrr, axis=5) # Compute scattering xs as aggregate of banked scatt_rate over tally # window divided by flux. Flux dimensionality increased to account for # extra dimensionality of scattering xs - extended_flux = self._flux[:,:,:,:,np.newaxis] - self._scattxs = np.divide(np.sum(self._scatt_rate, axis=5), - extended_flux, where=extended_flux > 0, - out=np.zeros_like(self._scattxs)) + extended_flux = self._flux[:, :, :, :, np.newaxis] + self._scattxs = np.divide( + np.sum(self._scatt_rate, axis=5), + extended_flux, + where=extended_flux > 0, + out=np.zeros_like(self._scattxs), + ) # Get nu-fission rr from CMFD tally 1 - nfissrr = tallies[tally_id].results[:,1,1] + nfissrr = tallies[tally_id].results[:, 1, 1] num_realizations = tallies[tally_id].num_realizations # Reshape nfissrr array to target shape. Swap x and z axes so that # shape is now [nx, ny, nz, ng, ng, 1] - reshape_nfissrr = np.swapaxes(nfissrr.reshape(target_tally_shape), - 0, 2) + reshape_nfissrr = np.swapaxes(nfissrr.reshape(target_tally_shape), 0, 2) # Nu-fission rr is flipped in both incoming and outgoing energy axes # as tally results are given in reverse order of energy group @@ -1928,15 +1985,17 @@ def _compute_xs(self): reshape_nfissrr = np.flip(reshape_nfissrr, axis=4) # Bank nu-fission rr to nfiss_rate - self._nfiss_rate = np.append(self._nfiss_rate, reshape_nfissrr, - axis=5) + self._nfiss_rate = np.append(self._nfiss_rate, reshape_nfissrr, axis=5) # Compute nu-fission xs as aggregate of banked nfiss_rate over tally # window divided by flux. Flux dimensionality increased to account for # extra dimensionality of nu-fission xs - self._nfissxs = np.divide(np.sum(self._nfiss_rate, axis=5), - extended_flux, where=extended_flux > 0, - out=np.zeros_like(self._nfissxs)) + self._nfissxs = np.divide( + np.sum(self._nfiss_rate, axis=5), + extended_flux, + where=extended_flux > 0, + out=np.zeros_like(self._nfissxs), + ) # Openmc source distribution is sum of nu-fission rr in incoming # energies @@ -1944,78 +2003,82 @@ def _compute_xs(self): # Bank OpenMC source distribution from current batch to # openmc_src_rate - self._openmc_src_rate = np.append(self._openmc_src_rate, openmc_src, - axis=4) + self._openmc_src_rate = np.append(self._openmc_src_rate, openmc_src, axis=4) # Compute source distribution over entire tally window self._openmc_src = np.sum(self._openmc_src_rate, axis=4) # Compute k_eff from source distribution - self._keff_bal = (np.sum(self._openmc_src) / num_realizations / - tally_windows) + self._keff_bal = np.sum(self._openmc_src) / num_realizations / tally_windows # Normalize openmc source distribution self._openmc_src /= np.sum(self._openmc_src) * self._norm # Get surface currents from CMFD tally 2 tally_id = self._tally_ids[2] - current = tallies[tally_id].results[:,0,1] + current = tallies[tally_id].results[:, 0, 1] # Define target tally reshape dimensions for current target_tally_shape = [nz, ny, nx, 12, ng, 1] # Reshape current array to target shape. Swap x and z axes so that # shape is now [nx, ny, nz, 12, ng, 1] - reshape_current = np.swapaxes(current.reshape(target_tally_shape), - 0, 2) + reshape_current = np.swapaxes(current.reshape(target_tally_shape), 0, 2) # Current is flipped in energy axis as tally results are given in # reverse order of energy group reshape_current = np.flip(reshape_current, axis=4) # Bank current to current_rate - self._current_rate = np.append(self._current_rate, reshape_current, - axis=5) + self._current_rate = np.append(self._current_rate, reshape_current, axis=5) # Compute current as aggregate of banked current_rate over tally window - self._current = np.where(is_accel[..., np.newaxis, np.newaxis], - np.sum(self._current_rate, axis=5), 0.0) + self._current = np.where( + is_accel[..., np.newaxis, np.newaxis], + np.sum(self._current_rate, axis=5), + 0.0, + ) # Get p1 scatter rr from CMFD tally 3 tally_id = self._tally_ids[3] - p1scattrr = tallies[tally_id].results[:,0,1] + p1scattrr = tallies[tally_id].results[:, 0, 1] # Define target tally reshape dimensions for p1 scatter tally target_tally_shape = [nz, ny, nx, 2, ng, 1] # Reshape and extract only p1 data from tally results as there is # no need for p0 data - reshape_p1scattrr = np.swapaxes(p1scattrr.reshape(target_tally_shape), - 0, 2)[:,:,:,1,:,:] + reshape_p1scattrr = np.swapaxes(p1scattrr.reshape(target_tally_shape), 0, 2)[ + :, :, :, 1, :, : + ] # p1-scatter rr is flipped in energy axis as tally results are given in # reverse order of energy group reshape_p1scattrr = np.flip(reshape_p1scattrr, axis=3) # Bank p1-scatter rr to p1scatt_rate - self._p1scatt_rate = np.append(self._p1scatt_rate, reshape_p1scattrr, - axis=4) + self._p1scatt_rate = np.append(self._p1scatt_rate, reshape_p1scattrr, axis=4) # Compute p1-scatter xs as aggregate of banked p1scatt_rate over tally # window divided by flux - self._p1scattxs = np.divide(np.sum(self._p1scatt_rate, axis=4), - self._flux, where=self._flux > 0, - out=np.zeros_like(self._p1scattxs)) + self._p1scattxs = np.divide( + np.sum(self._p1scatt_rate, axis=4), + self._flux, + where=self._flux > 0, + out=np.zeros_like(self._p1scattxs), + ) if self._set_reference_params: # Set diffusion coefficients based on reference value - self._diffcof = np.where(self._flux > 0, - self._ref_d[None, None, None, :], 0.0) + self._diffcof = np.where( + self._flux > 0, self._ref_d[None, None, None, :], 0.0 + ) else: # Calculate and store diffusion coefficient - with np.errstate(divide='ignore', invalid='ignore'): - self._diffcof = np.where(self._flux > 0, 1.0 / (3.0 * - (self._totalxs-self._p1scattxs)), 0.) + with np.errstate(divide="ignore", invalid="ignore"): + self._diffcof = np.where( + self._flux > 0, 1.0 / (3.0 * (self._totalxs - self._p1scattxs)), 0.0 + ) def _compute_effective_downscatter(self): """Changes downscatter rate for zero upscatter""" @@ -2027,35 +2090,35 @@ def _compute_effective_downscatter(self): return # Extract cross sections and flux for each group - flux1 = self._flux[:,:,:,0] - flux2 = self._flux[:,:,:,1] - sigt1 = self._totalxs[:,:,:,0] - sigt2 = self._totalxs[:,:,:,1] + flux1 = self._flux[:, :, :, 0] + flux2 = self._flux[:, :, :, 1] + sigt1 = self._totalxs[:, :, :, 0] + sigt2 = self._totalxs[:, :, :, 1] # First energy index is incoming energy, second is outgoing energy - sigs11 = self._scattxs[:,:,:,0,0] - sigs21 = self._scattxs[:,:,:,1,0] - sigs12 = self._scattxs[:,:,:,0,1] - sigs22 = self._scattxs[:,:,:,1,1] + sigs11 = self._scattxs[:, :, :, 0, 0] + sigs21 = self._scattxs[:, :, :, 1, 0] + sigs12 = self._scattxs[:, :, :, 0, 1] + sigs22 = self._scattxs[:, :, :, 1, 1] # Compute absorption xs siga1 = sigt1 - sigs11 - sigs12 siga2 = sigt2 - sigs22 - sigs21 # Compute effective downscatter XS - sigs12_eff = sigs12 - sigs21 * np.divide(flux2, flux1, - where=flux1 > 0, - out=np.zeros_like(flux2)) + sigs12_eff = sigs12 - sigs21 * np.divide( + flux2, flux1, where=flux1 > 0, out=np.zeros_like(flux2) + ) # Recompute total cross sections and record - self._totalxs[:,:,:,0] = siga1 + sigs11 + sigs12_eff - self._totalxs[:,:,:,1] = siga2 + sigs22 + self._totalxs[:, :, :, 0] = siga1 + sigs11 + sigs12_eff + self._totalxs[:, :, :, 1] = siga2 + sigs22 # Record effective dowmscatter xs - self._scattxs[:,:,:,0,1] = sigs12_eff + self._scattxs[:, :, :, 0, 1] = sigs12_eff # Zero out upscatter cross section - self._scattxs[:,:,:,1,0] = 0.0 + self._scattxs[:, :, :, 1, 0] = 0.0 def _neutron_balance(self): """Computes the RMS neutron balance over the CMFD mesh""" @@ -2069,43 +2132,62 @@ def _neutron_balance(self): keff = openmc.lib.keff()[0] # Define leakage in each mesh cell and energy group - leakage = (((self._current[:,:,:,_CURRENTS['out_right'],:] - - self._current[:,:,:,_CURRENTS['in_right'],:]) - - (self._current[:,:,:,_CURRENTS['in_left'],:] - - self._current[:,:,:,_CURRENTS['out_left'],:])) + - ((self._current[:,:,:,_CURRENTS['out_front'],:] - - self._current[:,:,:,_CURRENTS['in_front'],:]) - - (self._current[:,:,:,_CURRENTS['in_back'],:] - - self._current[:,:,:,_CURRENTS['out_back'],:])) + - ((self._current[:,:,:,_CURRENTS['out_top'],:] - - self._current[:,:,:,_CURRENTS['in_top'],:]) - - (self._current[:,:,:,_CURRENTS['in_bottom'],:] - - self._current[:,:,:,_CURRENTS['out_bottom'],:]))) + leakage = ( + ( + ( + self._current[:, :, :, _CURRENTS["out_right"], :] + - self._current[:, :, :, _CURRENTS["in_right"], :] + ) + - ( + self._current[:, :, :, _CURRENTS["in_left"], :] + - self._current[:, :, :, _CURRENTS["out_left"], :] + ) + ) + + ( + ( + self._current[:, :, :, _CURRENTS["out_front"], :] + - self._current[:, :, :, _CURRENTS["in_front"], :] + ) + - ( + self._current[:, :, :, _CURRENTS["in_back"], :] + - self._current[:, :, :, _CURRENTS["out_back"], :] + ) + ) + + ( + ( + self._current[:, :, :, _CURRENTS["out_top"], :] + - self._current[:, :, :, _CURRENTS["in_top"], :] + ) + - ( + self._current[:, :, :, _CURRENTS["in_bottom"], :] + - self._current[:, :, :, _CURRENTS["out_bottom"], :] + ) + ) + ) # Compute total rr interactions = self._totalxs * self._flux # Compute scattering rr by broadcasting flux in outgoing energy and # summing over incoming energy - scattering = np.sum(self._scattxs * self._flux[:,:,:,:, np.newaxis], - axis=3) + scattering = np.sum(self._scattxs * self._flux[:, :, :, :, np.newaxis], axis=3) # Compute fission rr by broadcasting flux in outgoing energy and # summing over incoming energy - fission = np.sum(self._nfissxs * self._flux[:,:,:,:, np.newaxis], - axis=3) + fission = np.sum(self._nfissxs * self._flux[:, :, :, :, np.newaxis], axis=3) # Compute residual res = leakage + interactions - scattering - (1.0 / keff) * fission # Normalize res by flux and bank res - self._resnb = np.divide(res, self._flux, where=self._flux > 0, - out=np.zeros_like(self._flux)) + self._resnb = np.divide( + res, self._flux, where=self._flux > 0, out=np.zeros_like(self._flux) + ) # Calculate RMS and record for this batch - self._balance.append(np.sqrt( - np.sum(np.multiply(self._resnb, self._resnb)) / - (ng * num_accel))) + self._balance.append( + np.sqrt(np.sum(np.multiply(self._resnb, self._resnb)) / (ng * num_accel)) + ) def _precompute_array_indices(self): """Initializes cross section arrays and computes the indices @@ -2137,8 +2219,10 @@ def _precompute_array_indices(self): # Check length of reference diffusion parameters equal to number of # energy groups if self._ref_d.size != self._indices[3]: - raise OpenMCError('Number of reference diffusion parameters ' - 'must equal number of CMFD energy groups') + raise OpenMCError( + "Number of reference diffusion parameters " + "must equal number of CMFD energy groups" + ) # Logical for determining whether region of interest is accelerated # region @@ -2148,131 +2232,166 @@ def _precompute_array_indices(self): x_inds, y_inds, z_inds = np.indices((nx, ny, nz)) # Define slice equivalent to is_accel[0,:,:] - slice_x = x_inds[:1,:,:] - slice_y = y_inds[:1,:,:] - slice_z = z_inds[:1,:,:] + slice_x = x_inds[:1, :, :] + slice_y = y_inds[:1, :, :] + slice_z = z_inds[:1, :, :] bndry_accel = is_accel[(slice_x, slice_y, slice_z)] - self._first_x_accel = (slice_x[bndry_accel], slice_y[bndry_accel], - slice_z[bndry_accel]) + self._first_x_accel = ( + slice_x[bndry_accel], + slice_y[bndry_accel], + slice_z[bndry_accel], + ) # Define slice equivalent to is_accel[-1,:,:] - slice_x = x_inds[-1:,:,:] - slice_y = y_inds[-1:,:,:] - slice_z = z_inds[-1:,:,:] + slice_x = x_inds[-1:, :, :] + slice_y = y_inds[-1:, :, :] + slice_z = z_inds[-1:, :, :] bndry_accel = is_accel[(slice_x, slice_y, slice_z)] - self._last_x_accel = (slice_x[bndry_accel], slice_y[bndry_accel], - slice_z[bndry_accel]) + self._last_x_accel = ( + slice_x[bndry_accel], + slice_y[bndry_accel], + slice_z[bndry_accel], + ) # Define slice equivalent to is_accel[:,0,:] - slice_x = x_inds[:,:1,:] - slice_y = y_inds[:,:1,:] - slice_z = z_inds[:,:1,:] + slice_x = x_inds[:, :1, :] + slice_y = y_inds[:, :1, :] + slice_z = z_inds[:, :1, :] bndry_accel = is_accel[(slice_x, slice_y, slice_z)] - self._first_y_accel = (slice_x[bndry_accel], slice_y[bndry_accel], - slice_z[bndry_accel]) + self._first_y_accel = ( + slice_x[bndry_accel], + slice_y[bndry_accel], + slice_z[bndry_accel], + ) # Define slice equivalent to is_accel[:,-1,:] - slice_x = x_inds[:,-1:,:] - slice_y = y_inds[:,-1:,:] - slice_z = z_inds[:,-1:,:] + slice_x = x_inds[:, -1:, :] + slice_y = y_inds[:, -1:, :] + slice_z = z_inds[:, -1:, :] bndry_accel = is_accel[(slice_x, slice_y, slice_z)] - self._last_y_accel = (slice_x[bndry_accel], slice_y[bndry_accel], - slice_z[bndry_accel]) + self._last_y_accel = ( + slice_x[bndry_accel], + slice_y[bndry_accel], + slice_z[bndry_accel], + ) # Define slice equivalent to is_accel[:,:,0] - slice_x = x_inds[:,:,:1] - slice_y = y_inds[:,:,:1] - slice_z = z_inds[:,:,:1] + slice_x = x_inds[:, :, :1] + slice_y = y_inds[:, :, :1] + slice_z = z_inds[:, :, :1] bndry_accel = is_accel[(slice_x, slice_y, slice_z)] - self._first_z_accel = (slice_x[bndry_accel], slice_y[bndry_accel], - slice_z[bndry_accel]) + self._first_z_accel = ( + slice_x[bndry_accel], + slice_y[bndry_accel], + slice_z[bndry_accel], + ) # Define slice equivalent to is_accel[:,:,-1] - slice_x = x_inds[:,:,-1:] - slice_y = y_inds[:,:,-1:] - slice_z = z_inds[:,:,-1:] + slice_x = x_inds[:, :, -1:] + slice_y = y_inds[:, :, -1:] + slice_z = z_inds[:, :, -1:] bndry_accel = is_accel[(slice_x, slice_y, slice_z)] - self._last_z_accel = (slice_x[bndry_accel], slice_y[bndry_accel], - slice_z[bndry_accel]) + self._last_z_accel = ( + slice_x[bndry_accel], + slice_y[bndry_accel], + slice_z[bndry_accel], + ) # Define slice equivalent to is_accel[1:,:,:] - slice_x = x_inds[1:,:,:] - slice_y = y_inds[1:,:,:] - slice_z = z_inds[1:,:,:] + slice_x = x_inds[1:, :, :] + slice_y = y_inds[1:, :, :] + slice_z = z_inds[1:, :, :] bndry_accel = is_accel[(slice_x, slice_y, slice_z)] - self._notfirst_x_accel = (slice_x[bndry_accel], slice_y[bndry_accel], - slice_z[bndry_accel]) + self._notfirst_x_accel = ( + slice_x[bndry_accel], + slice_y[bndry_accel], + slice_z[bndry_accel], + ) # Define slice equivalent to is_accel[:-1,:,:] - slice_x = x_inds[:-1,:,:] - slice_y = y_inds[:-1,:,:] - slice_z = z_inds[:-1,:,:] + slice_x = x_inds[:-1, :, :] + slice_y = y_inds[:-1, :, :] + slice_z = z_inds[:-1, :, :] bndry_accel = is_accel[(slice_x, slice_y, slice_z)] - self._notlast_x_accel = (slice_x[bndry_accel], slice_y[bndry_accel], - slice_z[bndry_accel]) + self._notlast_x_accel = ( + slice_x[bndry_accel], + slice_y[bndry_accel], + slice_z[bndry_accel], + ) # Define slice equivalent to is_accel[:,1:,:] - slice_x = x_inds[:,1:,:] - slice_y = y_inds[:,1:,:] - slice_z = z_inds[:,1:,:] + slice_x = x_inds[:, 1:, :] + slice_y = y_inds[:, 1:, :] + slice_z = z_inds[:, 1:, :] bndry_accel = is_accel[(slice_x, slice_y, slice_z)] - self._notfirst_y_accel = (slice_x[bndry_accel], slice_y[bndry_accel], - slice_z[bndry_accel]) + self._notfirst_y_accel = ( + slice_x[bndry_accel], + slice_y[bndry_accel], + slice_z[bndry_accel], + ) # Define slice equivalent to is_accel[:,:-1,:] - slice_x = x_inds[:,:-1,:] - slice_y = y_inds[:,:-1,:] - slice_z = z_inds[:,:-1,:] + slice_x = x_inds[:, :-1, :] + slice_y = y_inds[:, :-1, :] + slice_z = z_inds[:, :-1, :] bndry_accel = is_accel[(slice_x, slice_y, slice_z)] - self._notlast_y_accel = (slice_x[bndry_accel], slice_y[bndry_accel], - slice_z[bndry_accel]) + self._notlast_y_accel = ( + slice_x[bndry_accel], + slice_y[bndry_accel], + slice_z[bndry_accel], + ) # Define slice equivalent to is_accel[:,:,1:] - slice_x = x_inds[:,:,1:] - slice_y = y_inds[:,:,1:] - slice_z = z_inds[:,:,1:] + slice_x = x_inds[:, :, 1:] + slice_y = y_inds[:, :, 1:] + slice_z = z_inds[:, :, 1:] bndry_accel = is_accel[(slice_x, slice_y, slice_z)] - self._notfirst_z_accel = (slice_x[bndry_accel], slice_y[bndry_accel], - slice_z[bndry_accel]) + self._notfirst_z_accel = ( + slice_x[bndry_accel], + slice_y[bndry_accel], + slice_z[bndry_accel], + ) # Define slice equivalent to is_accel[:,:,:-1] - slice_x = x_inds[:,:,:-1] - slice_y = y_inds[:,:,:-1] - slice_z = z_inds[:,:,:-1] + slice_x = x_inds[:, :, :-1] + slice_y = y_inds[:, :, :-1] + slice_z = z_inds[:, :, :-1] bndry_accel = is_accel[(slice_x, slice_y, slice_z)] - self._notlast_z_accel = (slice_x[bndry_accel], slice_y[bndry_accel], - slice_z[bndry_accel]) + self._notlast_z_accel = ( + slice_x[bndry_accel], + slice_y[bndry_accel], + slice_z[bndry_accel], + ) # Store logical for whether neighboring cell is reflector region # in all directions adj_reflector_left = np.roll(self._coremap, 1, axis=0) == _CMFD_NOACCEL self._is_adj_ref_left = adj_reflector_left[ - self._notfirst_x_accel + (np.newaxis,)] + self._notfirst_x_accel + (np.newaxis,) + ] - adj_reflector_right = np.roll(self._coremap, -1, axis=0) == \ - _CMFD_NOACCEL + adj_reflector_right = np.roll(self._coremap, -1, axis=0) == _CMFD_NOACCEL self._is_adj_ref_right = adj_reflector_right[ - self._notlast_x_accel + (np.newaxis,)] + self._notlast_x_accel + (np.newaxis,) + ] - adj_reflector_back = np.roll(self._coremap, 1, axis=1) == \ - _CMFD_NOACCEL + adj_reflector_back = np.roll(self._coremap, 1, axis=1) == _CMFD_NOACCEL self._is_adj_ref_back = adj_reflector_back[ - self._notfirst_y_accel + (np.newaxis,)] + self._notfirst_y_accel + (np.newaxis,) + ] - adj_reflector_front = np.roll(self._coremap, -1, axis=1) == \ - _CMFD_NOACCEL + adj_reflector_front = np.roll(self._coremap, -1, axis=1) == _CMFD_NOACCEL self._is_adj_ref_front = adj_reflector_front[ - self._notlast_y_accel + (np.newaxis,)] + self._notlast_y_accel + (np.newaxis,) + ] - adj_reflector_bottom = np.roll(self._coremap, 1, axis=2) == \ - _CMFD_NOACCEL + adj_reflector_bottom = np.roll(self._coremap, 1, axis=2) == _CMFD_NOACCEL self._is_adj_ref_bottom = adj_reflector_bottom[ - self._notfirst_z_accel + (np.newaxis,)] + self._notfirst_z_accel + (np.newaxis,) + ] - adj_reflector_top = np.roll(self._coremap, -1, axis=2) == \ - _CMFD_NOACCEL - self._is_adj_ref_top = adj_reflector_top[ - self._notlast_z_accel + (np.newaxis,)] + adj_reflector_top = np.roll(self._coremap, -1, axis=2) == _CMFD_NOACCEL + self._is_adj_ref_top = adj_reflector_top[self._notlast_z_accel + (np.newaxis,)] def _precompute_matrix_indices(self): """Computes the indices and row/column data used to populate CMFD CSR @@ -2285,29 +2404,47 @@ def _precompute_matrix_indices(self): # Shift coremap in all directions to determine whether leakage term # should be defined for particular cell in matrix - coremap_shift_left = np.pad(self._coremap, ((1,0),(0,0),(0,0)), - mode='constant', - constant_values=_CMFD_NOACCEL)[:-1,:,:] - - coremap_shift_right = np.pad(self._coremap, ((0,1),(0,0),(0,0)), - mode='constant', - constant_values=_CMFD_NOACCEL)[1:,:,:] - - coremap_shift_back = np.pad(self._coremap, ((0,0),(1,0),(0,0)), - mode='constant', - constant_values=_CMFD_NOACCEL)[:,:-1,:] - - coremap_shift_front = np.pad(self._coremap, ((0,0),(0,1),(0,0)), - mode='constant', - constant_values=_CMFD_NOACCEL)[:,1:,:] - - coremap_shift_bottom = np.pad(self._coremap, ((0,0),(0,0),(1,0)), - mode='constant', - constant_values=_CMFD_NOACCEL)[:,:,:-1] - - coremap_shift_top = np.pad(self._coremap, ((0,0),(0,0),(0,1)), - mode='constant', - constant_values=_CMFD_NOACCEL)[:,:,1:] + coremap_shift_left = np.pad( + self._coremap, + ((1, 0), (0, 0), (0, 0)), + mode="constant", + constant_values=_CMFD_NOACCEL, + )[:-1, :, :] + + coremap_shift_right = np.pad( + self._coremap, + ((0, 1), (0, 0), (0, 0)), + mode="constant", + constant_values=_CMFD_NOACCEL, + )[1:, :, :] + + coremap_shift_back = np.pad( + self._coremap, + ((0, 0), (1, 0), (0, 0)), + mode="constant", + constant_values=_CMFD_NOACCEL, + )[:, :-1, :] + + coremap_shift_front = np.pad( + self._coremap, + ((0, 0), (0, 1), (0, 0)), + mode="constant", + constant_values=_CMFD_NOACCEL, + )[:, 1:, :] + + coremap_shift_bottom = np.pad( + self._coremap, + ((0, 0), (0, 0), (1, 0)), + mode="constant", + constant_values=_CMFD_NOACCEL, + )[:, :, :-1] + + coremap_shift_top = np.pad( + self._coremap, + ((0, 0), (0, 0), (0, 1)), + mode="constant", + constant_values=_CMFD_NOACCEL, + )[:, :, 1:] # Create empty row and column vectors to store for loss matrix row = np.array([], dtype=int) @@ -2316,18 +2453,24 @@ def _precompute_matrix_indices(self): # Store all indices used to populate production and loss matrix is_accel = self._coremap != _CMFD_NOACCEL self._accel_idxs = np.where(is_accel) - self._accel_neig_left_idxs = (np.where(is_accel & - (coremap_shift_left != _CMFD_NOACCEL))) - self._accel_neig_right_idxs = (np.where(is_accel & - (coremap_shift_right != _CMFD_NOACCEL))) - self._accel_neig_back_idxs = (np.where(is_accel & - (coremap_shift_back != _CMFD_NOACCEL))) - self._accel_neig_front_idxs = (np.where(is_accel & - (coremap_shift_front != _CMFD_NOACCEL))) - self._accel_neig_bot_idxs = (np.where(is_accel & - (coremap_shift_bottom != _CMFD_NOACCEL))) - self._accel_neig_top_idxs = (np.where(is_accel & - (coremap_shift_top != _CMFD_NOACCEL))) + self._accel_neig_left_idxs = np.where( + is_accel & (coremap_shift_left != _CMFD_NOACCEL) + ) + self._accel_neig_right_idxs = np.where( + is_accel & (coremap_shift_right != _CMFD_NOACCEL) + ) + self._accel_neig_back_idxs = np.where( + is_accel & (coremap_shift_back != _CMFD_NOACCEL) + ) + self._accel_neig_front_idxs = np.where( + is_accel & (coremap_shift_front != _CMFD_NOACCEL) + ) + self._accel_neig_bot_idxs = np.where( + is_accel & (coremap_shift_bottom != _CMFD_NOACCEL) + ) + self._accel_neig_top_idxs = np.where( + is_accel & (coremap_shift_top != _CMFD_NOACCEL) + ) for g in range(ng): # Extract row and column data of regions where a cell and its @@ -2361,8 +2504,7 @@ def _precompute_matrix_indices(self): # Extract row and column data of regions where a cell and its # neighbor to the bottom are both fuel regions idx_x = ng * (self._coremap[self._accel_neig_bot_idxs]) + g - idx_y = ng * (coremap_shift_bottom[self._accel_neig_bot_idxs]) \ - + g + idx_y = ng * (coremap_shift_bottom[self._accel_neig_bot_idxs]) + g row = np.append(row, idx_x) col = np.append(col, idx_y) @@ -2431,9 +2573,9 @@ def _compute_dtilde(self): self._dtilde[boundary_grps + (0,)] = 2.0 * D / dx else: alb = self._albedo[0] - self._dtilde[boundary_grps + (0,)] = ((2.0 * D * (1.0 - alb)) - / (4.0 * D * (1.0 + alb) + - (1.0 - alb) * dx)) + self._dtilde[boundary_grps + (0,)] = (2.0 * D * (1.0 - alb)) / ( + 4.0 * D * (1.0 + alb) + (1.0 - alb) * dx + ) # Define dtilde at right surface for all mesh cells on right boundary # Separate between zero flux b.c. and alebdo b.c. @@ -2445,9 +2587,9 @@ def _compute_dtilde(self): self._dtilde[boundary_grps + (1,)] = 2.0 * D / dx else: alb = self._albedo[1] - self._dtilde[boundary_grps + (1,)] = ((2.0 * D * (1.0 - alb)) - / (4.0 * D * (1.0 + alb) + - (1.0 - alb) * dx)) + self._dtilde[boundary_grps + (1,)] = (2.0 * D * (1.0 - alb)) / ( + 4.0 * D * (1.0 + alb) + (1.0 - alb) * dx + ) # Define dtilde at back surface for all mesh cells on back boundary # Separate between zero flux b.c. and alebdo b.c. @@ -2459,9 +2601,9 @@ def _compute_dtilde(self): self._dtilde[boundary_grps + (2,)] = 2.0 * D / dy else: alb = self._albedo[2] - self._dtilde[boundary_grps + (2,)] = ((2.0 * D * (1.0 - alb)) - / (4.0 * D * (1.0 + alb) + - (1.0 - alb) * dy)) + self._dtilde[boundary_grps + (2,)] = (2.0 * D * (1.0 - alb)) / ( + 4.0 * D * (1.0 + alb) + (1.0 - alb) * dy + ) # Define dtilde at front surface for all mesh cells on front boundary # Separate between zero flux b.c. and alebdo b.c. @@ -2473,9 +2615,9 @@ def _compute_dtilde(self): self._dtilde[boundary_grps + (3,)] = 2.0 * D / dy else: alb = self._albedo[3] - self._dtilde[boundary_grps + (3,)] = ((2.0 * D * (1.0 - alb)) - / (4.0 * D * (1.0 + alb) + - (1.0 - alb) * dy)) + self._dtilde[boundary_grps + (3,)] = (2.0 * D * (1.0 - alb)) / ( + 4.0 * D * (1.0 + alb) + (1.0 - alb) * dy + ) # Define dtilde at bottom surface for all mesh cells on bottom boundary # Separate between zero flux b.c. and alebdo b.c. @@ -2487,9 +2629,9 @@ def _compute_dtilde(self): self._dtilde[boundary_grps + (4,)] = 2.0 * D / dz else: alb = self._albedo[4] - self._dtilde[boundary_grps + (4,)] = ((2.0 * D * (1.0 - alb)) - / (4.0 * D * (1.0 + alb) + - (1.0 - alb) * dz)) + self._dtilde[boundary_grps + (4,)] = (2.0 * D * (1.0 - alb)) / ( + 4.0 * D * (1.0 + alb) + (1.0 - alb) * dz + ) # Define dtilde at top surface for all mesh cells on top boundary # Separate between zero flux b.c. and alebdo b.c. @@ -2502,17 +2644,20 @@ def _compute_dtilde(self): self._dtilde[boundary_grps + (5,)] = 2.0 * D / dz else: alb = self._albedo[5] - self._dtilde[boundary_grps + (5,)] = ((2.0 * D * (1 - alb)) - / (4.0 * D * (1.0 + alb) + - (1.0 - alb) * dz)) + self._dtilde[boundary_grps + (5,)] = (2.0 * D * (1 - alb)) / ( + 4.0 * D * (1.0 + alb) + (1.0 - alb) * dz + ) # Define reflector albedo for all cells on the left surface, in case # a cell borders a reflector region on the left - current_in_left = self._current[:,:,:,_CURRENTS['in_left'],:] - current_out_left = self._current[:,:,:,_CURRENTS['out_left'],:] - ref_albedo = np.divide(current_in_left, current_out_left, - where=current_out_left > 1.0e-10, - out=np.ones_like(current_out_left)) + current_in_left = self._current[:, :, :, _CURRENTS["in_left"], :] + current_out_left = self._current[:, :, :, _CURRENTS["out_left"], :] + ref_albedo = np.divide( + current_in_left, + current_out_left, + where=current_out_left > 1.0e-10, + out=np.ones_like(current_out_left), + ) # Diffusion coefficient of neighbor to left neig_dc = np.roll(self._diffcof, 1, axis=0) @@ -2530,18 +2675,23 @@ def _compute_dtilde(self): neig_dx = neig_hxyz[boundary + (np.newaxis, 0)] alb = ref_albedo[boundary_grps] is_adj_ref = self._is_adj_ref_left - dtilde = np.where(is_adj_ref, (2.0 * D * (1.0 - alb)) / - (4.0 * D * (1.0 + alb) + (1.0 - alb) * dx), - (2.0 * D * neig_D) / (neig_dx * D + dx * neig_D)) + dtilde = np.where( + is_adj_ref, + (2.0 * D * (1.0 - alb)) / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dx), + (2.0 * D * neig_D) / (neig_dx * D + dx * neig_D), + ) self._dtilde[boundary_grps + (0,)] = dtilde # Define reflector albedo for all cells on the right surface, in case # a cell borders a reflector region on the right - current_in_right = self._current[:,:,:,_CURRENTS['in_right'],:] - current_out_right = self._current[:,:,:,_CURRENTS['out_right'],:] - ref_albedo = np.divide(current_in_right, current_out_right, - where=current_out_right > 1.0e-10, - out=np.ones_like(current_out_right)) + current_in_right = self._current[:, :, :, _CURRENTS["in_right"], :] + current_out_right = self._current[:, :, :, _CURRENTS["out_right"], :] + ref_albedo = np.divide( + current_in_right, + current_out_right, + where=current_out_right > 1.0e-10, + out=np.ones_like(current_out_right), + ) # Diffusion coefficient of neighbor to right neig_dc = np.roll(self._diffcof, -1, axis=0) @@ -2559,18 +2709,23 @@ def _compute_dtilde(self): neig_dx = neig_hxyz[boundary + (np.newaxis, 0)] alb = ref_albedo[boundary_grps] is_adj_ref = self._is_adj_ref_right - dtilde = np.where(is_adj_ref, (2.0 * D * (1.0 - alb)) / - (4.0 * D * (1.0 + alb) + (1.0 - alb) * dx), - (2.0 * D * neig_D) / (neig_dx * D + dx * neig_D)) + dtilde = np.where( + is_adj_ref, + (2.0 * D * (1.0 - alb)) / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dx), + (2.0 * D * neig_D) / (neig_dx * D + dx * neig_D), + ) self._dtilde[boundary_grps + (1,)] = dtilde # Define reflector albedo for all cells on the back surface, in case # a cell borders a reflector region on the back - current_in_back = self._current[:,:,:,_CURRENTS['in_back'],:] - current_out_back = self._current[:,:,:,_CURRENTS['out_back'],:] - ref_albedo = np.divide(current_in_back, current_out_back, - where=current_out_back > 1.0e-10, - out=np.ones_like(current_out_back)) + current_in_back = self._current[:, :, :, _CURRENTS["in_back"], :] + current_out_back = self._current[:, :, :, _CURRENTS["out_back"], :] + ref_albedo = np.divide( + current_in_back, + current_out_back, + where=current_out_back > 1.0e-10, + out=np.ones_like(current_out_back), + ) # Diffusion coefficient of neighbor to back neig_dc = np.roll(self._diffcof, 1, axis=1) @@ -2588,18 +2743,23 @@ def _compute_dtilde(self): neig_dy = neig_hxyz[boundary + (np.newaxis, 1)] alb = ref_albedo[boundary_grps] is_adj_ref = self._is_adj_ref_back - dtilde = np.where(is_adj_ref, (2.0 * D * (1.0 - alb)) / - (4.0 * D * (1.0 + alb) + (1.0 - alb) * dy), - (2.0 * D * neig_D) / (neig_dy * D + dy * neig_D)) + dtilde = np.where( + is_adj_ref, + (2.0 * D * (1.0 - alb)) / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dy), + (2.0 * D * neig_D) / (neig_dy * D + dy * neig_D), + ) self._dtilde[boundary_grps + (2,)] = dtilde # Define reflector albedo for all cells on the front surface, in case # a cell borders a reflector region in the front - current_in_front = self._current[:,:,:,_CURRENTS['in_front'],:] - current_out_front = self._current[:,:,:,_CURRENTS['out_front'],:] - ref_albedo = np.divide(current_in_front, current_out_front, - where=current_out_front > 1.0e-10, - out=np.ones_like(current_out_front)) + current_in_front = self._current[:, :, :, _CURRENTS["in_front"], :] + current_out_front = self._current[:, :, :, _CURRENTS["out_front"], :] + ref_albedo = np.divide( + current_in_front, + current_out_front, + where=current_out_front > 1.0e-10, + out=np.ones_like(current_out_front), + ) # Diffusion coefficient of neighbor to front neig_dc = np.roll(self._diffcof, -1, axis=1) @@ -2617,18 +2777,23 @@ def _compute_dtilde(self): neig_dy = neig_hxyz[boundary + (np.newaxis, 1)] alb = ref_albedo[boundary_grps] is_adj_ref = self._is_adj_ref_front - dtilde = np.where(is_adj_ref, (2.0 * D * (1.0 - alb)) / - (4.0 * D * (1.0 + alb) + (1.0 - alb) * dy), - (2.0 * D * neig_D) / (neig_dy * D + dy * neig_D)) + dtilde = np.where( + is_adj_ref, + (2.0 * D * (1.0 - alb)) / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dy), + (2.0 * D * neig_D) / (neig_dy * D + dy * neig_D), + ) self._dtilde[boundary_grps + (3,)] = dtilde # Define reflector albedo for all cells on the bottom surface, in case # a cell borders a reflector region on the bottom - current_in_bottom = self._current[:,:,:,_CURRENTS['in_bottom'],:] - current_out_bottom = self._current[:,:,:,_CURRENTS['out_bottom'],:] - ref_albedo = np.divide(current_in_bottom, current_out_bottom, - where=current_out_bottom > 1.0e-10, - out=np.ones_like(current_out_bottom)) + current_in_bottom = self._current[:, :, :, _CURRENTS["in_bottom"], :] + current_out_bottom = self._current[:, :, :, _CURRENTS["out_bottom"], :] + ref_albedo = np.divide( + current_in_bottom, + current_out_bottom, + where=current_out_bottom > 1.0e-10, + out=np.ones_like(current_out_bottom), + ) # Diffusion coefficient of neighbor to bottom neig_dc = np.roll(self._diffcof, 1, axis=2) @@ -2646,18 +2811,23 @@ def _compute_dtilde(self): neig_dz = neig_hxyz[boundary + (np.newaxis, 2)] alb = ref_albedo[boundary_grps] is_adj_ref = self._is_adj_ref_bottom - dtilde = np.where(is_adj_ref, (2.0 * D * (1.0 - alb)) / - (4.0 * D * (1.0 + alb) + (1.0 - alb) * dz), - (2.0 * D * neig_D) / (neig_dz * D + dz * neig_D)) + dtilde = np.where( + is_adj_ref, + (2.0 * D * (1.0 - alb)) / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dz), + (2.0 * D * neig_D) / (neig_dz * D + dz * neig_D), + ) self._dtilde[boundary_grps + (4,)] = dtilde # Define reflector albedo for all cells on the top surface, in case # a cell borders a reflector region on the top - current_in_top = self._current[:,:,:,_CURRENTS['in_top'],:] - current_out_top = self._current[:,:,:,_CURRENTS['out_top'],:] - ref_albedo = np.divide(current_in_top, current_out_top, - where=current_out_top > 1.0e-10, - out=np.ones_like(current_out_top)) + current_in_top = self._current[:, :, :, _CURRENTS["in_top"], :] + current_out_top = self._current[:, :, :, _CURRENTS["out_top"], :] + ref_albedo = np.divide( + current_in_top, + current_out_top, + where=current_out_top > 1.0e-10, + out=np.ones_like(current_out_top), + ) # Diffusion coefficient of neighbor to top neig_dc = np.roll(self._diffcof, -1, axis=2) @@ -2675,9 +2845,11 @@ def _compute_dtilde(self): neig_dz = neig_hxyz[boundary + (np.newaxis, 2)] alb = ref_albedo[boundary_grps] is_adj_ref = self._is_adj_ref_top - dtilde = np.where(is_adj_ref, (2.0 * D * (1.0 - alb)) / - (4.0 * D * (1.0 + alb) + (1.0 - alb) * dz), - (2.0 * D * neig_D) / (neig_dz * D + dz * neig_D)) + dtilde = np.where( + is_adj_ref, + (2.0 * D * (1.0 - alb)) / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dz), + (2.0 * D * neig_D) / (neig_dz * D + dz * neig_D), + ) self._dtilde[boundary_grps + (5,)] = dtilde def _compute_dhat(self): @@ -2690,33 +2862,30 @@ def _compute_dhat(self): """ # Define current in each direction - current_in_left = self._current[:,:,:,_CURRENTS['in_left'],:] - current_out_left = self._current[:,:,:,_CURRENTS['out_left'],:] - current_in_right = self._current[:,:,:,_CURRENTS['in_right'],:] - current_out_right = self._current[:,:,:,_CURRENTS['out_right'],:] - current_in_back = self._current[:,:,:,_CURRENTS['in_back'],:] - current_out_back = self._current[:,:,:,_CURRENTS['out_back'],:] - current_in_front = self._current[:,:,:,_CURRENTS['in_front'],:] - current_out_front = self._current[:,:,:,_CURRENTS['out_front'],:] - current_in_bottom = self._current[:,:,:,_CURRENTS['in_bottom'],:] - current_out_bottom = self._current[:,:,:,_CURRENTS['out_bottom'],:] - current_in_top = self._current[:,:,:,_CURRENTS['in_top'],:] - current_out_top = self._current[:,:,:,_CURRENTS['out_top'],:] - - dx = self._hxyz[:,:,:,np.newaxis,0] - dy = self._hxyz[:,:,:,np.newaxis,1] - dz = self._hxyz[:,:,:,np.newaxis,2] - dxdydz = np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] + current_in_left = self._current[:, :, :, _CURRENTS["in_left"], :] + current_out_left = self._current[:, :, :, _CURRENTS["out_left"], :] + current_in_right = self._current[:, :, :, _CURRENTS["in_right"], :] + current_out_right = self._current[:, :, :, _CURRENTS["out_right"], :] + current_in_back = self._current[:, :, :, _CURRENTS["in_back"], :] + current_out_back = self._current[:, :, :, _CURRENTS["out_back"], :] + current_in_front = self._current[:, :, :, _CURRENTS["in_front"], :] + current_out_front = self._current[:, :, :, _CURRENTS["out_front"], :] + current_in_bottom = self._current[:, :, :, _CURRENTS["in_bottom"], :] + current_out_bottom = self._current[:, :, :, _CURRENTS["out_bottom"], :] + current_in_top = self._current[:, :, :, _CURRENTS["in_top"], :] + current_out_top = self._current[:, :, :, _CURRENTS["out_top"], :] + + dx = self._hxyz[:, :, :, np.newaxis, 0] + dy = self._hxyz[:, :, :, np.newaxis, 1] + dz = self._hxyz[:, :, :, np.newaxis, 2] + dxdydz = np.prod(self._hxyz, axis=3)[:, :, :, np.newaxis] # Define net current on each face net_current_left = (current_in_left - current_out_left) / dxdydz * dx - net_current_right = (current_out_right - current_in_right) / dxdydz * \ - dx + net_current_right = (current_out_right - current_in_right) / dxdydz * dx net_current_back = (current_in_back - current_out_back) / dxdydz * dy - net_current_front = (current_out_front - current_in_front) / dxdydz * \ - dy - net_current_bottom = (current_in_bottom - current_out_bottom) / \ - dxdydz * dz + net_current_front = (current_out_front - current_in_front) / dxdydz * dy + net_current_bottom = (current_in_bottom - current_out_bottom) / dxdydz * dz net_current_top = (current_out_top - current_in_top) / dxdydz * dz # Define flux in each cell @@ -2783,9 +2952,11 @@ def _compute_dhat(self): flux = cell_flux[boundary_grps] flux_left = neig_flux[boundary_grps] is_adj_ref = self._is_adj_ref_left - dhat = np.where(is_adj_ref, (net_current + dtilde * flux) / flux, - (net_current - dtilde * (flux_left - flux)) / - (flux_left + flux)) + dhat = np.where( + is_adj_ref, + (net_current + dtilde * flux) / flux, + (net_current - dtilde * (flux_left - flux)) / (flux_left + flux), + ) self._dhat[boundary_grps + (0,)] = dhat # Cell flux of neighbor to right @@ -2801,9 +2972,11 @@ def _compute_dhat(self): flux = cell_flux[boundary_grps] flux_right = neig_flux[boundary_grps] is_adj_ref = self._is_adj_ref_right - dhat = np.where(is_adj_ref, (net_current - dtilde * flux) / flux, - (net_current + dtilde * (flux_right - flux)) / - (flux_right + flux)) + dhat = np.where( + is_adj_ref, + (net_current - dtilde * flux) / flux, + (net_current + dtilde * (flux_right - flux)) / (flux_right + flux), + ) self._dhat[boundary_grps + (1,)] = dhat # Cell flux of neighbor to back @@ -2819,9 +2992,11 @@ def _compute_dhat(self): flux = cell_flux[boundary_grps] flux_back = neig_flux[boundary_grps] is_adj_ref = self._is_adj_ref_back - dhat = np.where(is_adj_ref, (net_current + dtilde * flux) / flux, - (net_current - dtilde * (flux_back - flux)) / - (flux_back + flux)) + dhat = np.where( + is_adj_ref, + (net_current + dtilde * flux) / flux, + (net_current - dtilde * (flux_back - flux)) / (flux_back + flux), + ) self._dhat[boundary_grps + (2,)] = dhat # Cell flux of neighbor to front @@ -2837,9 +3012,11 @@ def _compute_dhat(self): flux = cell_flux[boundary_grps] flux_front = neig_flux[boundary_grps] is_adj_ref = self._is_adj_ref_front - dhat = np.where(is_adj_ref, (net_current - dtilde * flux) / flux, - (net_current + dtilde * (flux_front - flux)) / - (flux_front + flux)) + dhat = np.where( + is_adj_ref, + (net_current - dtilde * flux) / flux, + (net_current + dtilde * (flux_front - flux)) / (flux_front + flux), + ) self._dhat[boundary_grps + (3,)] = dhat # Cell flux of neighbor to bottom @@ -2855,9 +3032,11 @@ def _compute_dhat(self): flux = cell_flux[boundary_grps] flux_bottom = neig_flux[boundary_grps] is_adj_ref = self._is_adj_ref_bottom - dhat = np.where(is_adj_ref, (net_current + dtilde * flux) / flux, - (net_current - dtilde * (flux_bottom - flux)) / - (flux_bottom + flux)) + dhat = np.where( + is_adj_ref, + (net_current + dtilde * flux) / flux, + (net_current - dtilde * (flux_bottom - flux)) / (flux_bottom + flux), + ) self._dhat[boundary_grps + (4,)] = dhat # Cell flux of neighbor to top @@ -2873,22 +3052,26 @@ def _compute_dhat(self): flux = cell_flux[boundary_grps] flux_top = neig_flux[boundary_grps] is_adj_ref = self._is_adj_ref_top - dhat = np.where(is_adj_ref, (net_current - dtilde * flux) / flux, - (net_current + dtilde * (flux_top - flux)) / - (flux_top + flux)) + dhat = np.where( + is_adj_ref, + (net_current - dtilde * flux) / flux, + (net_current + dtilde * (flux_top - flux)) / (flux_top + flux), + ) self._dhat[boundary_grps + (5,)] = dhat def _create_cmfd_tally(self): """Creates all tallies in-memory that are used to solve CMFD problem""" # Create Mesh object based on CMFDMesh mesh_type, stored internally - if self._mesh.mesh_type == 'regular': + 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.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 @@ -2930,7 +3113,7 @@ def _create_cmfd_tally(self): for i in range(n_tallies): cmfd_tally = openmc.lib.Tally() # Set nuclide bins - cmfd_tally.nuclides = ['total'] + cmfd_tally.nuclides = ["total"] self._tally_ids.append(cmfd_tally.id) # Set attributes of CMFD flux, total tally @@ -2941,22 +3124,21 @@ def _create_cmfd_tally(self): else: cmfd_tally.filters = [mesh_filter] # Set scores, type, and estimator for tally - cmfd_tally.scores = ['flux', 'total'] - cmfd_tally.type = 'volume' - cmfd_tally.estimator = 'analog' + cmfd_tally.scores = ["flux", "total"] + cmfd_tally.type = "volume" + cmfd_tally.estimator = "analog" # Set attributes of CMFD neutron production tally elif i == 1: # Set filters for tally if self._energy_filters: - cmfd_tally.filters = [mesh_filter, energy_filter, - energyout_filter] + cmfd_tally.filters = [mesh_filter, energy_filter, energyout_filter] else: cmfd_tally.filters = [mesh_filter] # Set scores, type, and estimator for tally - cmfd_tally.scores = ['nu-scatter', 'nu-fission'] - cmfd_tally.type = 'volume' - cmfd_tally.estimator = 'analog' + cmfd_tally.scores = ["nu-scatter", "nu-fission"] + cmfd_tally.type = "volume" + cmfd_tally.estimator = "analog" # Set attributes of CMFD surface current tally elif i == 2: @@ -2966,22 +3148,21 @@ def _create_cmfd_tally(self): else: cmfd_tally.filters = [meshsurface_filter] # Set scores, type, and estimator for tally - cmfd_tally.scores = ['current'] - cmfd_tally.type = 'mesh-surface' - cmfd_tally.estimator = 'analog' + cmfd_tally.scores = ["current"] + cmfd_tally.type = "mesh-surface" + cmfd_tally.estimator = "analog" # Set attributes of CMFD P1 scatter tally elif i == 3: # Set filters for tally if self._energy_filters: - cmfd_tally.filters = [mesh_filter, legendre_filter, - energy_filter] + cmfd_tally.filters = [mesh_filter, legendre_filter, energy_filter] else: cmfd_tally.filters = [mesh_filter, legendre_filter] # Set scores for tally - cmfd_tally.scores = ['scatter'] - cmfd_tally.type = 'volume' - cmfd_tally.estimator = 'analog' + cmfd_tally.scores = ["scatter"] + cmfd_tally.type = "volume" + cmfd_tally.estimator = "analog" # Set all tallies to be active from beginning cmfd_tally.active = True diff --git a/openmc/config.py b/openmc/config.py index ab53ab61b5f..d3ab4beb6bd 100644 --- a/openmc/config.py +++ b/openmc/config.py @@ -12,7 +12,7 @@ class _Config(MutableMapping): def __init__(self, data=()): - self._mapping = {'resolve_paths': True} + self._mapping = {"resolve_paths": True} self.update(data) def __getitem__(self, key): @@ -20,35 +20,37 @@ def __getitem__(self, key): def __delitem__(self, key): del self._mapping[key] - if key == 'cross_sections': - del os.environ['OPENMC_CROSS_SECTIONS'] - elif key == 'mg_cross_sections': - del os.environ['OPENMC_MG_CROSS_SECTIONS'] - elif key == 'chain_file': - del os.environ['OPENMC_CHAIN_FILE'] + if key == "cross_sections": + del os.environ["OPENMC_CROSS_SECTIONS"] + elif key == "mg_cross_sections": + del os.environ["OPENMC_MG_CROSS_SECTIONS"] + elif key == "chain_file": + del os.environ["OPENMC_CHAIN_FILE"] # Reset photon source data since it relies on chain file _DECAY_PHOTON_ENERGY.clear() def __setitem__(self, key, value): - if key == 'cross_sections': + if key == "cross_sections": # Force environment variable to match self._set_path(key, value) - os.environ['OPENMC_CROSS_SECTIONS'] = str(value) - elif key == 'mg_cross_sections': + os.environ["OPENMC_CROSS_SECTIONS"] = str(value) + elif key == "mg_cross_sections": self._set_path(key, value) - os.environ['OPENMC_MG_CROSS_SECTIONS'] = str(value) - elif key == 'chain_file': + os.environ["OPENMC_MG_CROSS_SECTIONS"] = str(value) + elif key == "chain_file": self._set_path(key, value) - os.environ['OPENMC_CHAIN_FILE'] = str(value) + os.environ["OPENMC_CHAIN_FILE"] = str(value) # Reset photon source data since it relies on chain file _DECAY_PHOTON_ENERGY.clear() _DECAY_ENERGY.clear() - elif key == 'resolve_paths': + elif key == "resolve_paths": self._mapping[key] = value else: - raise KeyError(f'Unrecognized config key: {key}. Acceptable keys ' - 'are "cross_sections", "mg_cross_sections", ' - '"chain_file", and "resolve_paths".') + raise KeyError( + f"Unrecognized config key: {key}. Acceptable keys " + 'are "cross_sections", "mg_cross_sections", ' + '"chain_file", and "resolve_paths".' + ) def __iter__(self): return iter(self._mapping) @@ -83,30 +85,32 @@ def patch(self, key, value): else: self[key] = previous_value + def _default_config(): """Return default configuration""" config = _Config() # Set cross sections using environment variable if "OPENMC_CROSS_SECTIONS" in os.environ: - config['cross_sections'] = os.environ["OPENMC_CROSS_SECTIONS"] + config["cross_sections"] = os.environ["OPENMC_CROSS_SECTIONS"] if "OPENMC_MG_CROSS_SECTIONS" in os.environ: - config['mg_cross_sections'] = os.environ["OPENMC_MG_CROSS_SECTIONS"] + config["mg_cross_sections"] = os.environ["OPENMC_MG_CROSS_SECTIONS"] # Set depletion chain chain_file = os.environ.get("OPENMC_CHAIN_FILE") - if (chain_file is None and - config.get('cross_sections') is not None and - config['cross_sections'].exists() - ): + if ( + chain_file is None + and config.get("cross_sections") is not None + and config["cross_sections"].exists() + ): # Check for depletion chain in cross_sections.xml - data = DataLibrary.from_xml(config['cross_sections']) + data = DataLibrary.from_xml(config["cross_sections"]) for lib in reversed(data.libraries): - if lib['type'] == 'depletion_chain': - chain_file = lib['path'] + if lib["type"] == "depletion_chain": + chain_file = lib["path"] break if chain_file is not None: - config['chain_file'] = chain_file + config["chain_file"] = chain_file return config diff --git a/openmc/data/ace.py b/openmc/data/ace.py index 1247593a806..aca18a018f1 100644 --- a/openmc/data/ace.py +++ b/openmc/data/ace.py @@ -27,7 +27,7 @@ from .endf import ENDF_FLOAT_RE -def get_metadata(zaid, metastable_scheme='nndc'): +def get_metadata(zaid, metastable_scheme="nndc"): """Return basic identifying data for a nuclide with a given ZAID. Parameters @@ -58,13 +58,13 @@ def get_metadata(zaid, metastable_scheme='nndc'): """ - cv.check_type('zaid', zaid, int) - cv.check_value('metastable_scheme', metastable_scheme, ['nndc', 'mcnp']) + cv.check_type("zaid", zaid, int) + cv.check_value("metastable_scheme", metastable_scheme, ["nndc", "mcnp"]) Z = zaid // 1000 mass_number = zaid % 1000 - if metastable_scheme == 'mcnp': + if metastable_scheme == "mcnp": if zaid > 1000000: # New SZA format Z = Z % 1000 @@ -79,7 +79,7 @@ def get_metadata(zaid, metastable_scheme='nndc'): metastable = 0 else: metastable = 1 if mass_number > 300 else 0 - elif metastable_scheme == 'nndc': + elif metastable_scheme == "nndc": metastable = 1 if mass_number > 300 else 0 while mass_number > 3 * Z: @@ -105,23 +105,24 @@ def ascii_to_binary(ascii_file, binary_file): """ # Read data from ASCII file - with open(str(ascii_file), 'r') as ascii_file: + with open(str(ascii_file), "r") as ascii_file: lines = ascii_file.readlines() # Set default record length record_length = 4096 # Open binary file - with open(str(binary_file), 'wb') as binary_file: + with open(str(binary_file), "wb") as binary_file: idx = 0 while idx < len(lines): # check if it's a > 2.0.0 version header - if lines[idx].split()[0][1] == '.': - if lines[idx + 1].split()[3] == '3': + if lines[idx].split()[0][1] == ".": + if lines[idx + 1].split()[3] == "3": idx = idx + 3 else: - raise NotImplementedError('Only backwards compatible ACE' - 'headers currently supported') + raise NotImplementedError( + "Only backwards compatible ACE" "headers currently supported" + ) # Read/write header block hz = lines[idx][:10].encode() aw0 = float(lines[idx][10:22]) @@ -129,30 +130,32 @@ def ascii_to_binary(ascii_file, binary_file): hd = lines[idx][35:45].encode() hk = lines[idx + 1][:70].encode() hm = lines[idx + 1][70:80].encode() - binary_file.write(struct.pack(str('=10sdd10s70s10s'), - hz, aw0, tz, hd, hk, hm)) + binary_file.write( + struct.pack(str("=10sdd10s70s10s"), hz, aw0, tz, hd, hk, hm) + ) # Read/write IZ/AW pairs - data = ' '.join(lines[idx + 2:idx + 6]).split() + data = " ".join(lines[idx + 2 : idx + 6]).split() iz = np.array(data[::2], dtype=int) aw = np.array(data[1::2], dtype=float) izaw = [item for sublist in zip(iz, aw) for item in sublist] - binary_file.write(struct.pack(str('=' + 16*'id'), *izaw)) + binary_file.write(struct.pack(str("=" + 16 * "id"), *izaw)) # Read/write NXS and JXS arrays. Null bytes are added at the end so # that XSS will start at the second record - nxs = [int(x) for x in ' '.join(lines[idx + 6:idx + 8]).split()] - jxs = [int(x) for x in ' '.join(lines[idx + 8:idx + 12]).split()] - binary_file.write(struct.pack(str(f'=16i32i{record_length - 500}x'), - *(nxs + jxs))) + nxs = [int(x) for x in " ".join(lines[idx + 6 : idx + 8]).split()] + jxs = [int(x) for x in " ".join(lines[idx + 8 : idx + 12]).split()] + binary_file.write( + struct.pack(str(f"=16i32i{record_length - 500}x"), *(nxs + jxs)) + ) # Read/write XSS array. Null bytes are added to form a complete record # at the end of the file - n_lines = (nxs[0] + 3)//4 + n_lines = (nxs[0] + 3) // 4 start = idx + _ACE_HEADER_SIZE - xss = np.fromstring(' '.join(lines[start:start + n_lines]), sep=' ') - extra_bytes = record_length - ((len(xss)*8 - 1) % record_length + 1) - binary_file.write(struct.pack(str(f'={nxs[0]}d{extra_bytes}x'), *xss)) + xss = np.fromstring(" ".join(lines[start : start + n_lines]), sep=" ") + extra_bytes = record_length - ((len(xss) * 8 - 1) % record_length + 1) + binary_file.write(struct.pack(str(f"={nxs[0]}d{extra_bytes}x"), *xss)) # Advance to next table in file idx += _ACE_HEADER_SIZE + n_lines @@ -183,7 +186,7 @@ def get_table(filename, name=None): if lib.tables: return lib.tables[0] else: - raise ValueError(f'Could not find ACE table with name: {name}') + raise ValueError(f"Could not find ACE table with name: {name}") # The beginning of an ASCII ACE file consists of 12 lines that include the name, @@ -224,24 +227,25 @@ def __init__(self, filename, table_names=None, verbose=False): # Determine whether file is ASCII or binary filename = str(filename) try: - fh = open(filename, 'rb') + fh = open(filename, "rb") # Grab 10 lines of the library - sb = b''.join([fh.readline() for i in range(10)]) + sb = b"".join([fh.readline() for i in range(10)]) # Try to decode it with ascii - sb.decode('ascii') + sb.decode("ascii") # No exception so proceed with ASCII - reopen in non-binary fh.close() - with open(filename, 'r') as fh: + with open(filename, "r") as fh: self._read_ascii(fh, table_names, verbose) except UnicodeDecodeError: fh.close() - with open(filename, 'rb') as fh: + with open(filename, "rb") as fh: self._read_binary(fh, table_names, verbose) - def _read_binary(self, ace_file, table_names, verbose=False, - recl_length=4096, entries=512): + def _read_binary( + self, ace_file, table_names, verbose=False, recl_length=4096, entries=512 + ): """Read a binary (Type 2) ACE table. Parameters @@ -271,24 +275,25 @@ def _read_binary(self, ace_file, table_names, verbose=False, # Read name, atomic mass ratio, temperature, date, comment, and # material - name, atomic_weight_ratio, temperature, date, comment, mat = \ - struct.unpack(str('=10sdd10s70s10s'), ace_file.read(116)) + name, atomic_weight_ratio, temperature, date, comment, mat = struct.unpack( + str("=10sdd10s70s10s"), ace_file.read(116) + ) name = name.decode().strip() # Read ZAID/awr combinations - data = struct.unpack(str('=' + 16*'id'), ace_file.read(192)) + data = struct.unpack(str("=" + 16 * "id"), ace_file.read(192)) pairs = list(zip(data[::2], data[1::2])) # Read NXS - nxs = list(struct.unpack(str('=16i'), ace_file.read(64))) + nxs = list(struct.unpack(str("=16i"), ace_file.read(64))) # Determine length of XSS and number of records length = nxs[0] - n_records = (length + entries - 1)//entries + n_records = (length + entries - 1) // entries # verify that we are supposed to read this table in if (table_names is not None) and (name not in table_names): - ace_file.seek(start_position + recl_length*(n_records + 1)) + ace_file.seek(start_position + recl_length * (n_records + 1)) continue if verbose: @@ -296,12 +301,11 @@ def _read_binary(self, ace_file, table_names, verbose=False, print(f"Loading nuclide {name} at {kelvin} K") # Read JXS - jxs = list(struct.unpack(str('=32i'), ace_file.read(128))) + jxs = list(struct.unpack(str("=32i"), ace_file.read(128))) # Read XSS ace_file.seek(start_position + recl_length) - xss = list(struct.unpack(str(f'={length}d'), - ace_file.read(length*8))) + xss = list(struct.unpack(str(f"={length}d"), ace_file.read(length * 8))) # Insert zeros at beginning of NXS, JXS, and XSS arrays so that the # indexing will be the same as Fortran. This makes it easier to @@ -316,12 +320,11 @@ def _read_binary(self, ace_file, table_names, verbose=False, xss = np.array(xss) # Create ACE table with data read in - table = Table(name, atomic_weight_ratio, temperature, pairs, - nxs, jxs, xss) + table = Table(name, atomic_weight_ratio, temperature, pairs, nxs, jxs, xss) self.tables.append(table) # Advance to next record - ace_file.seek(start_position + recl_length*(n_records + 1)) + ace_file.seek(start_position + recl_length * (n_records + 1)) def _read_ascii(self, ace_file, table_names, verbose=False): """Read an ASCII (Type 1) ACE table. @@ -342,12 +345,12 @@ def _read_ascii(self, ace_file, table_names, verbose=False): lines = [ace_file.readline() for i in range(_ACE_HEADER_SIZE + 1)] - while len(lines) != 0 and lines[0].strip() != '': + while len(lines) != 0 and lines[0].strip() != "": # Read name of table, atomic mass ratio, and temperature. If first # line is empty, we are at end of file # check if it's a 2.0 style header - if lines[0].split()[0][1] == '.': + if lines[0].split()[0][1] == ".": words = lines[0].split() name = words[1] words = lines[1].split() @@ -363,16 +366,15 @@ def _read_ascii(self, ace_file, table_names, verbose=False): atomic_weight_ratio = float(words[1]) temperature = float(words[2]) - datastr = ' '.join(lines[2:6]).split() - pairs = list(zip(map(int, datastr[::2]), - map(float, datastr[1::2]))) + datastr = " ".join(lines[2:6]).split() + pairs = list(zip(map(int, datastr[::2]), map(float, datastr[1::2]))) - datastr = '0 ' + ' '.join(lines[6:8]) - nxs = np.fromstring(datastr, sep=' ', dtype=int) + datastr = "0 " + " ".join(lines[6:8]) + nxs = np.fromstring(datastr, sep=" ", dtype=int) # Detemrine number of lines in the XSS array; each line consists of # four values - n_lines = (nxs[1] + 3)//4 + n_lines = (nxs[1] + 3) // 4 # Ensure that we have more tables to read in if (table_names is not None) and (table_names <= tables_seen): @@ -396,11 +398,13 @@ def _read_ascii(self, ace_file, table_names, verbose=False): # Insert zeros at beginning of NXS, JXS, and XSS arrays so that the # indexing will be the same as Fortran. This makes it easier to # follow the ACE format specification. - datastr = '0 ' + ' '.join(lines[8:_ACE_HEADER_SIZE]) - jxs = np.fromstring(datastr, dtype=int, sep=' ') + datastr = "0 " + " ".join(lines[8:_ACE_HEADER_SIZE]) + jxs = np.fromstring(datastr, dtype=int, sep=" ") - datastr = '0.0 ' + ''.join(lines[_ACE_HEADER_SIZE:_ACE_HEADER_SIZE + n_lines]) - xss = np.fromstring(datastr, sep=' ') + datastr = "0.0 " + "".join( + lines[_ACE_HEADER_SIZE : _ACE_HEADER_SIZE + n_lines] + ) + xss = np.fromstring(datastr, sep=" ") # When NJOY writes an ACE file, any values less than 1e-100 actually # get written without the 'e'. Thus, what we do here is check @@ -409,12 +413,11 @@ def _read_ascii(self, ace_file, table_names, verbose=False): # after it). If it's too short, then we apply the ENDF float regular # expression. We don't do this by default because it's expensive! if xss.size != nxs[1] + 1: - datastr = ENDF_FLOAT_RE.sub(r'\1e\2\3', datastr) - xss = np.fromstring(datastr, sep=' ') + datastr = ENDF_FLOAT_RE.sub(r"\1e\2\3", datastr) + xss = np.fromstring(datastr, sep=" ") assert xss.size == nxs[1] + 1 - table = Table(name, atomic_weight_ratio, temperature, pairs, - nxs, jxs, xss) + table = Table(name, atomic_weight_ratio, temperature, pairs, nxs, jxs, xss) self.tables.append(table) # Read all data blocks @@ -423,17 +426,18 @@ def _read_ascii(self, ace_file, table_names, verbose=False): class TableType(enum.Enum): """Type of ACE data table.""" - NEUTRON_CONTINUOUS = 'c' - NEUTRON_DISCRETE = 'd' - THERMAL_SCATTERING = 't' - DOSIMETRY = 'y' - PHOTOATOMIC = 'p' - PHOTONUCLEAR = 'u' - PROTON = 'h' - DEUTERON = 'o' - TRITON = 'r' - HELIUM3 = 's' - ALPHA = 'a' + + NEUTRON_CONTINUOUS = "c" + NEUTRON_DISCRETE = "d" + THERMAL_SCATTERING = "t" + DOSIMETRY = "y" + PHOTOATOMIC = "p" + PHOTONUCLEAR = "u" + PROTON = "h" + DEUTERON = "o" + TRITON = "r" + HELIUM3 = "s" + ALPHA = "a" @classmethod def from_suffix(cls, suffix): @@ -484,8 +488,8 @@ class Table(EqualityMixin): Type of the ACE data """ - def __init__(self, name, atomic_weight_ratio, temperature, pairs, - nxs, jxs, xss): + + def __init__(self, name, atomic_weight_ratio, temperature, pairs, nxs, jxs, xss): self.name = name self.atomic_weight_ratio = atomic_weight_ratio self.temperature = temperature @@ -496,11 +500,11 @@ def __init__(self, name, atomic_weight_ratio, temperature, pairs, @property def zaid(self): - return self.name.split('.')[0] + return self.name.split(".")[0] @property def data_type(self): - xs = self.name.split('.')[1] + xs = self.name.split(".")[1] return TableType.from_suffix(xs[-1]) def __repr__(self): @@ -523,18 +527,17 @@ def get_libraries_from_xsdir(path): xsdir = Path(path) # Find 'directory' section - with open(path, 'r') as fh: + with open(path, "r") as fh: lines = fh.readlines() for index, line in enumerate(lines): - if line.strip().lower() == 'directory': + if line.strip().lower() == "directory": break else: raise RuntimeError("Could not find 'directory' section in MCNP xsdir file") # Handle continuation lines indicated by '+' at end of line - lines = lines[index + 1:] - continue_lines = [i for i, line in enumerate(lines) - if line.strip().endswith('+')] + lines = lines[index + 1 :] + continue_lines = [i for i, line in enumerate(lines) if line.strip().endswith("+")] for i in reversed(continue_lines): lines[i] = lines[i].strip()[:-1] + lines.pop(i + 1) @@ -569,7 +572,7 @@ def get_libraries_from_xsdata(path): List of paths to ACE libraries """ xsdata = Path(path) - with open(xsdata, 'r') as xsdata_file: + with open(xsdata, "r") as xsdata_file: # As in get_libraries_from_xsdir, we use a dict for O(1) membership # check while retaining insertion order libraries = {} diff --git a/openmc/data/angle_distribution.py b/openmc/data/angle_distribution.py index e59ffa0c737..efef8ddc71d 100644 --- a/openmc/data/angle_distribution.py +++ b/openmc/data/angle_distribution.py @@ -10,8 +10,13 @@ from openmc.stats import Univariate, Tabular, Uniform, Legendre from .function import INTERPOLATION_SCHEME from .data import EV_PER_MEV -from .endf import get_head_record, get_cont_record, get_tab1_record, \ - get_list_record, get_tab2_record +from .endf import ( + get_head_record, + get_cont_record, + get_tab1_record, + get_list_record, + get_tab2_record, +) class AngleDistribution(EqualityMixin): @@ -44,8 +49,7 @@ def energy(self): @energy.setter def energy(self, energy): - cv.check_type('angle distribution incoming energy', energy, - Iterable, Real) + cv.check_type("angle distribution incoming energy", energy, Iterable, Real) self._energy = energy @property @@ -54,8 +58,7 @@ def mu(self): @mu.setter def mu(self, mu): - cv.check_type('angle distribution scattering cosines', mu, - Iterable, Univariate) + cv.check_type("angle distribution scattering cosines", mu, Iterable, Univariate) self._mu = mu def to_hdf5(self, group): @@ -68,11 +71,12 @@ def to_hdf5(self, group): """ - dset = group.create_dataset('energy', data=self.energy) + dset = group.create_dataset("energy", data=self.energy) # Make sure all data is tabular - mu_tabular = [mu_i if isinstance(mu_i, Tabular) else - mu_i.to_tabular() for mu_i in self.mu] + mu_tabular = [ + mu_i if isinstance(mu_i, Tabular) else mu_i.to_tabular() for mu_i in self.mu + ] # Determine total number of (mu,p) pairs and create array n_pairs = sum([len(mu_i.x) for mu_i in mu_tabular]) @@ -87,18 +91,18 @@ def to_hdf5(self, group): for i, mu_i in enumerate(mu_tabular): n = len(mu_i.x) offsets[i] = j - interpolation[i] = 1 if mu_i.interpolation == 'histogram' else 2 - pairs[0, j:j+n] = mu_i.x - pairs[1, j:j+n] = mu_i.p - pairs[2, j:j+n] = mu_i.c + interpolation[i] = 1 if mu_i.interpolation == "histogram" else 2 + pairs[0, j : j + n] = mu_i.x + pairs[1, j : j + n] = mu_i.p + pairs[2, j : j + n] = mu_i.c j += n # Create dataset for distributions - dset = group.create_dataset('mu', data=pairs) + dset = group.create_dataset("mu", data=pairs) # Write interpolation as attribute - dset.attrs['offsets'] = offsets - dset.attrs['interpolation'] = interpolation + dset.attrs["offsets"] = offsets + dset.attrs["interpolation"] = interpolation @classmethod def from_hdf5(cls, group): @@ -115,10 +119,10 @@ def from_hdf5(cls, group): Angular distribution """ - energy = group['energy'][()] - data = group['mu'] - offsets = data.attrs['offsets'] - interpolation = data.attrs['interpolation'] + energy = group["energy"][()] + data = group["mu"] + offsets = data.attrs["offsets"] + interpolation = data.attrs["interpolation"] mu = [] n_energy = len(energy) @@ -127,13 +131,13 @@ def from_hdf5(cls, group): # discrete lines j = offsets[i] if i < n_energy - 1: - n = offsets[i+1] - j + n = offsets[i + 1] - j else: n = data.shape[1] - j interp = INTERPOLATION_SCHEME[interpolation[i]] - mu_i = Tabular(data[0, j:j+n], data[1, j:j+n], interp) - mu_i.c = data[2, j:j+n] + mu_i = Tabular(data[0, j : j + n], data[1, j : j + n], interp) + mu_i.c = data[2, j : j + n] mu.append(mu_i) @@ -168,11 +172,11 @@ def from_ace(cls, ace, location_dist, location_start): idx += 1 # Incoming energy grid - energy = ace.xss[idx:idx + n_energies]*EV_PER_MEV + energy = ace.xss[idx : idx + n_energies] * EV_PER_MEV idx += n_energies # Read locations for angular distributions - lc = ace.xss[idx:idx + n_energies].astype(int) + lc = ace.xss[idx : idx + n_energies].astype(int) idx += n_energies mu = [] @@ -181,12 +185,12 @@ def from_ace(cls, ace, location_dist, location_start): # Equiprobable 32 bin distribution n_bins = 32 idx = location_dist + abs(lc[i]) - 1 - cos = ace.xss[idx:idx + n_bins + 1] + cos = ace.xss[idx : idx + n_bins + 1] pdf = np.zeros(n_bins + 1) - pdf[:n_bins] = 1.0/(n_bins*np.diff(cos)) + pdf[:n_bins] = 1.0 / (n_bins * np.diff(cos)) cdf = np.linspace(0.0, 1.0, n_bins + 1) - mu_i = Tabular(cos, pdf, 'histogram', ignore_negative=True) + mu_i = Tabular(cos, pdf, "histogram", ignore_negative=True) mu_i.c = cdf elif lc[i] < 0: # Tabular angular distribution @@ -194,14 +198,14 @@ def from_ace(cls, ace, location_dist, location_start): intt = int(ace.xss[idx]) n_points = int(ace.xss[idx + 1]) # Data is given as rows of (values, PDF, CDF) - data = ace.xss[idx + 2:idx + 2 + 3*n_points] + data = ace.xss[idx + 2 : idx + 2 + 3 * n_points] data.shape = (3, n_points) mu_i = Tabular(data[0], data[1], INTERPOLATION_SCHEME[intt]) mu_i.c = data[2] else: # Isotropic angular distribution - mu_i = Uniform(-1., 1.) + mu_i = Uniform(-1.0, 1.0) mu.append(mu_i) @@ -239,15 +243,16 @@ def from_endf(cls, ev, mt): # Check for obsolete energy transformation matrix. If present, just skip # it and keep reading if lvt > 0: - warn('Obsolete energy transformation matrix in MF=4 angular ' - 'distribution.') - for _ in range((nk + 5)//6): + warn( + "Obsolete energy transformation matrix in MF=4 angular " "distribution." + ) + for _ in range((nk + 5) // 6): file_obj.readline() if ltt == 0 and li == 1: # Purely isotropic - energy = np.array([0., ev.info['energy_max']]) - mu = [Uniform(-1., 1.), Uniform(-1., 1.)] + energy = np.array([0.0, ev.info["energy_max"]]) + mu = [Uniform(-1.0, 1.0), Uniform(-1.0, 1.0)] elif ltt == 1 and li == 0: # Legendre polynomial coefficients @@ -273,8 +278,10 @@ def from_endf(cls, ev, mt): params, f = get_tab1_record(file_obj) energy[i] = params[1] if f.n_regions > 1: - raise NotImplementedError('Angular distribution with multiple ' - 'interpolation regions not supported.') + raise NotImplementedError( + "Angular distribution with multiple " + "interpolation regions not supported." + ) mu.append(Tabular(f.x, f.y, INTERPOLATION_SCHEME[f.interpolation[0]])) elif ltt == 3 and li == 0: @@ -298,8 +305,10 @@ def from_endf(cls, ev, mt): params, f = get_tab1_record(file_obj) energy_tabulated[i] = params[1] if f.n_regions > 1: - raise NotImplementedError('Angular distribution with multiple ' - 'interpolation regions not supported.') + raise NotImplementedError( + "Angular distribution with multiple " + "interpolation regions not supported." + ) mu.append(Tabular(f.x, f.y, INTERPOLATION_SCHEME[f.interpolation[0]])) energy = np.concatenate((energy_legendre, energy_tabulated)) diff --git a/openmc/data/angle_energy.py b/openmc/data/angle_energy.py index 71ca47587d7..de7303ac3b1 100644 --- a/openmc/data/angle_energy.py +++ b/openmc/data/angle_energy.py @@ -6,6 +6,7 @@ class AngleEnergy(EqualityMixin, ABC): """Distribution in angle and energy of a secondary particle.""" + @abstractmethod def to_hdf5(self, group): pass @@ -25,26 +26,26 @@ def from_hdf5(group): Angle-energy distribution """ - dist_type = group.attrs['type'].decode() - if dist_type == 'uncorrelated': + dist_type = group.attrs["type"].decode() + if dist_type == "uncorrelated": return openmc.data.UncorrelatedAngleEnergy.from_hdf5(group) - elif dist_type == 'correlated': + elif dist_type == "correlated": return openmc.data.CorrelatedAngleEnergy.from_hdf5(group) - elif dist_type == 'kalbach-mann': + elif dist_type == "kalbach-mann": return openmc.data.KalbachMann.from_hdf5(group) - elif dist_type == 'nbody': + elif dist_type == "nbody": return openmc.data.NBodyPhaseSpace.from_hdf5(group) - elif dist_type == 'coherent_elastic': + elif dist_type == "coherent_elastic": return openmc.data.CoherentElasticAE.from_hdf5(group) - elif dist_type == 'incoherent_elastic': + elif dist_type == "incoherent_elastic": return openmc.data.IncoherentElasticAE.from_hdf5(group) - elif dist_type == 'incoherent_elastic_discrete': + elif dist_type == "incoherent_elastic_discrete": return openmc.data.IncoherentElasticAEDiscrete.from_hdf5(group) - elif dist_type == 'incoherent_inelastic_discrete': + elif dist_type == "incoherent_inelastic_discrete": return openmc.data.IncoherentInelasticAEDiscrete.from_hdf5(group) - elif dist_type == 'incoherent_inelastic': + elif dist_type == "incoherent_inelastic": return openmc.data.IncoherentInelasticAE.from_hdf5(group) - elif dist_type == 'mixed_elastic': + elif dist_type == "mixed_elastic": return openmc.data.MixedElasticAE.from_hdf5(group) @staticmethod @@ -89,7 +90,8 @@ def from_ace(ace, location_dist, location_start, rx=None): elif law == 4: distribution = openmc.data.UncorrelatedAngleEnergy() distribution.energy = openmc.data.ContinuousTabular.from_ace( - ace, idx, location_dist) + ace, idx, location_dist + ) elif law == 5: distribution = openmc.data.UncorrelatedAngleEnergy() distribution.energy = openmc.data.GeneralEvaporation.from_ace(ace, idx) @@ -103,14 +105,13 @@ def from_ace(ace, location_dist, location_start, rx=None): distribution = openmc.data.UncorrelatedAngleEnergy() distribution.energy = openmc.data.WattEnergy.from_ace(ace, idx) elif law == 44: - distribution = openmc.data.KalbachMann.from_ace( - ace, idx, location_dist) + distribution = openmc.data.KalbachMann.from_ace(ace, idx, location_dist) elif law == 61: distribution = openmc.data.CorrelatedAngleEnergy.from_ace( - ace, idx, location_dist) + ace, idx, location_dist + ) elif law == 66: - distribution = openmc.data.NBodyPhaseSpace.from_ace( - ace, idx, rx.q_value) + distribution = openmc.data.NBodyPhaseSpace.from_ace(ace, idx, rx.q_value) else: raise ValueError(f"Unsupported ACE secondary energy distribution law {law}") diff --git a/openmc/data/correlated.py b/openmc/data/correlated.py index 2ff095a5c4e..1de0c2c119f 100644 --- a/openmc/data/correlated.py +++ b/openmc/data/correlated.py @@ -5,8 +5,7 @@ import numpy as np import openmc.checkvalue as cv -from openmc.stats import Tabular, Univariate, Discrete, Mixture, \ - Uniform, Legendre +from openmc.stats import Tabular, Univariate, Discrete, Mixture, Uniform, Legendre from .function import INTERPOLATION_SCHEME from .angle_energy import AngleEnergy from .data import EV_PER_MEV @@ -44,7 +43,7 @@ class CorrelatedAngleEnergy(AngleEnergy): """ - _name = 'correlated' + _name = "correlated" def __init__(self, breakpoints, interpolation, energy, energy_out, mu): super().__init__() @@ -60,8 +59,9 @@ def breakpoints(self): @breakpoints.setter def breakpoints(self, breakpoints): - cv.check_type('correlated angle-energy breakpoints', breakpoints, - Iterable, Integral) + cv.check_type( + "correlated angle-energy breakpoints", breakpoints, Iterable, Integral + ) self._breakpoints = breakpoints @property @@ -70,8 +70,9 @@ def interpolation(self): @interpolation.setter def interpolation(self, interpolation): - cv.check_type('correlated angle-energy interpolation', interpolation, - Iterable, Integral) + cv.check_type( + "correlated angle-energy interpolation", interpolation, Iterable, Integral + ) self._interpolation = interpolation @property @@ -80,8 +81,7 @@ def energy(self): @energy.setter def energy(self, energy): - cv.check_type('correlated angle-energy incoming energy', energy, - Iterable, Real) + cv.check_type("correlated angle-energy incoming energy", energy, Iterable, Real) self._energy = energy @property @@ -90,8 +90,9 @@ def energy_out(self): @energy_out.setter def energy_out(self, energy_out): - cv.check_type('correlated angle-energy outgoing energy', energy_out, - Iterable, Univariate) + cv.check_type( + "correlated angle-energy outgoing energy", energy_out, Iterable, Univariate + ) self._energy_out = energy_out @property @@ -100,8 +101,9 @@ def mu(self): @mu.setter def mu(self, mu): - cv.check_iterable_type('correlated angle-energy outgoing cosine', - mu, Univariate, 2, 2) + cv.check_iterable_type( + "correlated angle-energy outgoing cosine", mu, Univariate, 2, 2 + ) self._mu = mu def to_hdf5(self, group): @@ -113,11 +115,10 @@ def to_hdf5(self, group): HDF5 group to write to """ - group.attrs['type'] = np.bytes_(self._name) + group.attrs["type"] = np.bytes_(self._name) - dset = group.create_dataset('energy', data=self.energy) - dset.attrs['interpolation'] = np.vstack((self.breakpoints, - self.interpolation)) + dset = group.create_dataset("energy", data=self.energy) + dset.attrs["interpolation"] = np.vstack((self.breakpoints, self.interpolation)) # Determine total number of (E,p) pairs and create array n_tuple = sum(len(d) for d in self.energy_out) @@ -126,12 +127,19 @@ def to_hdf5(self, group): # Make sure all mu data is tabular mu_tabular = [] for i, mu_i in enumerate(self.mu): - mu_tabular.append([mu_ij if isinstance(mu_ij, (Tabular, Discrete)) else - mu_ij.to_tabular() for mu_ij in mu_i]) + mu_tabular.append( + [ + ( + mu_ij + if isinstance(mu_ij, (Tabular, Discrete)) + else mu_ij.to_tabular() + ) + for mu_ij in mu_i + ] + ) # Determine total number of (mu,p) points and create array - n_tuple = sum(sum(len(mu_ij.x) for mu_ij in mu_i) - for mu_i in mu_tabular) + n_tuple = sum(sum(len(mu_ij.x) for mu_ij in mu_i) for mu_i in mu_tabular) mu = np.empty((3, n_tuple)) # Create array for offsets @@ -149,54 +157,57 @@ def to_hdf5(self, group): if isinstance(d, Mixture): discrete, continuous = d.distribution n_discrete_lines[i] = m = len(discrete) - interpolation[i] = 1 if continuous.interpolation == 'histogram' else 2 - eout[0, offset_e:offset_e+m] = discrete.x - eout[1, offset_e:offset_e+m] = discrete.p - eout[2, offset_e:offset_e+m] = discrete.c - eout[0, offset_e+m:offset_e+n] = continuous.x - eout[1, offset_e+m:offset_e+n] = continuous.p - eout[2, offset_e+m:offset_e+n] = continuous.c + interpolation[i] = 1 if continuous.interpolation == "histogram" else 2 + eout[0, offset_e : offset_e + m] = discrete.x + eout[1, offset_e : offset_e + m] = discrete.p + eout[2, offset_e : offset_e + m] = discrete.c + eout[0, offset_e + m : offset_e + n] = continuous.x + eout[1, offset_e + m : offset_e + n] = continuous.p + eout[2, offset_e + m : offset_e + n] = continuous.c else: if isinstance(d, Tabular): n_discrete_lines[i] = 0 - interpolation[i] = 1 if d.interpolation == 'histogram' else 2 + interpolation[i] = 1 if d.interpolation == "histogram" else 2 elif isinstance(d, Discrete): n_discrete_lines[i] = n interpolation[i] = 1 else: raise ValueError( - 'Invalid univariate energy distribution as part of ' - 'correlated angle-energy: {}'.format(d)) - eout[0, offset_e:offset_e+n] = d.x - eout[1, offset_e:offset_e+n] = d.p - eout[2, offset_e:offset_e+n] = d.c + "Invalid univariate energy distribution as part of " + "correlated angle-energy: {}".format(d) + ) + eout[0, offset_e : offset_e + n] = d.x + eout[1, offset_e : offset_e + n] = d.p + eout[2, offset_e : offset_e + n] = d.c for j, mu_ij in enumerate(mu_tabular[i]): if isinstance(mu_ij, Discrete): - eout[3, offset_e+j] = 0 + eout[3, offset_e + j] = 0 else: - eout[3, offset_e+j] = 1 if mu_ij.interpolation == 'histogram' else 2 - eout[4, offset_e+j] = offset_mu + eout[3, offset_e + j] = ( + 1 if mu_ij.interpolation == "histogram" else 2 + ) + eout[4, offset_e + j] = offset_mu n_mu = len(mu_ij) - mu[0, offset_mu:offset_mu+n_mu] = mu_ij.x - mu[1, offset_mu:offset_mu+n_mu] = mu_ij.p - mu[2, offset_mu:offset_mu+n_mu] = mu_ij.c + mu[0, offset_mu : offset_mu + n_mu] = mu_ij.x + mu[1, offset_mu : offset_mu + n_mu] = mu_ij.p + mu[2, offset_mu : offset_mu + n_mu] = mu_ij.c offset_mu += n_mu offset_e += n # Create dataset for outgoing energy distributions - dset = group.create_dataset('energy_out', data=eout) + dset = group.create_dataset("energy_out", data=eout) # Write interpolation on outgoing energy as attribute - dset.attrs['offsets'] = offsets - dset.attrs['interpolation'] = interpolation - dset.attrs['n_discrete_lines'] = n_discrete_lines + dset.attrs["offsets"] = offsets + dset.attrs["interpolation"] = interpolation + dset.attrs["n_discrete_lines"] = n_discrete_lines # Create dataset for outgoing angle distributions - group.create_dataset('mu', data=mu) + group.create_dataset("mu", data=mu) @classmethod def from_hdf5(cls, group): @@ -213,18 +224,18 @@ def from_hdf5(cls, group): Correlated angle-energy distribution """ - interp_data = group['energy'].attrs['interpolation'] + interp_data = group["energy"].attrs["interpolation"] energy_breakpoints = interp_data[0, :] energy_interpolation = interp_data[1, :] - energy = group['energy'][()] + energy = group["energy"][()] - offsets = group['energy_out'].attrs['offsets'] - interpolation = group['energy_out'].attrs['interpolation'] - n_discrete_lines = group['energy_out'].attrs['n_discrete_lines'] - dset_eout = group['energy_out'][()] + offsets = group["energy_out"].attrs["offsets"] + interpolation = group["energy_out"].attrs["interpolation"] + n_discrete_lines = group["energy_out"].attrs["n_discrete_lines"] + dset_eout = group["energy_out"][()] energy_out = [] - dset_mu = group['mu'][()] + dset_mu = group["mu"][()] mu = [] n_energy = len(energy) @@ -233,27 +244,27 @@ def from_hdf5(cls, group): # discrete lines offset_e = offsets[i] if i < n_energy - 1: - n = offsets[i+1] - offset_e + n = offsets[i + 1] - offset_e else: n = dset_eout.shape[1] - offset_e m = n_discrete_lines[i] # Create discrete distribution if lines are present if m > 0: - x = dset_eout[0, offset_e:offset_e+m] - p = dset_eout[1, offset_e:offset_e+m] + x = dset_eout[0, offset_e : offset_e + m] + p = dset_eout[1, offset_e : offset_e + m] eout_discrete = Discrete(x, p) - eout_discrete.c = dset_eout[2, offset_e:offset_e+m] + eout_discrete.c = dset_eout[2, offset_e : offset_e + m] p_discrete = eout_discrete.c[-1] # Create continuous distribution if m < n: interp = INTERPOLATION_SCHEME[interpolation[i]] - x = dset_eout[0, offset_e+m:offset_e+n] - p = dset_eout[1, offset_e+m:offset_e+n] + x = dset_eout[0, offset_e + m : offset_e + n] + p = dset_eout[1, offset_e + m : offset_e + n] eout_continuous = Tabular(x, p, interp, ignore_negative=True) - eout_continuous.c = dset_eout[2, offset_e+m:offset_e+n] + eout_continuous.c = dset_eout[2, offset_e + m : offset_e + n] # If both continuous and discrete are present, create a mixture # distribution @@ -262,8 +273,9 @@ def from_hdf5(cls, group): elif m == n: eout_i = eout_discrete else: - eout_i = Mixture([p_discrete, 1. - p_discrete], - [eout_discrete, eout_continuous]) + eout_i = Mixture( + [p_discrete, 1.0 - p_discrete], [eout_discrete, eout_continuous] + ) # Read angular distributions mu_i = [] @@ -279,15 +291,16 @@ def from_hdf5(cls, group): n_mu = dset_mu.shape[1] - offset_mu # Get data - x = dset_mu[0, offset_mu:offset_mu+n_mu] - p = dset_mu[1, offset_mu:offset_mu+n_mu] - c = dset_mu[2, offset_mu:offset_mu+n_mu] + x = dset_mu[0, offset_mu : offset_mu + n_mu] + p = dset_mu[1, offset_mu : offset_mu + n_mu] + c = dset_mu[2, offset_mu : offset_mu + n_mu] if interp_code == 0: mu_ij = Discrete(x, p) else: - mu_ij = Tabular(x, p, INTERPOLATION_SCHEME[interp_code], - ignore_negative=True) + mu_ij = Tabular( + x, p, INTERPOLATION_SCHEME[interp_code], ignore_negative=True + ) mu_ij.c = c mu_i.append(mu_ij) @@ -296,8 +309,7 @@ def from_hdf5(cls, group): energy_out.append(eout_i) mu.append(mu_i) - return cls(energy_breakpoints, energy_interpolation, - energy, energy_out, mu) + return cls(energy_breakpoints, energy_interpolation, energy, energy_out, mu) @classmethod def from_ace(cls, ace, idx, ldis): @@ -322,24 +334,24 @@ def from_ace(cls, ace, idx, ldis): """ # Read number of interpolation regions and incoming energies n_regions = int(ace.xss[idx]) - n_energy_in = int(ace.xss[idx + 1 + 2*n_regions]) + n_energy_in = int(ace.xss[idx + 1 + 2 * n_regions]) # Get interpolation information idx += 1 if n_regions > 0: - breakpoints = ace.xss[idx:idx + n_regions].astype(int) - interpolation = ace.xss[idx + n_regions:idx + 2*n_regions].astype(int) + breakpoints = ace.xss[idx : idx + n_regions].astype(int) + interpolation = ace.xss[idx + n_regions : idx + 2 * n_regions].astype(int) else: breakpoints = np.array([n_energy_in]) interpolation = np.array([2]) # Incoming energies at which distributions exist - idx += 2*n_regions + 1 - energy = ace.xss[idx:idx + n_energy_in]*EV_PER_MEV + idx += 2 * n_regions + 1 + energy = ace.xss[idx : idx + n_energy_in] * EV_PER_MEV # Location of distributions idx += n_energy_in - loc_dist = ace.xss[idx:idx + n_energy_in].astype(int) + loc_dist = ace.xss[idx : idx + n_energy_in].astype(int) # Initialize list of distributions energy_out = [] @@ -353,37 +365,45 @@ def from_ace(cls, ace, idx, ldis): # lines are present, the value given is 10*n_discrete_lines + intt n_discrete_lines, intt = divmod(int(ace.xss[idx]), 10) if intt not in (1, 2): - warn("Interpolation scheme for continuous tabular distribution " - "is not histogram or linear-linear.") + warn( + "Interpolation scheme for continuous tabular distribution " + "is not histogram or linear-linear." + ) intt = 2 # Secondary energy distribution n_energy_out = int(ace.xss[idx + 1]) - data = ace.xss[idx + 2:idx + 2 + 4*n_energy_out].copy() + data = ace.xss[idx + 2 : idx + 2 + 4 * n_energy_out].copy() data.shape = (4, n_energy_out) - data[0,:] *= EV_PER_MEV + data[0, :] *= EV_PER_MEV # Create continuous distribution - eout_continuous = Tabular(data[0][n_discrete_lines:], - data[1][n_discrete_lines:]/EV_PER_MEV, - INTERPOLATION_SCHEME[intt], - ignore_negative=True) + eout_continuous = Tabular( + data[0][n_discrete_lines:], + data[1][n_discrete_lines:] / EV_PER_MEV, + INTERPOLATION_SCHEME[intt], + ignore_negative=True, + ) eout_continuous.c = data[2][n_discrete_lines:] if np.any(data[1][n_discrete_lines:] < 0.0): - warn("Correlated angle-energy distribution has negative " - "probabilities.") + warn( + "Correlated angle-energy distribution has negative " + "probabilities." + ) # If discrete lines are present, create a mixture distribution if n_discrete_lines > 0: - eout_discrete = Discrete(data[0][:n_discrete_lines], - data[1][:n_discrete_lines]) + eout_discrete = Discrete( + data[0][:n_discrete_lines], data[1][:n_discrete_lines] + ) eout_discrete.c = data[2][:n_discrete_lines] if n_discrete_lines == n_energy_out: eout_i = eout_discrete else: p_discrete = min(sum(eout_discrete.p), 1.0) - eout_i = Mixture([p_discrete, 1. - p_discrete], - [eout_discrete, eout_continuous]) + eout_i = Mixture( + [p_discrete, 1.0 - p_discrete], [eout_discrete, eout_continuous] + ) else: eout_i = eout_continuous @@ -399,14 +419,14 @@ def from_ace(cls, ace, idx, ldis): intt = int(ace.xss[idx]) n_cosine = int(ace.xss[idx + 1]) - data = ace.xss[idx + 2:idx + 2 + 3*n_cosine] + data = ace.xss[idx + 2 : idx + 2 + 3 * n_cosine] data.shape = (3, n_cosine) mu_ij = Tabular(data[0], data[1], INTERPOLATION_SCHEME[intt]) mu_ij.c = data[2] else: # Isotropic distribution - mu_ij = Uniform(-1., 1.) + mu_ij = Uniform(-1.0, 1.0) mu_i.append(mu_ij) @@ -449,17 +469,17 @@ def from_endf(cls, file_obj): values.shape = (n_energy_out, n_angle + 2) # Outgoing energy distribution at the i-th incoming energy - eout_i = values[:,0] - eout_p_i = values[:,1] - energy_out_i = Tabular(eout_i, eout_p_i, INTERPOLATION_SCHEME[lep], - ignore_negative=True) + eout_i = values[:, 0] + eout_p_i = values[:, 1] + energy_out_i = Tabular( + eout_i, eout_p_i, INTERPOLATION_SCHEME[lep], ignore_negative=True + ) energy_out.append(energy_out_i) # Legendre coefficients used for angular distributions mu_i = [] for j in range(n_energy_out): - mu_i.append(Legendre(values[j,1:])) + mu_i.append(Legendre(values[j, 1:])) mu.append(mu_i) - return cls(tab2.breakpoints, tab2.interpolation, energy, - energy_out, mu) + return cls(tab2.breakpoints, tab2.interpolation, energy, energy_out, mu) diff --git a/openmc/data/data.py b/openmc/data/data.py index 408adf2e429..6df73d4e374 100644 --- a/openmc/data/data.py +++ b/openmc/data/data.py @@ -13,256 +13,633 @@ # "best measurement" is used. # Note that the abundances are given as atomic fractions! NATURAL_ABUNDANCE = { - 'H1': 0.99984426, 'H2': 0.00015574, 'He3': 0.000002, - 'He4': 0.999998, 'Li6': 0.07589, 'Li7': 0.92411, - 'Be9': 1.0, 'B10': 0.1982, 'B11': 0.8018, - 'C12': 0.988922, 'C13': 0.011078, 'N14': 0.996337, - 'N15': 0.003663, 'O16': 0.9976206, 'O17': 0.000379, - 'O18': 0.0020004, 'F19': 1.0, 'Ne20': 0.9048, - 'Ne21': 0.0027, 'Ne22': 0.0925, 'Na23': 1.0, - 'Mg24': 0.78951, 'Mg25': 0.1002, 'Mg26': 0.11029, - 'Al27': 1.0, 'Si28': 0.9222968, 'Si29': 0.0468316, - 'Si30': 0.0308716, 'P31': 1.0, 'S32': 0.9504074, - 'S33': 0.0074869, 'S34': 0.0419599, 'S36': 0.0001458, - 'Cl35': 0.757647, 'Cl37': 0.242353, 'Ar36': 0.003336, - 'Ar38': 0.000629, 'Ar40': 0.996035, 'K39': 0.932581, - 'K40': 0.000117, 'K41': 0.067302, 'Ca40': 0.96941, - 'Ca42': 0.00647, 'Ca43': 0.00135, 'Ca44': 0.02086, - 'Ca46': 0.00004, 'Ca48': 0.00187, 'Sc45': 1.0, - 'Ti46': 0.0825, 'Ti47': 0.0744, 'Ti48': 0.7372, - 'Ti49': 0.0541, 'Ti50': 0.0518, 'V50': 0.0025, - 'V51': 0.9975, 'Cr50': 0.04345, 'Cr52': 0.83789, - 'Cr53': 0.09501, 'Cr54': 0.02365, 'Mn55': 1.0, - 'Fe54': 0.05845, 'Fe56': 0.91754, 'Fe57': 0.02119, - 'Fe58': 0.00282, 'Co59': 1.0, 'Ni58': 0.680769, - 'Ni60': 0.262231, 'Ni61': 0.011399, 'Ni62': 0.036345, - 'Ni64': 0.009256, 'Cu63': 0.6915, 'Cu65': 0.3085, - 'Zn64': 0.4917, 'Zn66': 0.2773, 'Zn67': 0.0404, - 'Zn68': 0.1845, 'Zn70': 0.0061, 'Ga69': 0.60108, - 'Ga71': 0.39892, 'Ge70': 0.2052, 'Ge72': 0.2745, - 'Ge73': 0.0776, 'Ge74': 0.3652, 'Ge76': 0.0775, - 'As75': 1.0, 'Se74': 0.0086, 'Se76': 0.0923, - 'Se77': 0.076, 'Se78': 0.2369, 'Se80': 0.498, - 'Se82': 0.0882, 'Br79': 0.50686, 'Br81': 0.49314, - 'Kr78': 0.00355, 'Kr80': 0.02286, 'Kr82': 0.11593, - 'Kr83': 0.115, 'Kr84': 0.56987, 'Kr86': 0.17279, - 'Rb85': 0.7217, 'Rb87': 0.2783, 'Sr84': 0.0056, - 'Sr86': 0.0986, 'Sr87': 0.07, 'Sr88': 0.8258, - 'Y89': 1.0, 'Zr90': 0.5145, 'Zr91': 0.1122, - 'Zr92': 0.1715, 'Zr94': 0.1738, 'Zr96': 0.028, - 'Nb93': 1.0, 'Mo92': 0.14649, 'Mo94': 0.09187, - 'Mo95': 0.15873, 'Mo96': 0.16673, 'Mo97': 0.09582, - 'Mo98': 0.24292, 'Mo100': 0.09744, 'Ru96': 0.0554, - 'Ru98': 0.0187, 'Ru99': 0.1276, 'Ru100': 0.126, - 'Ru101': 0.1706, 'Ru102': 0.3155, 'Ru104': 0.1862, - 'Rh103': 1.0, 'Pd102': 0.0102, 'Pd104': 0.1114, - 'Pd105': 0.2233, 'Pd106': 0.2733, 'Pd108': 0.2646, - 'Pd110': 0.1172, 'Ag107': 0.51839, 'Ag109': 0.48161, - 'Cd106': 0.01245, 'Cd108': 0.00888, 'Cd110': 0.1247, - 'Cd111': 0.12795, 'Cd112': 0.24109, 'Cd113': 0.12227, - 'Cd114': 0.28754, 'Cd116': 0.07512, 'In113': 0.04281, - 'In115': 0.95719, 'Sn112': 0.0097, 'Sn114': 0.0066, - 'Sn115': 0.0034, 'Sn116': 0.1454, 'Sn117': 0.0768, - 'Sn118': 0.2422, 'Sn119': 0.0859, 'Sn120': 0.3258, - 'Sn122': 0.0463, 'Sn124': 0.0579, 'Sb121': 0.5721, - 'Sb123': 0.4279, 'Te120': 0.0009, 'Te122': 0.0255, - 'Te123': 0.0089, 'Te124': 0.0474, 'Te125': 0.0707, - 'Te126': 0.1884, 'Te128': 0.3174, 'Te130': 0.3408, - 'I127': 1.0, 'Xe124': 0.00095, 'Xe126': 0.00089, - 'Xe128': 0.0191, 'Xe129': 0.26401, 'Xe130': 0.04071, - 'Xe131': 0.21232, 'Xe132': 0.26909, 'Xe134': 0.10436, - 'Xe136': 0.08857, 'Cs133': 1.0, 'Ba130': 0.0011, - 'Ba132': 0.001, 'Ba134': 0.0242, 'Ba135': 0.0659, - 'Ba136': 0.0785, 'Ba137': 0.1123, 'Ba138': 0.717, - 'La138': 0.0008881, 'La139': 0.9991119, 'Ce136': 0.00186, - 'Ce138': 0.00251, 'Ce140': 0.88449, 'Ce142': 0.11114, - 'Pr141': 1.0, 'Nd142': 0.27153, 'Nd143': 0.12173, - 'Nd144': 0.23798, 'Nd145': 0.08293, 'Nd146': 0.17189, - 'Nd148': 0.05756, 'Nd150': 0.05638, 'Sm144': 0.0308, - 'Sm147': 0.15, 'Sm148': 0.1125, 'Sm149': 0.1382, - 'Sm150': 0.0737, 'Sm152': 0.2674, 'Sm154': 0.2274, - 'Eu151': 0.4781, 'Eu153': 0.5219, 'Gd152': 0.002, - 'Gd154': 0.0218, 'Gd155': 0.148, 'Gd156': 0.2047, - 'Gd157': 0.1565, 'Gd158': 0.2484, 'Gd160': 0.2186, - 'Tb159': 1.0, 'Dy156': 0.00056, 'Dy158': 0.00095, - 'Dy160': 0.02329, 'Dy161': 0.18889, 'Dy162': 0.25475, - 'Dy163': 0.24896, 'Dy164': 0.2826, 'Ho165': 1.0, - 'Er162': 0.00139, 'Er164': 0.01601, 'Er166': 0.33503, - 'Er167': 0.22869, 'Er168': 0.26978, 'Er170': 0.1491, - 'Tm169': 1.0, 'Yb168': 0.00123, 'Yb170': 0.02982, - 'Yb171': 0.14086, 'Yb172': 0.21686, 'Yb173': 0.16103, - 'Yb174': 0.32025, 'Yb176': 0.12995, 'Lu175': 0.97401, - 'Lu176': 0.02599, 'Hf174': 0.0016, 'Hf176': 0.0526, - 'Hf177': 0.186, 'Hf178': 0.2728, 'Hf179': 0.1362, - 'Hf180': 0.3508, 'Ta180': 0.0001201, 'Ta181': 0.9998799, - 'W180': 0.0012, 'W182': 0.265, 'W183': 0.1431, - 'W184': 0.3064, 'W186': 0.2843, 'Re185': 0.374, - 'Re187': 0.626, 'Os184': 0.0002, 'Os186': 0.0159, - 'Os187': 0.0196, 'Os188': 0.1324, 'Os189': 0.1615, - 'Os190': 0.2626, 'Os192': 0.4078, 'Ir191': 0.373, - 'Ir193': 0.627, 'Pt190': 0.00012, 'Pt192': 0.00782, - 'Pt194': 0.32864, 'Pt195': 0.33775, 'Pt196': 0.25211, - 'Pt198': 0.07356, 'Au197': 1.0, 'Hg196': 0.0015, - 'Hg198': 0.1004, 'Hg199': 0.1694, 'Hg200': 0.2314, - 'Hg201': 0.1317, 'Hg202': 0.2974, 'Hg204': 0.0682, - 'Tl203': 0.29524, 'Tl205': 0.70476, 'Pb204': 0.014, - 'Pb206': 0.241, 'Pb207': 0.221, 'Pb208': 0.524, - 'Bi209': 1.0, 'Th230': 0.0002, 'Th232': 0.9998, - 'Pa231': 1.0, 'U234': 0.000054, 'U235': 0.007204, - 'U238': 0.992742 + "H1": 0.99984426, + "H2": 0.00015574, + "He3": 0.000002, + "He4": 0.999998, + "Li6": 0.07589, + "Li7": 0.92411, + "Be9": 1.0, + "B10": 0.1982, + "B11": 0.8018, + "C12": 0.988922, + "C13": 0.011078, + "N14": 0.996337, + "N15": 0.003663, + "O16": 0.9976206, + "O17": 0.000379, + "O18": 0.0020004, + "F19": 1.0, + "Ne20": 0.9048, + "Ne21": 0.0027, + "Ne22": 0.0925, + "Na23": 1.0, + "Mg24": 0.78951, + "Mg25": 0.1002, + "Mg26": 0.11029, + "Al27": 1.0, + "Si28": 0.9222968, + "Si29": 0.0468316, + "Si30": 0.0308716, + "P31": 1.0, + "S32": 0.9504074, + "S33": 0.0074869, + "S34": 0.0419599, + "S36": 0.0001458, + "Cl35": 0.757647, + "Cl37": 0.242353, + "Ar36": 0.003336, + "Ar38": 0.000629, + "Ar40": 0.996035, + "K39": 0.932581, + "K40": 0.000117, + "K41": 0.067302, + "Ca40": 0.96941, + "Ca42": 0.00647, + "Ca43": 0.00135, + "Ca44": 0.02086, + "Ca46": 0.00004, + "Ca48": 0.00187, + "Sc45": 1.0, + "Ti46": 0.0825, + "Ti47": 0.0744, + "Ti48": 0.7372, + "Ti49": 0.0541, + "Ti50": 0.0518, + "V50": 0.0025, + "V51": 0.9975, + "Cr50": 0.04345, + "Cr52": 0.83789, + "Cr53": 0.09501, + "Cr54": 0.02365, + "Mn55": 1.0, + "Fe54": 0.05845, + "Fe56": 0.91754, + "Fe57": 0.02119, + "Fe58": 0.00282, + "Co59": 1.0, + "Ni58": 0.680769, + "Ni60": 0.262231, + "Ni61": 0.011399, + "Ni62": 0.036345, + "Ni64": 0.009256, + "Cu63": 0.6915, + "Cu65": 0.3085, + "Zn64": 0.4917, + "Zn66": 0.2773, + "Zn67": 0.0404, + "Zn68": 0.1845, + "Zn70": 0.0061, + "Ga69": 0.60108, + "Ga71": 0.39892, + "Ge70": 0.2052, + "Ge72": 0.2745, + "Ge73": 0.0776, + "Ge74": 0.3652, + "Ge76": 0.0775, + "As75": 1.0, + "Se74": 0.0086, + "Se76": 0.0923, + "Se77": 0.076, + "Se78": 0.2369, + "Se80": 0.498, + "Se82": 0.0882, + "Br79": 0.50686, + "Br81": 0.49314, + "Kr78": 0.00355, + "Kr80": 0.02286, + "Kr82": 0.11593, + "Kr83": 0.115, + "Kr84": 0.56987, + "Kr86": 0.17279, + "Rb85": 0.7217, + "Rb87": 0.2783, + "Sr84": 0.0056, + "Sr86": 0.0986, + "Sr87": 0.07, + "Sr88": 0.8258, + "Y89": 1.0, + "Zr90": 0.5145, + "Zr91": 0.1122, + "Zr92": 0.1715, + "Zr94": 0.1738, + "Zr96": 0.028, + "Nb93": 1.0, + "Mo92": 0.14649, + "Mo94": 0.09187, + "Mo95": 0.15873, + "Mo96": 0.16673, + "Mo97": 0.09582, + "Mo98": 0.24292, + "Mo100": 0.09744, + "Ru96": 0.0554, + "Ru98": 0.0187, + "Ru99": 0.1276, + "Ru100": 0.126, + "Ru101": 0.1706, + "Ru102": 0.3155, + "Ru104": 0.1862, + "Rh103": 1.0, + "Pd102": 0.0102, + "Pd104": 0.1114, + "Pd105": 0.2233, + "Pd106": 0.2733, + "Pd108": 0.2646, + "Pd110": 0.1172, + "Ag107": 0.51839, + "Ag109": 0.48161, + "Cd106": 0.01245, + "Cd108": 0.00888, + "Cd110": 0.1247, + "Cd111": 0.12795, + "Cd112": 0.24109, + "Cd113": 0.12227, + "Cd114": 0.28754, + "Cd116": 0.07512, + "In113": 0.04281, + "In115": 0.95719, + "Sn112": 0.0097, + "Sn114": 0.0066, + "Sn115": 0.0034, + "Sn116": 0.1454, + "Sn117": 0.0768, + "Sn118": 0.2422, + "Sn119": 0.0859, + "Sn120": 0.3258, + "Sn122": 0.0463, + "Sn124": 0.0579, + "Sb121": 0.5721, + "Sb123": 0.4279, + "Te120": 0.0009, + "Te122": 0.0255, + "Te123": 0.0089, + "Te124": 0.0474, + "Te125": 0.0707, + "Te126": 0.1884, + "Te128": 0.3174, + "Te130": 0.3408, + "I127": 1.0, + "Xe124": 0.00095, + "Xe126": 0.00089, + "Xe128": 0.0191, + "Xe129": 0.26401, + "Xe130": 0.04071, + "Xe131": 0.21232, + "Xe132": 0.26909, + "Xe134": 0.10436, + "Xe136": 0.08857, + "Cs133": 1.0, + "Ba130": 0.0011, + "Ba132": 0.001, + "Ba134": 0.0242, + "Ba135": 0.0659, + "Ba136": 0.0785, + "Ba137": 0.1123, + "Ba138": 0.717, + "La138": 0.0008881, + "La139": 0.9991119, + "Ce136": 0.00186, + "Ce138": 0.00251, + "Ce140": 0.88449, + "Ce142": 0.11114, + "Pr141": 1.0, + "Nd142": 0.27153, + "Nd143": 0.12173, + "Nd144": 0.23798, + "Nd145": 0.08293, + "Nd146": 0.17189, + "Nd148": 0.05756, + "Nd150": 0.05638, + "Sm144": 0.0308, + "Sm147": 0.15, + "Sm148": 0.1125, + "Sm149": 0.1382, + "Sm150": 0.0737, + "Sm152": 0.2674, + "Sm154": 0.2274, + "Eu151": 0.4781, + "Eu153": 0.5219, + "Gd152": 0.002, + "Gd154": 0.0218, + "Gd155": 0.148, + "Gd156": 0.2047, + "Gd157": 0.1565, + "Gd158": 0.2484, + "Gd160": 0.2186, + "Tb159": 1.0, + "Dy156": 0.00056, + "Dy158": 0.00095, + "Dy160": 0.02329, + "Dy161": 0.18889, + "Dy162": 0.25475, + "Dy163": 0.24896, + "Dy164": 0.2826, + "Ho165": 1.0, + "Er162": 0.00139, + "Er164": 0.01601, + "Er166": 0.33503, + "Er167": 0.22869, + "Er168": 0.26978, + "Er170": 0.1491, + "Tm169": 1.0, + "Yb168": 0.00123, + "Yb170": 0.02982, + "Yb171": 0.14086, + "Yb172": 0.21686, + "Yb173": 0.16103, + "Yb174": 0.32025, + "Yb176": 0.12995, + "Lu175": 0.97401, + "Lu176": 0.02599, + "Hf174": 0.0016, + "Hf176": 0.0526, + "Hf177": 0.186, + "Hf178": 0.2728, + "Hf179": 0.1362, + "Hf180": 0.3508, + "Ta180": 0.0001201, + "Ta181": 0.9998799, + "W180": 0.0012, + "W182": 0.265, + "W183": 0.1431, + "W184": 0.3064, + "W186": 0.2843, + "Re185": 0.374, + "Re187": 0.626, + "Os184": 0.0002, + "Os186": 0.0159, + "Os187": 0.0196, + "Os188": 0.1324, + "Os189": 0.1615, + "Os190": 0.2626, + "Os192": 0.4078, + "Ir191": 0.373, + "Ir193": 0.627, + "Pt190": 0.00012, + "Pt192": 0.00782, + "Pt194": 0.32864, + "Pt195": 0.33775, + "Pt196": 0.25211, + "Pt198": 0.07356, + "Au197": 1.0, + "Hg196": 0.0015, + "Hg198": 0.1004, + "Hg199": 0.1694, + "Hg200": 0.2314, + "Hg201": 0.1317, + "Hg202": 0.2974, + "Hg204": 0.0682, + "Tl203": 0.29524, + "Tl205": 0.70476, + "Pb204": 0.014, + "Pb206": 0.241, + "Pb207": 0.221, + "Pb208": 0.524, + "Bi209": 1.0, + "Th230": 0.0002, + "Th232": 0.9998, + "Pa231": 1.0, + "U234": 0.000054, + "U235": 0.007204, + "U238": 0.992742, } # Dictionary to give element symbols from IUPAC names # (and some common mispellings) -ELEMENT_SYMBOL = {'neutron': 'n', 'hydrogen': 'H', 'helium': 'He', - 'lithium': 'Li', 'beryllium': 'Be', 'boron': 'B', - 'carbon': 'C', 'nitrogen': 'N', 'oxygen': 'O', 'fluorine': 'F', - 'neon': 'Ne', 'sodium': 'Na', 'magnesium': 'Mg', - 'aluminium': 'Al', 'aluminum': 'Al', 'silicon': 'Si', - 'phosphorus': 'P', 'sulfur': 'S', 'sulphur': 'S', - 'chlorine': 'Cl', 'argon': 'Ar', 'potassium': 'K', - 'calcium': 'Ca', 'scandium': 'Sc', 'titanium': 'Ti', - 'vanadium': 'V', 'chromium': 'Cr', 'manganese': 'Mn', - 'iron': 'Fe', 'cobalt': 'Co', 'nickel': 'Ni', 'copper': 'Cu', - 'zinc': 'Zn', 'gallium': 'Ga', 'germanium': 'Ge', - 'arsenic': 'As', 'selenium': 'Se', 'bromine': 'Br', - 'krypton': 'Kr', 'rubidium': 'Rb', 'strontium': 'Sr', - 'yttrium': 'Y', 'zirconium': 'Zr', 'niobium': 'Nb', - 'molybdenum': 'Mo', 'technetium': 'Tc', 'ruthenium': 'Ru', - 'rhodium': 'Rh', 'palladium': 'Pd', 'silver': 'Ag', - 'cadmium': 'Cd', 'indium': 'In', 'tin': 'Sn', 'antimony': 'Sb', - 'tellurium': 'Te', 'iodine': 'I', 'xenon': 'Xe', - 'caesium': 'Cs', 'cesium': 'Cs', 'barium': 'Ba', - 'lanthanum': 'La', 'cerium': 'Ce', 'praseodymium': 'Pr', - 'neodymium': 'Nd', 'promethium': 'Pm', 'samarium': 'Sm', - 'europium': 'Eu', 'gadolinium': 'Gd', 'terbium': 'Tb', - 'dysprosium': 'Dy', 'holmium': 'Ho', 'erbium': 'Er', - 'thulium': 'Tm', 'ytterbium': 'Yb', 'lutetium': 'Lu', - 'hafnium': 'Hf', 'tantalum': 'Ta', 'tungsten': 'W', - 'wolfram': 'W', 'rhenium': 'Re', 'osmium': 'Os', - 'iridium': 'Ir', 'platinum': 'Pt', 'gold': 'Au', - 'mercury': 'Hg', 'thallium': 'Tl', 'lead': 'Pb', - 'bismuth': 'Bi', 'polonium': 'Po', 'astatine': 'At', - 'radon': 'Rn', 'francium': 'Fr', 'radium': 'Ra', - 'actinium': 'Ac', 'thorium': 'Th', 'protactinium': 'Pa', - 'uranium': 'U', 'neptunium': 'Np', 'plutonium': 'Pu', - 'americium': 'Am', 'curium': 'Cm', 'berkelium': 'Bk', - 'californium': 'Cf', 'einsteinium': 'Es', 'fermium': 'Fm', - 'mendelevium': 'Md', 'nobelium': 'No', 'lawrencium': 'Lr', - 'rutherfordium': 'Rf', 'dubnium': 'Db', 'seaborgium': 'Sg', - 'bohrium': 'Bh', 'hassium': 'Hs', 'meitnerium': 'Mt', - 'darmstadtium': 'Ds', 'roentgenium': 'Rg', 'copernicium': 'Cn', - 'nihonium': 'Nh', 'flerovium': 'Fl', 'moscovium': 'Mc', - 'livermorium': 'Lv', 'tennessine': 'Ts', 'oganesson': 'Og'} - -ATOMIC_SYMBOL = {0: 'n', 1: 'H', 2: 'He', 3: 'Li', 4: 'Be', 5: 'B', 6: 'C', - 7: 'N', 8: 'O', 9: 'F', 10: 'Ne', 11: 'Na', 12: 'Mg', 13: 'Al', - 14: 'Si', 15: 'P', 16: 'S', 17: 'Cl', 18: 'Ar', 19: 'K', - 20: 'Ca', 21: 'Sc', 22: 'Ti', 23: 'V', 24: 'Cr', 25: 'Mn', - 26: 'Fe', 27: 'Co', 28: 'Ni', 29: 'Cu', 30: 'Zn', 31: 'Ga', - 32: 'Ge', 33: 'As', 34: 'Se', 35: 'Br', 36: 'Kr', 37: 'Rb', - 38: 'Sr', 39: 'Y', 40: 'Zr', 41: 'Nb', 42: 'Mo', 43: 'Tc', - 44: 'Ru', 45: 'Rh', 46: 'Pd', 47: 'Ag', 48: 'Cd', 49: 'In', - 50: 'Sn', 51: 'Sb', 52: 'Te', 53: 'I', 54: 'Xe', 55: 'Cs', - 56: 'Ba', 57: 'La', 58: 'Ce', 59: 'Pr', 60: 'Nd', 61: 'Pm', - 62: 'Sm', 63: 'Eu', 64: 'Gd', 65: 'Tb', 66: 'Dy', 67: 'Ho', - 68: 'Er', 69: 'Tm', 70: 'Yb', 71: 'Lu', 72: 'Hf', 73: 'Ta', - 74: 'W', 75: 'Re', 76: 'Os', 77: 'Ir', 78: 'Pt', 79: 'Au', - 80: 'Hg', 81: 'Tl', 82: 'Pb', 83: 'Bi', 84: 'Po', 85: 'At', - 86: 'Rn', 87: 'Fr', 88: 'Ra', 89: 'Ac', 90: 'Th', 91: 'Pa', - 92: 'U', 93: 'Np', 94: 'Pu', 95: 'Am', 96: 'Cm', 97: 'Bk', - 98: 'Cf', 99: 'Es', 100: 'Fm', 101: 'Md', 102: 'No', - 103: 'Lr', 104: 'Rf', 105: 'Db', 106: 'Sg', 107: 'Bh', - 108: 'Hs', 109: 'Mt', 110: 'Ds', 111: 'Rg', 112: 'Cn', - 113: 'Nh', 114: 'Fl', 115: 'Mc', 116: 'Lv', 117: 'Ts', - 118: 'Og'} +ELEMENT_SYMBOL = { + "neutron": "n", + "hydrogen": "H", + "helium": "He", + "lithium": "Li", + "beryllium": "Be", + "boron": "B", + "carbon": "C", + "nitrogen": "N", + "oxygen": "O", + "fluorine": "F", + "neon": "Ne", + "sodium": "Na", + "magnesium": "Mg", + "aluminium": "Al", + "aluminum": "Al", + "silicon": "Si", + "phosphorus": "P", + "sulfur": "S", + "sulphur": "S", + "chlorine": "Cl", + "argon": "Ar", + "potassium": "K", + "calcium": "Ca", + "scandium": "Sc", + "titanium": "Ti", + "vanadium": "V", + "chromium": "Cr", + "manganese": "Mn", + "iron": "Fe", + "cobalt": "Co", + "nickel": "Ni", + "copper": "Cu", + "zinc": "Zn", + "gallium": "Ga", + "germanium": "Ge", + "arsenic": "As", + "selenium": "Se", + "bromine": "Br", + "krypton": "Kr", + "rubidium": "Rb", + "strontium": "Sr", + "yttrium": "Y", + "zirconium": "Zr", + "niobium": "Nb", + "molybdenum": "Mo", + "technetium": "Tc", + "ruthenium": "Ru", + "rhodium": "Rh", + "palladium": "Pd", + "silver": "Ag", + "cadmium": "Cd", + "indium": "In", + "tin": "Sn", + "antimony": "Sb", + "tellurium": "Te", + "iodine": "I", + "xenon": "Xe", + "caesium": "Cs", + "cesium": "Cs", + "barium": "Ba", + "lanthanum": "La", + "cerium": "Ce", + "praseodymium": "Pr", + "neodymium": "Nd", + "promethium": "Pm", + "samarium": "Sm", + "europium": "Eu", + "gadolinium": "Gd", + "terbium": "Tb", + "dysprosium": "Dy", + "holmium": "Ho", + "erbium": "Er", + "thulium": "Tm", + "ytterbium": "Yb", + "lutetium": "Lu", + "hafnium": "Hf", + "tantalum": "Ta", + "tungsten": "W", + "wolfram": "W", + "rhenium": "Re", + "osmium": "Os", + "iridium": "Ir", + "platinum": "Pt", + "gold": "Au", + "mercury": "Hg", + "thallium": "Tl", + "lead": "Pb", + "bismuth": "Bi", + "polonium": "Po", + "astatine": "At", + "radon": "Rn", + "francium": "Fr", + "radium": "Ra", + "actinium": "Ac", + "thorium": "Th", + "protactinium": "Pa", + "uranium": "U", + "neptunium": "Np", + "plutonium": "Pu", + "americium": "Am", + "curium": "Cm", + "berkelium": "Bk", + "californium": "Cf", + "einsteinium": "Es", + "fermium": "Fm", + "mendelevium": "Md", + "nobelium": "No", + "lawrencium": "Lr", + "rutherfordium": "Rf", + "dubnium": "Db", + "seaborgium": "Sg", + "bohrium": "Bh", + "hassium": "Hs", + "meitnerium": "Mt", + "darmstadtium": "Ds", + "roentgenium": "Rg", + "copernicium": "Cn", + "nihonium": "Nh", + "flerovium": "Fl", + "moscovium": "Mc", + "livermorium": "Lv", + "tennessine": "Ts", + "oganesson": "Og", +} + +ATOMIC_SYMBOL = { + 0: "n", + 1: "H", + 2: "He", + 3: "Li", + 4: "Be", + 5: "B", + 6: "C", + 7: "N", + 8: "O", + 9: "F", + 10: "Ne", + 11: "Na", + 12: "Mg", + 13: "Al", + 14: "Si", + 15: "P", + 16: "S", + 17: "Cl", + 18: "Ar", + 19: "K", + 20: "Ca", + 21: "Sc", + 22: "Ti", + 23: "V", + 24: "Cr", + 25: "Mn", + 26: "Fe", + 27: "Co", + 28: "Ni", + 29: "Cu", + 30: "Zn", + 31: "Ga", + 32: "Ge", + 33: "As", + 34: "Se", + 35: "Br", + 36: "Kr", + 37: "Rb", + 38: "Sr", + 39: "Y", + 40: "Zr", + 41: "Nb", + 42: "Mo", + 43: "Tc", + 44: "Ru", + 45: "Rh", + 46: "Pd", + 47: "Ag", + 48: "Cd", + 49: "In", + 50: "Sn", + 51: "Sb", + 52: "Te", + 53: "I", + 54: "Xe", + 55: "Cs", + 56: "Ba", + 57: "La", + 58: "Ce", + 59: "Pr", + 60: "Nd", + 61: "Pm", + 62: "Sm", + 63: "Eu", + 64: "Gd", + 65: "Tb", + 66: "Dy", + 67: "Ho", + 68: "Er", + 69: "Tm", + 70: "Yb", + 71: "Lu", + 72: "Hf", + 73: "Ta", + 74: "W", + 75: "Re", + 76: "Os", + 77: "Ir", + 78: "Pt", + 79: "Au", + 80: "Hg", + 81: "Tl", + 82: "Pb", + 83: "Bi", + 84: "Po", + 85: "At", + 86: "Rn", + 87: "Fr", + 88: "Ra", + 89: "Ac", + 90: "Th", + 91: "Pa", + 92: "U", + 93: "Np", + 94: "Pu", + 95: "Am", + 96: "Cm", + 97: "Bk", + 98: "Cf", + 99: "Es", + 100: "Fm", + 101: "Md", + 102: "No", + 103: "Lr", + 104: "Rf", + 105: "Db", + 106: "Sg", + 107: "Bh", + 108: "Hs", + 109: "Mt", + 110: "Ds", + 111: "Rg", + 112: "Cn", + 113: "Nh", + 114: "Fl", + 115: "Mc", + 116: "Lv", + 117: "Ts", + 118: "Og", +} ATOMIC_NUMBER = {value: key for key, value in ATOMIC_SYMBOL.items()} DADZ = { - '(n,2nd)': (-3, -1), - '(n,2n)': (-1, 0), - '(n,3n)': (-2, 0), - '(n,na)': (-4, -2), - '(n,n3a)': (-12, -6), - '(n,2na)': (-5, -2), - '(n,3na)': (-6, -2), - '(n,np)': (-1, -1), - '(n,n2a)': (-8, -4), - '(n,2n2a)': (-9, -4), - '(n,nd)': (-2, -1), - '(n,nt)': (-3, -1), - '(n,n3He)': (-3, -2), - '(n,nd2a)': (-10, -5), - '(n,nt2a)': (-11, -5), - '(n,4n)': (-3, 0), - '(n,2np)': (-2, -1), - '(n,3np)': (-3, -1), - '(n,n2p)': (-2, -2), - '(n,npa)': (-5, -3), - '(n,gamma)': (1, 0), - '(n,p)': (0, -1), - '(n,d)': (-1, -1), - '(n,t)': (-2, -1), - '(n,3He)': (-2, -2), - '(n,a)': (-3, -2), - '(n,2a)': (-7, -4), - '(n,3a)': (-11, -6), - '(n,2p)': (-1, -2), - '(n,pa)': (-4, -3), - '(n,t2a)': (-10, -5), - '(n,d2a)': (-9, -5), - '(n,pd)': (-2, -2), - '(n,pt)': (-3, -2), - '(n,da)': (-5, -3), - '(n,5n)': (-4, 0), - '(n,6n)': (-5, 0), - '(n,2nt)': (-4, -1), - '(n,ta)': (-6, -3), - '(n,4np)': (-4, -1), - '(n,3nd)': (-4, -1), - '(n,nda)': (-6, -3), - '(n,2npa)': (-6, -3), - '(n,7n)': (-6, 0), - '(n,8n)': (-7, 0), - '(n,5np)': (-5, -1), - '(n,6np)': (-6, -1), - '(n,7np)': (-7, -1), - '(n,4na)': (-7, -2), - '(n,5na)': (-8, -2), - '(n,6na)': (-9, -2), - '(n,7na)': (-10, -2), - '(n,4nd)': (-5, -1), - '(n,5nd)': (-6, -1), - '(n,6nd)': (-7, -1), - '(n,3nt)': (-5, -1), - '(n,4nt)': (-6, -1), - '(n,5nt)': (-7, -1), - '(n,6nt)': (-8, -1), - '(n,2n3He)': (-4, -2), - '(n,3n3He)': (-5, -2), - '(n,4n3He)': (-6, -2), - '(n,3n2p)': (-4, -2), - '(n,3n2a)': (-10, -4), - '(n,3npa)': (-7, -3), - '(n,dt)': (-4, -2), - '(n,npd)': (-3, -2), - '(n,npt)': (-4, -2), - '(n,ndt)': (-5, -2), - '(n,np3He)': (-4, -3), - '(n,nd3He)': (-5, -3), - '(n,nt3He)': (-6, -3), - '(n,nta)': (-7, -3), - '(n,2n2p)': (-3, -2), - '(n,p3He)': (-4, -3), - '(n,d3He)': (-5, -3), - '(n,3Hea)': (-6, -4), - '(n,4n2p)': (-5, -2), - '(n,4n2a)': (-11, -4), - '(n,4npa)': (-8, -3), - '(n,3p)': (-2, -3), - '(n,n3p)': (-3, -3), - '(n,3n2pa)': (-8, -4), - '(n,5n2p)': (-6, -2), + "(n,2nd)": (-3, -1), + "(n,2n)": (-1, 0), + "(n,3n)": (-2, 0), + "(n,na)": (-4, -2), + "(n,n3a)": (-12, -6), + "(n,2na)": (-5, -2), + "(n,3na)": (-6, -2), + "(n,np)": (-1, -1), + "(n,n2a)": (-8, -4), + "(n,2n2a)": (-9, -4), + "(n,nd)": (-2, -1), + "(n,nt)": (-3, -1), + "(n,n3He)": (-3, -2), + "(n,nd2a)": (-10, -5), + "(n,nt2a)": (-11, -5), + "(n,4n)": (-3, 0), + "(n,2np)": (-2, -1), + "(n,3np)": (-3, -1), + "(n,n2p)": (-2, -2), + "(n,npa)": (-5, -3), + "(n,gamma)": (1, 0), + "(n,p)": (0, -1), + "(n,d)": (-1, -1), + "(n,t)": (-2, -1), + "(n,3He)": (-2, -2), + "(n,a)": (-3, -2), + "(n,2a)": (-7, -4), + "(n,3a)": (-11, -6), + "(n,2p)": (-1, -2), + "(n,pa)": (-4, -3), + "(n,t2a)": (-10, -5), + "(n,d2a)": (-9, -5), + "(n,pd)": (-2, -2), + "(n,pt)": (-3, -2), + "(n,da)": (-5, -3), + "(n,5n)": (-4, 0), + "(n,6n)": (-5, 0), + "(n,2nt)": (-4, -1), + "(n,ta)": (-6, -3), + "(n,4np)": (-4, -1), + "(n,3nd)": (-4, -1), + "(n,nda)": (-6, -3), + "(n,2npa)": (-6, -3), + "(n,7n)": (-6, 0), + "(n,8n)": (-7, 0), + "(n,5np)": (-5, -1), + "(n,6np)": (-6, -1), + "(n,7np)": (-7, -1), + "(n,4na)": (-7, -2), + "(n,5na)": (-8, -2), + "(n,6na)": (-9, -2), + "(n,7na)": (-10, -2), + "(n,4nd)": (-5, -1), + "(n,5nd)": (-6, -1), + "(n,6nd)": (-7, -1), + "(n,3nt)": (-5, -1), + "(n,4nt)": (-6, -1), + "(n,5nt)": (-7, -1), + "(n,6nt)": (-8, -1), + "(n,2n3He)": (-4, -2), + "(n,3n3He)": (-5, -2), + "(n,4n3He)": (-6, -2), + "(n,3n2p)": (-4, -2), + "(n,3n2a)": (-10, -4), + "(n,3npa)": (-7, -3), + "(n,dt)": (-4, -2), + "(n,npd)": (-3, -2), + "(n,npt)": (-4, -2), + "(n,ndt)": (-5, -2), + "(n,np3He)": (-4, -3), + "(n,nd3He)": (-5, -3), + "(n,nt3He)": (-6, -3), + "(n,nta)": (-7, -3), + "(n,2n2p)": (-3, -2), + "(n,p3He)": (-4, -3), + "(n,d3He)": (-5, -3), + "(n,3Hea)": (-6, -4), + "(n,4n2p)": (-5, -2), + "(n,4n2a)": (-11, -4), + "(n,4npa)": (-8, -3), + "(n,3p)": (-2, -3), + "(n,n3p)": (-3, -3), + "(n,3n2pa)": (-8, -4), + "(n,5n2p)": (-6, -2), } # Values here are from the Committee on Data for Science and Technology @@ -285,12 +662,13 @@ _ATOMIC_MASS: dict[str, float] = {} # Regex for GNDS nuclide names (used in zam function) -_GNDS_NAME_RE = re.compile(r'([A-Zn][a-z]*)(\d+)((?:_[em]\d+)?)') +_GNDS_NAME_RE = re.compile(r"([A-Zn][a-z]*)(\d+)((?:_[em]\d+)?)") # Used in half_life function as a cache _HALF_LIFE: dict[str, float] = {} _LOG_TWO = log(2.0) + def atomic_mass(isotope): """Return atomic mass of isotope in atomic mass units. @@ -311,28 +689,29 @@ def atomic_mass(isotope): if not _ATOMIC_MASS: # Load data from AME2020 file - mass_file = os.path.join(os.path.dirname(__file__), 'mass_1.mas20.txt') - with open(mass_file, 'r') as ame: + mass_file = os.path.join(os.path.dirname(__file__), "mass_1.mas20.txt") + with open(mass_file, "r") as ame: # Read lines in file starting at line 37 for line in itertools.islice(ame, 36, None): - name = f'{line[20:22].strip()}{int(line[16:19])}' - mass = float(line[106:109]) + 1e-6*float( - line[110:116] + '.' + line[117:123]) + name = f"{line[20:22].strip()}{int(line[16:19])}" + mass = float(line[106:109]) + 1e-6 * float( + line[110:116] + "." + line[117:123] + ) _ATOMIC_MASS[name.lower()] = mass # For isotopes found in some libraries that represent all natural # isotopes of their element (e.g. C0), calculate the atomic mass as # the sum of the atomic mass times the natural abundance of the isotopes # that make up the element. - for element in ['C', 'Zn', 'Pt', 'Os', 'Tl']: - isotope_zero = element.lower() + '0' - _ATOMIC_MASS[isotope_zero] = 0. + for element in ["C", "Zn", "Pt", "Os", "Tl"]: + isotope_zero = element.lower() + "0" + _ATOMIC_MASS[isotope_zero] = 0.0 for iso, abundance in isotopes(element): _ATOMIC_MASS[isotope_zero] += abundance * _ATOMIC_MASS[iso.lower()] # Get rid of metastable information - if '_' in isotope: - isotope = isotope[:isotope.find('_')] + if "_" in isotope: + isotope = isotope[: isotope.find("_")] return _ATOMIC_MASS[isotope.lower()] @@ -354,10 +733,10 @@ def atomic_weight(element): Atomic weight of element in [amu] """ - weight = 0. + weight = 0.0 for nuclide, abundance in isotopes(element): weight += atomic_mass(nuclide) * abundance - if weight > 0.: + if weight > 0.0: return weight else: raise ValueError(f"No naturally-occurring isotopes for element '{element}'.") @@ -385,7 +764,7 @@ def half_life(isotope): global _HALF_LIFE if not _HALF_LIFE: # Load ENDF/B-VIII.0 data from JSON file - half_life_path = Path(__file__).with_name('half_life.json') + half_life_path = Path(__file__).with_name("half_life.json") _HALF_LIFE = json.loads(half_life_path.read_text()) return _HALF_LIFE.get(isotope.lower()) @@ -458,49 +837,150 @@ def water_density(temperature, pressure=0.1013): elif temperature > 623.15: warn("Results are not valid for temperatures above 623.15 K.") elif temperature <= 0.0: - raise ValueError('Temperature must be positive.') + raise ValueError("Temperature must be positive.") # IAPWS region 4 parameters - n4 = [0.11670521452767e4, -0.72421316703206e6, -0.17073846940092e2, - 0.12020824702470e5, -0.32325550322333e7, 0.14915108613530e2, - -0.48232657361591e4, 0.40511340542057e6, -0.23855557567849, - 0.65017534844798e3] + n4 = [ + 0.11670521452767e4, + -0.72421316703206e6, + -0.17073846940092e2, + 0.12020824702470e5, + -0.32325550322333e7, + 0.14915108613530e2, + -0.48232657361591e4, + 0.40511340542057e6, + -0.23855557567849, + 0.65017534844798e3, + ] # Compute the saturation temperature at the given pressure. - beta = pressure**(0.25) + beta = pressure ** (0.25) E = beta**2 + n4[2] * beta + n4[5] F = n4[0] * beta**2 + n4[3] * beta + n4[6] G = n4[1] * beta**2 + n4[4] * beta + n4[7] D = 2.0 * G / (-F - sqrt(F**2 - 4 * E * G)) - T_sat = 0.5 * (n4[9] + D - - sqrt((n4[9] + D)**2 - 4.0 * (n4[8] + n4[9] * D))) + T_sat = 0.5 * (n4[9] + D - sqrt((n4[9] + D) ** 2 - 4.0 * (n4[8] + n4[9] * D))) # Make sure we aren't above saturation. (Relax this bound by .2 degrees # for deg C to K conversions.) if temperature > T_sat + 0.2: - warn("Results are not valid for temperatures above saturation " - "(above the boiling point).") + warn( + "Results are not valid for temperatures above saturation " + "(above the boiling point)." + ) # IAPWS region 1 parameters R_GAS_CONSTANT = 0.461526 # kJ / kg / K ref_p = 16.53 # MPa ref_T = 1386 # K - n1f = [0.14632971213167, -0.84548187169114, -0.37563603672040e1, - 0.33855169168385e1, -0.95791963387872, 0.15772038513228, - -0.16616417199501e-1, 0.81214629983568e-3, 0.28319080123804e-3, - -0.60706301565874e-3, -0.18990068218419e-1, -0.32529748770505e-1, - -0.21841717175414e-1, -0.52838357969930e-4, -0.47184321073267e-3, - -0.30001780793026e-3, 0.47661393906987e-4, -0.44141845330846e-5, - -0.72694996297594e-15, -0.31679644845054e-4, -0.28270797985312e-5, - -0.85205128120103e-9, -0.22425281908000e-5, -0.65171222895601e-6, - -0.14341729937924e-12, -0.40516996860117e-6, -0.12734301741641e-8, - -0.17424871230634e-9, -0.68762131295531e-18, 0.14478307828521e-19, - 0.26335781662795e-22, -0.11947622640071e-22, 0.18228094581404e-23, - -0.93537087292458e-25] - I1f = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, - 4, 4, 5, 8, 8, 21, 23, 29, 30, 31, 32] - J1f = [-2, -1, 0, 1, 2, 3, 4, 5, -9, -7, -1, 0, 1, 3, -3, 0, 1, 3, 17, -4, - 0, 6, -5, -2, 10, -8, -11, -6, -29, -31, -38, -39, -40, -41] + n1f = [ + 0.14632971213167, + -0.84548187169114, + -0.37563603672040e1, + 0.33855169168385e1, + -0.95791963387872, + 0.15772038513228, + -0.16616417199501e-1, + 0.81214629983568e-3, + 0.28319080123804e-3, + -0.60706301565874e-3, + -0.18990068218419e-1, + -0.32529748770505e-1, + -0.21841717175414e-1, + -0.52838357969930e-4, + -0.47184321073267e-3, + -0.30001780793026e-3, + 0.47661393906987e-4, + -0.44141845330846e-5, + -0.72694996297594e-15, + -0.31679644845054e-4, + -0.28270797985312e-5, + -0.85205128120103e-9, + -0.22425281908000e-5, + -0.65171222895601e-6, + -0.14341729937924e-12, + -0.40516996860117e-6, + -0.12734301741641e-8, + -0.17424871230634e-9, + -0.68762131295531e-18, + 0.14478307828521e-19, + 0.26335781662795e-22, + -0.11947622640071e-22, + 0.18228094581404e-23, + -0.93537087292458e-25, + ] + I1f = [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 4, + 4, + 4, + 5, + 8, + 8, + 21, + 23, + 29, + 30, + 31, + 32, + ] + J1f = [ + -2, + -1, + 0, + 1, + 2, + 3, + 4, + 5, + -9, + -7, + -1, + 0, + 1, + 3, + -3, + 0, + 1, + 3, + 17, + -4, + 0, + 6, + -5, + -2, + 10, + -8, + -11, + -6, + -29, + -31, + -38, + -39, + -40, + -41, + ] # Nondimensionalize the pressure and temperature. pi = pressure / ref_p @@ -510,7 +990,7 @@ def water_density(temperature, pressure=0.1013): # respect to pi. gamma1_pi = 0.0 for n, I, J in zip(n1f, I1f, J1f): - gamma1_pi -= n * I * (7.1 - pi)**(I - 1) * (tau - 1.222)**J + gamma1_pi -= n * I * (7.1 - pi) ** (I - 1) * (tau - 1.222) ** J # Compute the leading coefficient. This sets the units at # 1 [MPa] * [kg K / kJ] * [1 / K] @@ -545,9 +1025,8 @@ def gnds_name(Z, A, m=0): """ if m > 0: - return f'{ATOMIC_SYMBOL[Z]}{A}_m{m}' - return f'{ATOMIC_SYMBOL[Z]}{A}' - + return f"{ATOMIC_SYMBOL[Z]}{A}_m{m}" + return f"{ATOMIC_SYMBOL[Z]}{A}" def _get_element_symbol(element: str) -> str: @@ -586,7 +1065,7 @@ def isotopes(element: str) -> list[tuple[str, float]]: # Get the nuclides present in nature result = [] for kv in NATURAL_ABUNDANCE.items(): - if re.match(r'{}\d+'.format(element), kv[0]): + if re.match(r"{}\d+".format(element), kv[0]): result.append(kv) return result @@ -609,8 +1088,9 @@ def zam(name): try: symbol, A, state = _GNDS_NAME_RE.match(name).groups() except AttributeError: - raise ValueError(f"'{name}' does not appear to be a nuclide name in " - "GNDS format") + raise ValueError( + f"'{name}' does not appear to be a nuclide name in " "GNDS format" + ) if symbol not in ATOMIC_NUMBER: raise ValueError(f"'{symbol}' is not a recognized element symbol") diff --git a/openmc/data/decay.py b/openmc/data/decay.py index 66acb2212d8..30fc1b0b5c8 100644 --- a/openmc/data/decay.py +++ b/openmc/data/decay.py @@ -19,31 +19,31 @@ # Gives name and (change in A, change in Z) resulting from decay _DECAY_MODES = { - 0: ('gamma', (0, 0)), - 1: ('beta-', (0, 1)), - 2: ('ec/beta+', (0, -1)), - 3: ('IT', (0, 0)), - 4: ('alpha', (-4, -2)), - 5: ('n', (-1, 0)), - 6: ('sf', None), - 7: ('p', (-1, -1)), - 8: ('e-', (0, 0)), - 9: ('xray', (0, 0)), - 10: ('unknown', None) + 0: ("gamma", (0, 0)), + 1: ("beta-", (0, 1)), + 2: ("ec/beta+", (0, -1)), + 3: ("IT", (0, 0)), + 4: ("alpha", (-4, -2)), + 5: ("n", (-1, 0)), + 6: ("sf", None), + 7: ("p", (-1, -1)), + 8: ("e-", (0, 0)), + 9: ("xray", (0, 0)), + 10: ("unknown", None), } _RADIATION_TYPES = { - 0: 'gamma', - 1: 'beta-', - 2: 'ec/beta+', - 4: 'alpha', - 5: 'n', - 6: 'sf', - 7: 'p', - 8: 'e-', - 9: 'xray', - 10: 'anti-neutrino', - 11: 'neutrino' + 0: "gamma", + 1: "beta-", + 2: "ec/beta+", + 4: "alpha", + 5: "n", + 6: "sf", + 7: "p", + 8: "e-", + 9: "xray", + 10: "anti-neutrino", + 11: "neutrino", } @@ -64,10 +64,9 @@ def get_decay_modes(value): if int(value) == 10: # The logic below would treat 10.0 as [1, 0] rather than [10] as it # should, so we handle this case separately - return ['unknown'] + return ["unknown"] else: - return [_DECAY_MODES[int(x)][0] for x in - str(value).strip('0').replace('.', '')] + return [_DECAY_MODES[int(x)][0] for x in str(value).strip("0").replace(".", "")] class FissionProductYields(EqualityMixin): @@ -105,6 +104,7 @@ class FissionProductYields(EqualityMixin): at 0.0253 eV. """ + def __init__(self, ev_or_filename): # Define function that can be used to read both independent and # cumulative yields @@ -123,12 +123,12 @@ def get_yields(file_obj): # Get yields for i-th energy yields = {} for j in range(n_products): - Z, A = divmod(int(values[4*j]), 1000) - isomeric_state = int(values[4*j + 1]) + Z, A = divmod(int(values[4 * j]), 1000) + isomeric_state = int(values[4 * j + 1]) name = ATOMIC_SYMBOL[Z] + str(A) if isomeric_state > 0: - name += f'_m{isomeric_state}' - yield_j = ufloat(values[4*j + 2], values[4*j + 3]) + name += f"_m{isomeric_state}" + yield_j = ufloat(values[4 * j + 2], values[4 * j + 3]) yields[name] = yield_j data.append(yields) @@ -143,10 +143,10 @@ def get_yields(file_obj): # Assign basic nuclide properties self.nuclide = { - 'name': ev.gnds_name, - 'atomic_number': ev.target['atomic_number'], - 'mass_number': ev.target['mass_number'], - 'isomeric_state': ev.target['isomeric_state'] + "name": ev.gnds_name, + "atomic_number": ev.target["atomic_number"], + "mass_number": ev.target["mass_number"], + "isomeric_state": ev.target["isomeric_state"], } # Read independent yields (MF=8, MT=454) @@ -210,8 +210,7 @@ class DecayMode(EqualityMixin): """ - def __init__(self, parent, modes, daughter_state, energy, - branching_ratio): + def __init__(self, parent, modes, daughter_state, energy, branching_ratio): self._daughter_state = daughter_state self.parent = parent self.modes = modes @@ -219,9 +218,9 @@ def __init__(self, parent, modes, daughter_state, energy, self.branching_ratio = branching_ratio def __repr__(self): - return (' {}, {}>'.format( - ','.join(self.modes), self.parent, self.daughter, - self.branching_ratio)) + return " {}, {}>".format( + ",".join(self.modes), self.parent, self.daughter, self.branching_ratio + ) @property def branching_ratio(self): @@ -229,20 +228,25 @@ def branching_ratio(self): @branching_ratio.setter def branching_ratio(self, branching_ratio): - cv.check_type('branching ratio', branching_ratio, UFloat) - cv.check_greater_than('branching ratio', - branching_ratio.nominal_value, 0.0, True) + cv.check_type("branching ratio", branching_ratio, UFloat) + cv.check_greater_than( + "branching ratio", branching_ratio.nominal_value, 0.0, True + ) if branching_ratio.nominal_value == 0.0: - warn('Decay mode {} of parent {} has a zero branching ratio.' - .format(self.modes, self.parent)) - cv.check_greater_than('branching ratio uncertainty', - branching_ratio.std_dev, 0.0, True) + warn( + "Decay mode {} of parent {} has a zero branching ratio.".format( + self.modes, self.parent + ) + ) + cv.check_greater_than( + "branching ratio uncertainty", branching_ratio.std_dev, 0.0, True + ) self._branching_ratio = branching_ratio @property def daughter(self): # Determine atomic number and mass number of parent - symbol, A = re.match(r'([A-Zn][a-z]*)(\d+)', self.parent).groups() + symbol, A = re.match(r"([A-Zn][a-z]*)(\d+)", self.parent).groups() A = int(A) Z = ATOMIC_NUMBER[symbol] @@ -256,9 +260,9 @@ def daughter(self): Z += delta_Z if self._daughter_state > 0: - return f'{ATOMIC_SYMBOL[Z]}{A}_m{self._daughter_state}' + return f"{ATOMIC_SYMBOL[Z]}{A}_m{self._daughter_state}" else: - return f'{ATOMIC_SYMBOL[Z]}{A}' + return f"{ATOMIC_SYMBOL[Z]}{A}" @property def parent(self): @@ -266,7 +270,7 @@ def parent(self): @parent.setter def parent(self, parent): - cv.check_type('parent nuclide', parent, str) + cv.check_type("parent nuclide", parent, str) self._parent = parent @property @@ -275,10 +279,9 @@ def energy(self): @energy.setter def energy(self, energy): - cv.check_type('decay energy', energy, UFloat) - cv.check_greater_than('decay energy', energy.nominal_value, 0.0, True) - cv.check_greater_than('decay energy uncertainty', - energy.std_dev, 0.0, True) + cv.check_type("decay energy", energy, UFloat) + cv.check_greater_than("decay energy", energy.nominal_value, 0.0, True) + cv.check_greater_than("decay energy uncertainty", energy.std_dev, 0.0, True) self._energy = energy @property @@ -287,7 +290,7 @@ def modes(self): @modes.setter def modes(self, modes): - cv.check_type('decay modes', modes, Iterable, str) + cv.check_type("decay modes", modes, Iterable, str) self._modes = modes @@ -326,6 +329,7 @@ class Decay(EqualityMixin): .. versionadded:: 0.13.1 """ + def __init__(self, ev_or_filename): # Get evaluation if str is passed if isinstance(ev_or_filename, Evaluation): @@ -345,70 +349,81 @@ def __init__(self, ev_or_filename): items = get_head_record(file_obj) Z, A = divmod(items[0], 1000) metastable = items[3] - self.nuclide['atomic_number'] = Z - self.nuclide['mass_number'] = A - self.nuclide['isomeric_state'] = metastable + self.nuclide["atomic_number"] = Z + self.nuclide["mass_number"] = A + self.nuclide["isomeric_state"] = metastable if metastable > 0: - self.nuclide['name'] = f'{ATOMIC_SYMBOL[Z]}{A}_m{metastable}' + self.nuclide["name"] = f"{ATOMIC_SYMBOL[Z]}{A}_m{metastable}" else: - self.nuclide['name'] = f'{ATOMIC_SYMBOL[Z]}{A}' - self.nuclide['mass'] = items[1] # AWR - self.nuclide['excited_state'] = items[2] # State of the original nuclide - self.nuclide['stable'] = (items[4] == 1) # Nucleus stability flag + self.nuclide["name"] = f"{ATOMIC_SYMBOL[Z]}{A}" + self.nuclide["mass"] = items[1] # AWR + self.nuclide["excited_state"] = items[2] # State of the original nuclide + self.nuclide["stable"] = items[4] == 1 # Nucleus stability flag # Determine if radioactive/stable - if not self.nuclide['stable']: + if not self.nuclide["stable"]: NSP = items[5] # Number of radiation types # Half-life and decay energies items, values = get_list_record(file_obj) self.half_life = ufloat(items[0], items[1]) - NC = items[4]//2 + NC = items[4] // 2 pairs = list(zip(values[::2], values[1::2])) ex = self.average_energies - ex['light'] = ufloat(*pairs[0]) - ex['electromagnetic'] = ufloat(*pairs[1]) - ex['heavy'] = ufloat(*pairs[2]) + ex["light"] = ufloat(*pairs[0]) + ex["electromagnetic"] = ufloat(*pairs[1]) + ex["heavy"] = ufloat(*pairs[2]) if NC == 17: - ex['beta-'] = ufloat(*pairs[3]) - ex['beta+'] = ufloat(*pairs[4]) - ex['auger'] = ufloat(*pairs[5]) - ex['conversion'] = ufloat(*pairs[6]) - ex['gamma'] = ufloat(*pairs[7]) - ex['xray'] = ufloat(*pairs[8]) - ex['bremsstrahlung'] = ufloat(*pairs[9]) - ex['annihilation'] = ufloat(*pairs[10]) - ex['alpha'] = ufloat(*pairs[11]) - ex['recoil'] = ufloat(*pairs[12]) - ex['SF'] = ufloat(*pairs[13]) - ex['neutron'] = ufloat(*pairs[14]) - ex['proton'] = ufloat(*pairs[15]) - ex['neutrino'] = ufloat(*pairs[16]) + ex["beta-"] = ufloat(*pairs[3]) + ex["beta+"] = ufloat(*pairs[4]) + ex["auger"] = ufloat(*pairs[5]) + ex["conversion"] = ufloat(*pairs[6]) + ex["gamma"] = ufloat(*pairs[7]) + ex["xray"] = ufloat(*pairs[8]) + ex["bremsstrahlung"] = ufloat(*pairs[9]) + ex["annihilation"] = ufloat(*pairs[10]) + ex["alpha"] = ufloat(*pairs[11]) + ex["recoil"] = ufloat(*pairs[12]) + ex["SF"] = ufloat(*pairs[13]) + ex["neutron"] = ufloat(*pairs[14]) + ex["proton"] = ufloat(*pairs[15]) + ex["neutrino"] = ufloat(*pairs[16]) items, values = get_list_record(file_obj) spin = items[0] # ENDF-102 specifies that unknown spin should be reported as -77.777 if spin == -77.777: - self.nuclide['spin'] = None + self.nuclide["spin"] = None else: - self.nuclide['spin'] = spin - self.nuclide['parity'] = items[1] # Parity of the nuclide + self.nuclide["spin"] = spin + self.nuclide["parity"] = items[1] # Parity of the nuclide # Decay mode information n_modes = items[5] # Number of decay modes for i in range(n_modes): - decay_type = get_decay_modes(values[6*i]) - isomeric_state = int(values[6*i + 1]) - energy = ufloat(*values[6*i + 2:6*i + 4]) - branching_ratio = ufloat(*values[6*i + 4:6*(i + 1)]) - - mode = DecayMode(self.nuclide['name'], decay_type, isomeric_state, - energy, branching_ratio) + decay_type = get_decay_modes(values[6 * i]) + isomeric_state = int(values[6 * i + 1]) + energy = ufloat(*values[6 * i + 2 : 6 * i + 4]) + branching_ratio = ufloat(*values[6 * i + 4 : 6 * (i + 1)]) + + mode = DecayMode( + self.nuclide["name"], + decay_type, + isomeric_state, + energy, + branching_ratio, + ) self.modes.append(mode) - discrete_type = {0.0: None, 1.0: 'allowed', 2.0: 'first-forbidden', - 3.0: 'second-forbidden', 4.0: 'third-forbidden', - 5.0: 'fourth-forbidden', 6.0: 'fifth-forbidden'} + discrete_type = { + 0.0: None, + 1.0: "allowed", + 2.0: "first-forbidden", + 3.0: "second-forbidden", + 4.0: "third-forbidden", + 5.0: "fourth-forbidden", + 6.0: "fifth-forbidden", + } # Read spectra for i in range(NSP): @@ -416,75 +431,78 @@ def __init__(self, ev_or_filename): items, values = get_list_record(file_obj) # Decay radiation type - spectrum['type'] = _RADIATION_TYPES[items[1]] + spectrum["type"] = _RADIATION_TYPES[items[1]] # Continuous spectrum flag - spectrum['continuous_flag'] = {0: 'discrete', 1: 'continuous', - 2: 'both'}[items[2]] - spectrum['discrete_normalization'] = ufloat(*values[0:2]) - spectrum['energy_average'] = ufloat(*values[2:4]) - spectrum['continuous_normalization'] = ufloat(*values[4:6]) + spectrum["continuous_flag"] = { + 0: "discrete", + 1: "continuous", + 2: "both", + }[items[2]] + spectrum["discrete_normalization"] = ufloat(*values[0:2]) + spectrum["energy_average"] = ufloat(*values[2:4]) + spectrum["continuous_normalization"] = ufloat(*values[4:6]) NER = items[5] # Number of tabulated discrete energies - if not spectrum['continuous_flag'] == 'continuous': + if not spectrum["continuous_flag"] == "continuous": # Information about discrete spectrum - spectrum['discrete'] = [] + spectrum["discrete"] = [] for j in range(NER): items, values = get_list_record(file_obj) di = {} - di['energy'] = ufloat(*items[0:2]) - di['from_mode'] = get_decay_modes(values[0]) - di['type'] = discrete_type[values[1]] - di['intensity'] = ufloat(*values[2:4]) - if spectrum['type'] == 'ec/beta+': - di['positron_intensity'] = ufloat(*values[4:6]) - elif spectrum['type'] == 'gamma': + di["energy"] = ufloat(*items[0:2]) + di["from_mode"] = get_decay_modes(values[0]) + di["type"] = discrete_type[values[1]] + di["intensity"] = ufloat(*values[2:4]) + if spectrum["type"] == "ec/beta+": + di["positron_intensity"] = ufloat(*values[4:6]) + elif spectrum["type"] == "gamma": if len(values) >= 6: - di['internal_pair'] = ufloat(*values[4:6]) + di["internal_pair"] = ufloat(*values[4:6]) if len(values) >= 8: - di['total_internal_conversion'] = ufloat(*values[6:8]) + di["total_internal_conversion"] = ufloat(*values[6:8]) if len(values) == 12: - di['k_shell_conversion'] = ufloat(*values[8:10]) - di['l_shell_conversion'] = ufloat(*values[10:12]) - spectrum['discrete'].append(di) + di["k_shell_conversion"] = ufloat(*values[8:10]) + di["l_shell_conversion"] = ufloat(*values[10:12]) + spectrum["discrete"].append(di) - if not spectrum['continuous_flag'] == 'discrete': + if not spectrum["continuous_flag"] == "discrete": # Read continuous spectrum ci = {} - params, ci['probability'] = get_tab1_record(file_obj) - ci['from_mode'] = get_decay_modes(params[0]) + params, ci["probability"] = get_tab1_record(file_obj) + ci["from_mode"] = get_decay_modes(params[0]) # Read covariance (Ek, Fk) table LCOV = params[3] if LCOV != 0: items, values = get_list_record(file_obj) - ci['covariance_lb'] = items[3] - ci['covariance'] = zip(values[0::2], values[1::2]) + ci["covariance_lb"] = items[3] + ci["covariance"] = zip(values[0::2], values[1::2]) - spectrum['continuous'] = ci + spectrum["continuous"] = ci # Add spectrum to dictionary - self.spectra[spectrum['type']] = spectrum + self.spectra[spectrum["type"]] = spectrum else: items, values = get_list_record(file_obj) items, values = get_list_record(file_obj) - self.nuclide['spin'] = items[0] - self.nuclide['parity'] = items[1] - self.half_life = ufloat(float('inf'), float('inf')) + self.nuclide["spin"] = items[0] + self.nuclide["parity"] = items[1] + self.half_life = ufloat(float("inf"), float("inf")) @property def decay_constant(self): if self.half_life.n == 0.0: - name = self.nuclide['name'] + name = self.nuclide["name"] raise ValueError(f"{name} is listed as unstable but has a zero half-life.") - return log(2.)/self.half_life + return log(2.0) / self.half_life @property def decay_energy(self): energy = self.average_energies if energy: - return energy['light'] + energy['electromagnetic'] + energy['heavy'] + return energy["light"] + energy["electromagnetic"] + energy["heavy"] else: return ufloat(0, 0) @@ -515,52 +533,55 @@ def sources(self): return self._sources sources = {} - name = self.nuclide['name'] + name = self.nuclide["name"] decay_constant = self.decay_constant.n for particle, spectra in self.spectra.items(): # Set particle type based on 'particle' above particle_type = { - 'gamma': 'photon', - 'beta-': 'electron', - 'ec/beta+': 'positron', - 'alpha': 'alpha', - 'n': 'neutron', - 'sf': 'fragment', - 'p': 'proton', - 'e-': 'electron', - 'xray': 'photon', - 'anti-neutrino': 'anti-neutrino', - 'neutrino': 'neutrino', + "gamma": "photon", + "beta-": "electron", + "ec/beta+": "positron", + "alpha": "alpha", + "n": "neutron", + "sf": "fragment", + "p": "proton", + "e-": "electron", + "xray": "photon", + "anti-neutrino": "anti-neutrino", + "neutrino": "neutrino", }[particle] if particle_type not in sources: sources[particle_type] = [] # Create distribution for discrete - if spectra['continuous_flag'] in ('discrete', 'both'): + if spectra["continuous_flag"] in ("discrete", "both"): energies = [] intensities = [] - for discrete_data in spectra['discrete']: - energies.append(discrete_data['energy'].n) - intensities.append(discrete_data['intensity'].n) + for discrete_data in spectra["discrete"]: + energies.append(discrete_data["energy"].n) + intensities.append(discrete_data["intensity"].n) energies = np.array(energies) - intensity = spectra['discrete_normalization'].n + intensity = spectra["discrete_normalization"].n rates = decay_constant * intensity * np.array(intensities) dist_discrete = Discrete(energies, rates) sources[particle_type].append(dist_discrete) # Create distribution for continuous - if spectra['continuous_flag'] in ('continuous', 'both'): - f = spectra['continuous']['probability'] + if spectra["continuous_flag"] in ("continuous", "both"): + f = spectra["continuous"]["probability"] if len(f.interpolation) > 1: - raise NotImplementedError("Multiple interpolation regions: {name}, {particle}") + raise NotImplementedError( + "Multiple interpolation regions: {name}, {particle}" + ) interpolation = INTERPOLATION_SCHEME[f.interpolation[0]] - if interpolation not in ('histogram', 'linear-linear'): + if interpolation not in ("histogram", "linear-linear"): warn( f"Continuous spectra with {interpolation} interpolation " - f"({name}, {particle}) encountered.") + f"({name}, {particle}) encountered." + ) - intensity = spectra['continuous_normalization'].n + intensity = spectra["continuous_normalization"].n rates = decay_constant * intensity * f.y dist_continuous = Tabular(f.x, rates, interpolation) sources[particle_type].append(dist_continuous) @@ -569,7 +590,8 @@ def sources(self): merged_sources = {} for particle_type, dist_list in sources.items(): merged_sources[particle_type] = combine_distributions( - dist_list, [1.0]*len(dist_list)) + dist_list, [1.0] * len(dist_list) + ) self._sources = merged_sources return self._sources @@ -600,7 +622,7 @@ def decay_photon_energy(nuclide: str) -> Univariate | None: intensities, given as [Bq]. """ if not _DECAY_PHOTON_ENERGY: - chain_file = openmc.config.get('chain_file') + chain_file = openmc.config.get("chain_file") if chain_file is None: raise DataError( "A depletion chain file must be specified with " @@ -608,15 +630,18 @@ def decay_photon_energy(nuclide: str) -> Univariate | None: ) from openmc.deplete import Chain + chain = Chain.from_xml(chain_file) for nuc in chain.nuclides: - if 'photon' in nuc.sources: - _DECAY_PHOTON_ENERGY[nuc.name] = nuc.sources['photon'] + if "photon" in nuc.sources: + _DECAY_PHOTON_ENERGY[nuc.name] = nuc.sources["photon"] # If the chain file contained no sources at all, warn the user if not _DECAY_PHOTON_ENERGY: - warn(f"Chain file '{chain_file}' does not have any decay photon " - "sources listed.") + warn( + f"Chain file '{chain_file}' does not have any decay photon " + "sources listed." + ) return _DECAY_PHOTON_ENERGY.get(nuclide) @@ -645,7 +670,7 @@ def decay_energy(nuclide: str): 0.0 is returned. """ if not _DECAY_ENERGY: - chain_file = openmc.config.get('chain_file') + chain_file = openmc.config.get("chain_file") if chain_file is None: raise DataError( "A depletion chain file must be specified with " @@ -653,6 +678,7 @@ def decay_energy(nuclide: str): ) from openmc.deplete import Chain + chain = Chain.from_xml(chain_file) for nuc in chain.nuclides: if nuc.decay_energy: @@ -663,5 +689,3 @@ def decay_energy(nuclide: str): warn(f"Chain file '{chain_file}' does not have any decay energy.") return _DECAY_ENERGY.get(nuclide, 0.0) - - diff --git a/openmc/data/effective_dose/dose.py b/openmc/data/effective_dose/dose.py index c7f458d1c6c..8117bdd2f70 100644 --- a/openmc/data/effective_dose/dose.py +++ b/openmc/data/effective_dose/dose.py @@ -5,19 +5,19 @@ import openmc.checkvalue as cv _FILES = { - ('icrp74', 'neutron'): Path('icrp74') / 'neutrons.txt', - ('icrp74', 'photon'): Path('icrp74') / 'photons.txt', - ('icrp116', 'electron'): Path('icrp116') / 'electrons.txt', - ('icrp116', 'helium'): Path('icrp116') / 'helium_ions.txt', - ('icrp116', 'mu-'): Path('icrp116') / 'negative_muons.txt', - ('icrp116', 'pi-'): Path('icrp116') / 'negative_pions.txt', - ('icrp116', 'neutron'): Path('icrp116') / 'neutrons.txt', - ('icrp116', 'photon'): Path('icrp116') / 'photons.txt', - ('icrp116', 'photon kerma'): Path('icrp116') / 'photons_kerma.txt', - ('icrp116', 'mu+'): Path('icrp116') / 'positive_muons.txt', - ('icrp116', 'pi+'): Path('icrp116') / 'positive_pions.txt', - ('icrp116', 'positron'): Path('icrp116') / 'positrons.txt', - ('icrp116', 'proton'): Path('icrp116') / 'protons.txt', + ("icrp74", "neutron"): Path("icrp74") / "neutrons.txt", + ("icrp74", "photon"): Path("icrp74") / "photons.txt", + ("icrp116", "electron"): Path("icrp116") / "electrons.txt", + ("icrp116", "helium"): Path("icrp116") / "helium_ions.txt", + ("icrp116", "mu-"): Path("icrp116") / "negative_muons.txt", + ("icrp116", "pi-"): Path("icrp116") / "negative_pions.txt", + ("icrp116", "neutron"): Path("icrp116") / "neutrons.txt", + ("icrp116", "photon"): Path("icrp116") / "photons.txt", + ("icrp116", "photon kerma"): Path("icrp116") / "photons_kerma.txt", + ("icrp116", "mu+"): Path("icrp116") / "positive_muons.txt", + ("icrp116", "pi+"): Path("icrp116") / "positive_pions.txt", + ("icrp116", "positron"): Path("icrp116") / "positrons.txt", + ("icrp116", "proton"): Path("icrp116") / "protons.txt", } _DOSE_TABLES = {} @@ -35,12 +35,12 @@ def _load_dose_icrp(data_source: str, particle: str): """ path = Path(__file__).parent / _FILES[data_source, particle] - data = np.loadtxt(path, skiprows=3, encoding='utf-8') - data[:, 0] *= 1e6 # Change energies to eV + data = np.loadtxt(path, skiprows=3, encoding="utf-8") + data[:, 0] *= 1e6 # Change energies to eV _DOSE_TABLES[data_source, particle] = data -def dose_coefficients(particle, geometry='AP', data_source='icrp116'): +def dose_coefficients(particle, geometry="AP", data_source="icrp116"): """Return effective dose conversion coefficients. This function provides fluence (and air kerma) to effective or ambient dose @@ -78,8 +78,8 @@ def dose_coefficients(particle, geometry='AP', data_source='icrp116'): """ - cv.check_value('geometry', geometry, {'AP', 'PA', 'LLAT', 'RLAT', 'ROT', 'ISO'}) - cv.check_value('data_source', data_source, {'icrp74', 'icrp116'}) + cv.check_value("geometry", geometry, {"AP", "PA", "LLAT", "RLAT", "ROT", "ISO"}) + cv.check_value("data_source", data_source, {"icrp74", "icrp116"}) if (data_source, particle) not in _FILES: raise ValueError(f"{particle} has no dose data in data source {data_source}.") @@ -90,10 +90,10 @@ def dose_coefficients(particle, geometry='AP', data_source='icrp116'): data = _DOSE_TABLES[data_source, particle] # Determine index for selected geometry - if particle in ('neutron', 'photon', 'proton', 'photon kerma'): - columns = ('AP', 'PA', 'LLAT', 'RLAT', 'ROT', 'ISO') + if particle in ("neutron", "photon", "proton", "photon kerma"): + columns = ("AP", "PA", "LLAT", "RLAT", "ROT", "ISO") else: - columns = ('AP', 'PA', 'ISO') + columns = ("AP", "PA", "ISO") index = columns.index(geometry) # Pull out energy and dose from table diff --git a/openmc/data/effective_dose/icrp74/generate_photon_effective_dose.py b/openmc/data/effective_dose/icrp74/generate_photon_effective_dose.py index f8e970137e2..58f0a84c0f5 100644 --- a/openmc/data/effective_dose/icrp74/generate_photon_effective_dose.py +++ b/openmc/data/effective_dose/icrp74/generate_photon_effective_dose.py @@ -2,49 +2,256 @@ import numpy as np # Data from Table A.1 (air kerma per fluence) -energy_a1 = np.array([ - 0.01, 0.015, 0.02, 0.03, 0.04, 0.05, 0.06, 0.08, 0.1, 0.15, 0.2, - 0.3, 0.4, 0.5, 0.6, 0.8, 1.0, 1.5, 2.0, 3.0, 4.0, 5.0, 6.0, 8.0, 10.0 -]) -air_kerma = np.array([7.43, 3.12, 1.68, 0.721, 0.429, 0.323, 0.289, 0.307, 0.371, 0.599, 0.856, 1.38, - 1.89, 2.38, 2.84, 3.69, 4.47, 6.14, 7.55, 9.96, 12.1, 14.1, 16.1, 20.1, 24.0]) +energy_a1 = np.array( + [ + 0.01, + 0.015, + 0.02, + 0.03, + 0.04, + 0.05, + 0.06, + 0.08, + 0.1, + 0.15, + 0.2, + 0.3, + 0.4, + 0.5, + 0.6, + 0.8, + 1.0, + 1.5, + 2.0, + 3.0, + 4.0, + 5.0, + 6.0, + 8.0, + 10.0, + ] +) +air_kerma = np.array( + [ + 7.43, + 3.12, + 1.68, + 0.721, + 0.429, + 0.323, + 0.289, + 0.307, + 0.371, + 0.599, + 0.856, + 1.38, + 1.89, + 2.38, + 2.84, + 3.69, + 4.47, + 6.14, + 7.55, + 9.96, + 12.1, + 14.1, + 16.1, + 20.1, + 24.0, + ] +) # Data from Table A.17 (effective dose per air kerma) -energy_a17 = np.array([ - 0.01, 0.015, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.1, 0.15, 0.2, 0.3, - 0.4, 0.5, 0.6, 0.8, 1.0, 2.0, 4.0, 6.0, 8.0, 10.0 -]) +energy_a17 = np.array( + [ + 0.01, + 0.015, + 0.02, + 0.03, + 0.04, + 0.05, + 0.06, + 0.07, + 0.08, + 0.1, + 0.15, + 0.2, + 0.3, + 0.4, + 0.5, + 0.6, + 0.8, + 1.0, + 2.0, + 4.0, + 6.0, + 8.0, + 10.0, + ] +) dose_per_airkerma = { - 'AP': np.array([ - 0.00653, 0.0402, 0.122, 0.416, 0.788, 1.106, 1.308, 1.407, 1.433, 1.394, - 1.256, 1.173, 1.093, 1.056, 1.036, 1.024, 1.010, 1.003, 0.992, 0.993, - 0.993, 0.991, 0.990 - ]), - 'PA': np.array([ - 0.00248, 0.00586, 0.0181, 0.128, 0.370, 0.640, 0.846, 0.966, 1.019, - 1.030, 0.959, 0.915, 0.880, 0.871, 0.869, 0.870, 0.875, 0.880, 0.901, - 0.918, 0.924, 0.927, 0.929 - ]), - 'RLAT': np.array([ - 0.00172, 0.00549, 0.0151, 0.0782, 0.205, 0.345, 0.455, 0.522, 0.554, - 0.571, 0.551, 0.549, 0.557, 0.570, 0.585, 0.600, 0.628, 0.651, 0.728, - 0.796, 0.827, 0.846, 0.860 - ]), - 'LLAT': np.array([ - 0.00172, 0.00549, 0.0155, 0.0904, 0.241, 0.405, 0.528, 0.598, 0.628, - 0.641, 0.620, 0.615, 0.615, 0.623, 0.635, 0.648, 0.670, 0.691, 0.757, - 0.813, 0.836, 0.850, 0.859 - ]), - 'ROT': np.array([ - 0.00326, 0.0153, 0.0462, 0.191, 0.426, 0.661, 0.828, 0.924, 0.961, - 0.960, 0.892, 0.854, 0.824, 0.814, 0.812, 0.814, 0.821, 0.831, 0.871, - 0.909, 0.925, 0.934, 0.941 - ]), - 'ISO': np.array([ - 0.00271, 0.0123, 0.0362, 0.143, 0.326, 0.511, 0.642, 0.720, 0.749, - 0.748, 0.700, 0.679, 0.664, 0.667, 0.675, 0.684, 0.703, 0.719, 0.774, - 0.824, 0.846, 0.859, 0.868 - ]) + "AP": np.array( + [ + 0.00653, + 0.0402, + 0.122, + 0.416, + 0.788, + 1.106, + 1.308, + 1.407, + 1.433, + 1.394, + 1.256, + 1.173, + 1.093, + 1.056, + 1.036, + 1.024, + 1.010, + 1.003, + 0.992, + 0.993, + 0.993, + 0.991, + 0.990, + ] + ), + "PA": np.array( + [ + 0.00248, + 0.00586, + 0.0181, + 0.128, + 0.370, + 0.640, + 0.846, + 0.966, + 1.019, + 1.030, + 0.959, + 0.915, + 0.880, + 0.871, + 0.869, + 0.870, + 0.875, + 0.880, + 0.901, + 0.918, + 0.924, + 0.927, + 0.929, + ] + ), + "RLAT": np.array( + [ + 0.00172, + 0.00549, + 0.0151, + 0.0782, + 0.205, + 0.345, + 0.455, + 0.522, + 0.554, + 0.571, + 0.551, + 0.549, + 0.557, + 0.570, + 0.585, + 0.600, + 0.628, + 0.651, + 0.728, + 0.796, + 0.827, + 0.846, + 0.860, + ] + ), + "LLAT": np.array( + [ + 0.00172, + 0.00549, + 0.0155, + 0.0904, + 0.241, + 0.405, + 0.528, + 0.598, + 0.628, + 0.641, + 0.620, + 0.615, + 0.615, + 0.623, + 0.635, + 0.648, + 0.670, + 0.691, + 0.757, + 0.813, + 0.836, + 0.850, + 0.859, + ] + ), + "ROT": np.array( + [ + 0.00326, + 0.0153, + 0.0462, + 0.191, + 0.426, + 0.661, + 0.828, + 0.924, + 0.961, + 0.960, + 0.892, + 0.854, + 0.824, + 0.814, + 0.812, + 0.814, + 0.821, + 0.831, + 0.871, + 0.909, + 0.925, + 0.934, + 0.941, + ] + ), + "ISO": np.array( + [ + 0.00271, + 0.0123, + 0.0362, + 0.143, + 0.326, + 0.511, + 0.642, + 0.720, + 0.749, + 0.748, + 0.700, + 0.679, + 0.664, + 0.667, + 0.675, + 0.684, + 0.703, + 0.719, + 0.774, + 0.824, + 0.846, + 0.859, + 0.868, + ] + ), } # Interpolate air kerma onto energy grid for Table A.17 @@ -58,12 +265,14 @@ # Create table table = PrettyTable() -table.field_names = ['Energy (MeV)', 'AP', 'PA', 'LLAT', 'RLAT', 'ROT', 'ISO'] -table.float_format = '.7' +table.field_names = ["Energy (MeV)", "AP", "PA", "LLAT", "RLAT", "ROT", "ISO"] +table.float_format = ".7" for i, energy in enumerate(energy_a17): row = [energy] for geometry in table.field_names[1:]: row.append(dose_per_fluence[geometry][i]) table.add_row(row) -print('Photons: Effective dose per fluence, in units of pSv cm², for monoenergetic particles incident in various geometries.\n') +print( + "Photons: Effective dose per fluence, in units of pSv cm², for monoenergetic particles incident in various geometries.\n" +) print(table.get_string(border=False)) diff --git a/openmc/data/endf.py b/openmc/data/endf.py index 63cd092ebb1..d8a143b8330 100644 --- a/openmc/data/endf.py +++ b/openmc/data/endf.py @@ -6,6 +6,7 @@ http://www-nds.iaea.org/ndspub/documents/endf/endf102/endf102.pdf """ + import io from pathlib import PurePath import re @@ -17,51 +18,152 @@ from endf.records import float_endf -_LIBRARY = {0: 'ENDF/B', 1: 'ENDF/A', 2: 'JEFF', 3: 'EFF', - 4: 'ENDF/B High Energy', 5: 'CENDL', 6: 'JENDL', - 17: 'TENDL', 18: 'ROSFOND', 21: 'SG-21', 31: 'INDL/V', - 32: 'INDL/A', 33: 'FENDL', 34: 'IRDF', 35: 'BROND', - 36: 'INGDB-90', 37: 'FENDL/A', 41: 'BROND'} +_LIBRARY = { + 0: "ENDF/B", + 1: "ENDF/A", + 2: "JEFF", + 3: "EFF", + 4: "ENDF/B High Energy", + 5: "CENDL", + 6: "JENDL", + 17: "TENDL", + 18: "ROSFOND", + 21: "SG-21", + 31: "INDL/V", + 32: "INDL/A", + 33: "FENDL", + 34: "IRDF", + 35: "BROND", + 36: "INGDB-90", + 37: "FENDL/A", + 41: "BROND", +} _SUBLIBRARY = { - 0: 'Photo-nuclear data', - 1: 'Photo-induced fission product yields', - 3: 'Photo-atomic data', - 4: 'Radioactive decay data', - 5: 'Spontaneous fission product yields', - 6: 'Atomic relaxation data', - 10: 'Incident-neutron data', - 11: 'Neutron-induced fission product yields', - 12: 'Thermal neutron scattering data', - 19: 'Neutron standards', - 113: 'Electro-atomic data', - 10010: 'Incident-proton data', - 10011: 'Proton-induced fission product yields', - 10020: 'Incident-deuteron data', - 10030: 'Incident-triton data', - 20030: 'Incident-helion (3He) data', - 20040: 'Incident-alpha data' + 0: "Photo-nuclear data", + 1: "Photo-induced fission product yields", + 3: "Photo-atomic data", + 4: "Radioactive decay data", + 5: "Spontaneous fission product yields", + 6: "Atomic relaxation data", + 10: "Incident-neutron data", + 11: "Neutron-induced fission product yields", + 12: "Thermal neutron scattering data", + 19: "Neutron standards", + 113: "Electro-atomic data", + 10010: "Incident-proton data", + 10011: "Proton-induced fission product yields", + 10020: "Incident-deuteron data", + 10030: "Incident-triton data", + 20030: "Incident-helion (3He) data", + 20040: "Incident-alpha data", +} + +SUM_RULES = { + 1: [2, 3], + 3: [ + 4, + 5, + 11, + 16, + 17, + 22, + 23, + 24, + 25, + 27, + 28, + 29, + 30, + 32, + 33, + 34, + 35, + 36, + 37, + 41, + 42, + 44, + 45, + 152, + 153, + 154, + 156, + 157, + 158, + 159, + 160, + 161, + 162, + 163, + 164, + 165, + 166, + 167, + 168, + 169, + 170, + 171, + 172, + 173, + 174, + 175, + 176, + 177, + 178, + 179, + 180, + 181, + 183, + 184, + 185, + 186, + 187, + 188, + 189, + 190, + 194, + 195, + 196, + 198, + 199, + 200, + ], + 4: list(range(50, 92)), + 16: list(range(875, 892)), + 18: [19, 20, 21, 38], + 27: [18, 101], + 101: [ + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 155, + 182, + 191, + 192, + 193, + 197, + ], + 103: list(range(600, 650)), + 104: list(range(650, 700)), + 105: list(range(700, 750)), + 106: list(range(750, 800)), + 107: list(range(800, 850)), } -SUM_RULES = {1: [2, 3], - 3: [4, 5, 11, 16, 17, 22, 23, 24, 25, 27, 28, 29, 30, 32, 33, 34, 35, - 36, 37, 41, 42, 44, 45, 152, 153, 154, 156, 157, 158, 159, 160, - 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, - 173, 174, 175, 176, 177, 178, 179, 180, 181, 183, 184, 185, - 186, 187, 188, 189, 190, 194, 195, 196, 198, 199, 200], - 4: list(range(50, 92)), - 16: list(range(875, 892)), - 18: [19, 20, 21, 38], - 27: [18, 101], - 101: [102, 103, 104, 105, 106, 107, 108, 109, 111, 112, 113, 114, - 115, 116, 117, 155, 182, 191, 192, 193, 197], - 103: list(range(600, 650)), - 104: list(range(650, 700)), - 105: list(range(700, 750)), - 106: list(range(750, 800)), - 107: list(range(800, 850))} - -ENDF_FLOAT_RE = re.compile(r'([\s\-\+]?\d*\.\d+)([\+\-]) ?(\d+)') +ENDF_FLOAT_RE = re.compile(r"([\s\-\+]?\d*\.\d+)([\+\-]) ?(\d+)") def py_float_endf(s): @@ -84,7 +186,7 @@ def py_float_endf(s): The number """ - return float(ENDF_FLOAT_RE.sub(r'\1e\2\3', s)) + return float(ENDF_FLOAT_RE.sub(r"\1e\2\3", s)) def int_endf(s): @@ -201,11 +303,11 @@ def get_list_record(file_obj): # read items b = [] - for i in range((NPL - 1)//6 + 1): + for i in range((NPL - 1) // 6 + 1): line = file_obj.readline() - n = min(6, NPL - 6*i) + n = min(6, NPL - 6 * i) for j in range(n): - b.append(float_endf(line[11*j:11*(j + 1)])) + b.append(float_endf(line[11 * j : 11 * (j + 1)])) return (items, b) @@ -240,7 +342,7 @@ def get_tab1_record(file_obj): breakpoints = np.zeros(n_regions, dtype=int) interpolation = np.zeros(n_regions, dtype=int) m = 0 - for i in range((n_regions - 1)//3 + 1): + for i in range((n_regions - 1) // 3 + 1): line = file_obj.readline() to_read = min(3, n_regions - m) for j in range(to_read): @@ -253,7 +355,7 @@ def get_tab1_record(file_obj): x = np.zeros(n_pairs) y = np.zeros(n_pairs) m = 0 - for i in range((n_pairs - 1)//3 + 1): + for i in range((n_pairs - 1) // 3 + 1): line = file_obj.readline() to_read = min(3, n_pairs - m) for j in range(to_read): @@ -274,7 +376,7 @@ def get_tab2_record(file_obj): breakpoints = np.zeros(n_regions, dtype=int) interpolation = np.zeros(n_regions, dtype=int) m = 0 - for i in range((n_regions - 1)//3 + 1): + for i in range((n_regions - 1) // 3 + 1): line = file_obj.readline() to_read = min(3, n_regions - m) for j in range(to_read): @@ -304,7 +406,7 @@ def get_intg_record(file_obj): # determine how many items are in list and NDIGIT items = get_cont_record(file_obj) ndigit = items[2] - npar = items[3] # Number of parameters + npar = items[3] # Number of parameters nlines = items[4] # Lines to read NROW_RULES = {2: 18, 3: 12, 4: 11, 5: 9, 6: 8} nrow = NROW_RULES[ndigit] @@ -317,13 +419,15 @@ def get_intg_record(file_obj): jj = int_endf(line[5:10]) - 1 factor = 10**ndigit for j in range(nrow): - if jj+j >= ii: + if jj + j >= ii: break - element = int_endf(line[11+(ndigit+1)*j:11+(ndigit+1)*(j+1)]) + element = int_endf( + line[11 + (ndigit + 1) * j : 11 + (ndigit + 1) * (j + 1)] + ) if element > 0: - corr[ii, jj] = (element+0.5)/factor + corr[ii, jj] = (element + 0.5) / factor elif element < 0: - corr[ii, jj] = (element-0.5)/factor + corr[ii, jj] = (element - 0.5) / factor # Symmetrize the correlation matrix corr = corr + corr.T - np.diag(corr.diagonal()) @@ -345,11 +449,11 @@ def get_evaluations(filename): """ evaluations = [] - with open(str(filename), 'r') as fh: + with open(str(filename), "r") as fh: while True: pos = fh.tell() line = fh.readline() - if line[66:70] == ' -1': + if line[66:70] == " -1": break fh.seek(pos) evaluations.append(Evaluation(fh)) @@ -380,9 +484,10 @@ class Evaluation: indicator (MOD). """ + def __init__(self, filename_or_obj): if isinstance(filename_or_obj, (str, PurePath)): - fh = open(str(filename_or_obj), 'r') + fh = open(str(filename_or_obj), "r") need_to_close = True else: fh = filename_or_obj @@ -425,10 +530,10 @@ def __init__(self, filename_or_obj): fh.readline() break - section_data = '' + section_data = "" while True: line = fh.readline() - if line[72:75] == ' 0': + if line[72:75] == " 0": break else: section_data += line @@ -440,7 +545,7 @@ def __init__(self, filename_or_obj): self._read_header() def __repr__(self): - name = self.target['zsymam'].replace(' ', '') + name = self.target["zsymam"].replace(" ", "") return f"<{self.info['sublibrary']} for {name} {self.info['library']}>" def _read_header(self): @@ -449,61 +554,61 @@ def _read_header(self): # Information about target/projectile items = get_head_record(file_obj) Z, A = divmod(items[0], 1000) - self.target['atomic_number'] = Z - self.target['mass_number'] = A - self.target['mass'] = items[1] + self.target["atomic_number"] = Z + self.target["mass_number"] = A + self.target["mass"] = items[1] self._LRP = items[2] - self.target['fissionable'] = (items[3] == 1) + self.target["fissionable"] = items[3] == 1 try: library = _LIBRARY[items[4]] except KeyError: - library = 'Unknown' - self.info['modification'] = items[5] + library = "Unknown" + self.info["modification"] = items[5] # Control record 1 items = get_cont_record(file_obj) - self.target['excitation_energy'] = items[0] - self.target['stable'] = (int(items[1]) == 0) - self.target['state'] = items[2] - self.target['isomeric_state'] = m = items[3] - self.info['format'] = items[5] - assert self.info['format'] == 6 + self.target["excitation_energy"] = items[0] + self.target["stable"] = int(items[1]) == 0 + self.target["state"] = items[2] + self.target["isomeric_state"] = m = items[3] + self.info["format"] = items[5] + assert self.info["format"] == 6 # Set correct excited state for Am242_m1, which is wrong in ENDF/B-VII.1 if Z == 95 and A == 242 and m == 1: - self.target['state'] = 2 + self.target["state"] = 2 # Control record 2 items = get_cont_record(file_obj) - self.projectile['mass'] = items[0] - self.info['energy_max'] = items[1] + self.projectile["mass"] = items[0] + self.info["energy_max"] = items[1] library_release = items[2] - self.info['sublibrary'] = _SUBLIBRARY[items[4]] + self.info["sublibrary"] = _SUBLIBRARY[items[4]] library_version = items[5] - self.info['library'] = (library, library_version, library_release) + self.info["library"] = (library, library_version, library_release) # Control record 3 items = get_cont_record(file_obj) - self.target['temperature'] = items[0] - self.info['derived'] = (items[2] > 0) + self.target["temperature"] = items[0] + self.info["derived"] = items[2] > 0 NWD = items[4] NXC = items[5] # Text records text = [get_text_record(file_obj) for i in range(NWD)] if len(text) >= 5: - self.target['zsymam'] = text[0][0:11] - self.info['laboratory'] = text[0][11:22] - self.info['date'] = text[0][22:32] - self.info['author'] = text[0][32:66] - self.info['reference'] = text[1][1:22] - self.info['date_distribution'] = text[1][22:32] - self.info['date_release'] = text[1][33:43] - self.info['date_entry'] = text[1][55:63] - self.info['identifier'] = text[2:5] - self.info['description'] = text[5:] + self.target["zsymam"] = text[0][0:11] + self.info["laboratory"] = text[0][11:22] + self.info["date"] = text[0][22:32] + self.info["author"] = text[0][32:66] + self.info["reference"] = text[1][1:22] + self.info["date_distribution"] = text[1][22:32] + self.info["date_release"] = text[1][33:43] + self.info["date_entry"] = text[1][55:63] + self.info["identifier"] = text[2:5] + self.info["description"] = text[5:] else: - self.target['zsymam'] = 'Unknown' + self.target["zsymam"] = "Unknown" # File numbers, reaction designations, and number of records for i in range(NXC): @@ -512,9 +617,11 @@ def _read_header(self): @property def gnds_name(self): - return gnds_name(self.target['atomic_number'], - self.target['mass_number'], - self.target['isomeric_state']) + return gnds_name( + self.target["atomic_number"], + self.target["mass_number"], + self.target["isomeric_state"], + ) class Tabulated2D: @@ -534,6 +641,7 @@ class Tabulated2D: ln(x). """ + def __init__(self, breakpoints, interpolation): self.breakpoints = breakpoints self.interpolation = interpolation diff --git a/openmc/data/energy_distribution.py b/openmc/data/energy_distribution.py index 069ab1b9beb..a8525153e13 100644 --- a/openmc/data/energy_distribution.py +++ b/openmc/data/energy_distribution.py @@ -15,6 +15,7 @@ class EnergyDistribution(EqualityMixin, ABC): """Abstract superclass for all energy distributions.""" + def __init__(self): pass @@ -37,20 +38,20 @@ def from_hdf5(group): Energy distribution """ - energy_type = group.attrs['type'].decode() - if energy_type == 'maxwell': + energy_type = group.attrs["type"].decode() + if energy_type == "maxwell": return MaxwellEnergy.from_hdf5(group) - elif energy_type == 'evaporation': + elif energy_type == "evaporation": return Evaporation.from_hdf5(group) - elif energy_type == 'watt': + elif energy_type == "watt": return WattEnergy.from_hdf5(group) - elif energy_type == 'madland-nix': + elif energy_type == "madland-nix": return MadlandNix.from_hdf5(group) - elif energy_type == 'discrete_photon': + elif energy_type == "discrete_photon": return DiscretePhoton.from_hdf5(group) - elif energy_type == 'level': + elif energy_type == "level": return LevelInelastic.from_hdf5(group) - elif energy_type == 'continuous': + elif energy_type == "continuous": return ContinuousTabular.from_hdf5(group) else: raise ValueError(f"Unknown energy distribution type: {energy_type}") @@ -254,7 +255,7 @@ def theta(self): @theta.setter def theta(self, theta): - cv.check_type('Maxwell theta', theta, Tabulated1D) + cv.check_type("Maxwell theta", theta, Tabulated1D) self._theta = theta @property @@ -263,7 +264,7 @@ def u(self): @u.setter def u(self, u): - cv.check_type('Maxwell restriction energy', u, Real) + cv.check_type("Maxwell restriction energy", u, Real) self._u = u def to_hdf5(self, group): @@ -276,9 +277,9 @@ def to_hdf5(self, group): """ - group.attrs['type'] = np.bytes_('maxwell') - group.attrs['u'] = self.u - self.theta.to_hdf5(group, 'theta') + group.attrs["type"] = np.bytes_("maxwell") + group.attrs["u"] = self.u + self.theta.to_hdf5(group, "theta") @classmethod def from_hdf5(cls, group): @@ -295,8 +296,8 @@ def from_hdf5(cls, group): Maxwell distribution """ - theta = Tabulated1D.from_hdf5(group['theta']) - u = group.attrs['u'] + theta = Tabulated1D.from_hdf5(group["theta"]) + u = group.attrs["u"] return cls(theta, u) @classmethod @@ -322,8 +323,8 @@ def from_ace(cls, ace, idx=0): # Restriction energy nr = int(ace.xss[idx]) - ne = int(ace.xss[idx + 1 + 2*nr]) - u = ace.xss[idx + 2 + 2*nr + 2*ne]*EV_PER_MEV + ne = int(ace.xss[idx + 1 + 2 * nr]) + u = ace.xss[idx + 2 + 2 * nr + 2 * ne] * EV_PER_MEV return cls(theta, u) @@ -387,7 +388,7 @@ def theta(self): @theta.setter def theta(self, theta): - cv.check_type('Evaporation theta', theta, Tabulated1D) + cv.check_type("Evaporation theta", theta, Tabulated1D) self._theta = theta @property @@ -396,7 +397,7 @@ def u(self): @u.setter def u(self, u): - cv.check_type('Evaporation restriction energy', u, Real) + cv.check_type("Evaporation restriction energy", u, Real) self._u = u def to_hdf5(self, group): @@ -409,9 +410,9 @@ def to_hdf5(self, group): """ - group.attrs['type'] = np.bytes_('evaporation') - group.attrs['u'] = self.u - self.theta.to_hdf5(group, 'theta') + group.attrs["type"] = np.bytes_("evaporation") + group.attrs["u"] = self.u + self.theta.to_hdf5(group, "theta") @classmethod def from_hdf5(cls, group): @@ -428,8 +429,8 @@ def from_hdf5(cls, group): Evaporation spectrum """ - theta = Tabulated1D.from_hdf5(group['theta']) - u = group.attrs['u'] + theta = Tabulated1D.from_hdf5(group["theta"]) + u = group.attrs["u"] return cls(theta, u) @classmethod @@ -455,8 +456,8 @@ def from_ace(cls, ace, idx=0): # Restriction energy nr = int(ace.xss[idx]) - ne = int(ace.xss[idx + 1 + 2*nr]) - u = ace.xss[idx + 2 + 2*nr + 2*ne]*EV_PER_MEV + ne = int(ace.xss[idx + 1 + 2 * nr]) + u = ace.xss[idx + 2 + 2 * nr + 2 * ne] * EV_PER_MEV return cls(theta, u) @@ -524,7 +525,7 @@ def a(self): @a.setter def a(self, a): - cv.check_type('Watt a', a, Tabulated1D) + cv.check_type("Watt a", a, Tabulated1D) self._a = a @property @@ -533,7 +534,7 @@ def b(self): @b.setter def b(self, b): - cv.check_type('Watt b', b, Tabulated1D) + cv.check_type("Watt b", b, Tabulated1D) self._b = b @property @@ -542,7 +543,7 @@ def u(self): @u.setter def u(self, u): - cv.check_type('Watt restriction energy', u, Real) + cv.check_type("Watt restriction energy", u, Real) self._u = u def to_hdf5(self, group): @@ -555,10 +556,10 @@ def to_hdf5(self, group): """ - group.attrs['type'] = np.bytes_('watt') - group.attrs['u'] = self.u - self.a.to_hdf5(group, 'a') - self.b.to_hdf5(group, 'b') + group.attrs["type"] = np.bytes_("watt") + group.attrs["u"] = self.u + self.a.to_hdf5(group, "a") + self.b.to_hdf5(group, "b") @classmethod def from_hdf5(cls, group): @@ -575,9 +576,9 @@ def from_hdf5(cls, group): Watt fission spectrum """ - a = Tabulated1D.from_hdf5(group['a']) - b = Tabulated1D.from_hdf5(group['b']) - u = group.attrs['u'] + a = Tabulated1D.from_hdf5(group["a"]) + b = Tabulated1D.from_hdf5(group["b"]) + u = group.attrs["u"] return cls(a, b, u) @classmethod @@ -603,8 +604,8 @@ def from_ace(cls, ace, idx): # Advance index nr = int(ace.xss[idx]) - ne = int(ace.xss[idx + 1 + 2*nr]) - idx += 2 + 2*nr + 2*ne + ne = int(ace.xss[idx + 1 + 2 * nr]) + idx += 2 + 2 * nr + 2 * ne # Energy-dependent b parameter -- units are MeV^-1 b = Tabulated1D.from_ace(ace, idx) @@ -612,11 +613,11 @@ def from_ace(cls, ace, idx): # Advance index nr = int(ace.xss[idx]) - ne = int(ace.xss[idx + 1 + 2*nr]) - idx += 2 + 2*nr + 2*ne + ne = int(ace.xss[idx + 1 + 2 * nr]) + idx += 2 + 2 * nr + 2 * ne # Restriction energy - u = ace.xss[idx]*EV_PER_MEV + u = ace.xss[idx] * EV_PER_MEV return cls(a, b, u) @@ -692,9 +693,9 @@ def efl(self): @efl.setter def efl(self, efl): - name = 'Madland-Nix light fragment energy' + name = "Madland-Nix light fragment energy" cv.check_type(name, efl, Real) - cv.check_greater_than(name, efl, 0.) + cv.check_greater_than(name, efl, 0.0) self._efl = efl @property @@ -703,9 +704,9 @@ def efh(self): @efh.setter def efh(self, efh): - name = 'Madland-Nix heavy fragment energy' + name = "Madland-Nix heavy fragment energy" cv.check_type(name, efh, Real) - cv.check_greater_than(name, efh, 0.) + cv.check_greater_than(name, efh, 0.0) self._efh = efh @property @@ -714,7 +715,7 @@ def tm(self): @tm.setter def tm(self, tm): - cv.check_type('Madland-Nix maximum temperature', tm, Tabulated1D) + cv.check_type("Madland-Nix maximum temperature", tm, Tabulated1D) self._tm = tm def to_hdf5(self, group): @@ -727,9 +728,9 @@ def to_hdf5(self, group): """ - group.attrs['type'] = np.bytes_('madland-nix') - group.attrs['efl'] = self.efl - group.attrs['efh'] = self.efh + group.attrs["type"] = np.bytes_("madland-nix") + group.attrs["efl"] = self.efl + group.attrs["efh"] = self.efh self.tm.to_hdf5(group) @classmethod @@ -747,9 +748,9 @@ def from_hdf5(cls, group): Madland-Nix fission spectrum """ - efl = group.attrs['efl'] - efh = group.attrs['efh'] - tm = Tabulated1D.from_hdf5(group['tm']) + efl = group.attrs["efl"] + efh = group.attrs["efh"] + tm = Tabulated1D.from_hdf5(group["tm"]) return cls(efl, efh, tm) @classmethod @@ -814,7 +815,7 @@ def primary_flag(self): @primary_flag.setter def primary_flag(self, primary_flag): - cv.check_type('discrete photon primary_flag', primary_flag, Integral) + cv.check_type("discrete photon primary_flag", primary_flag, Integral) self._primary_flag = primary_flag @property @@ -823,7 +824,7 @@ def energy(self): @energy.setter def energy(self, energy): - cv.check_type('discrete photon energy', energy, Real) + cv.check_type("discrete photon energy", energy, Real) self._energy = energy @property @@ -832,7 +833,7 @@ def atomic_weight_ratio(self): @atomic_weight_ratio.setter def atomic_weight_ratio(self, atomic_weight_ratio): - cv.check_type('atomic weight ratio', atomic_weight_ratio, Real) + cv.check_type("atomic weight ratio", atomic_weight_ratio, Real) self._atomic_weight_ratio = atomic_weight_ratio def to_hdf5(self, group): @@ -845,10 +846,10 @@ def to_hdf5(self, group): """ - group.attrs['type'] = np.bytes_('discrete_photon') - group.attrs['primary_flag'] = self.primary_flag - group.attrs['energy'] = self.energy - group.attrs['atomic_weight_ratio'] = self.atomic_weight_ratio + group.attrs["type"] = np.bytes_("discrete_photon") + group.attrs["primary_flag"] = self.primary_flag + group.attrs["energy"] = self.energy + group.attrs["atomic_weight_ratio"] = self.atomic_weight_ratio @classmethod def from_hdf5(cls, group): @@ -865,9 +866,9 @@ def from_hdf5(cls, group): Discrete photon energy distribution """ - primary_flag = group.attrs['primary_flag'] - energy = group.attrs['energy'] - awr = group.attrs['atomic_weight_ratio'] + primary_flag = group.attrs["primary_flag"] + energy = group.attrs["energy"] + awr = group.attrs["atomic_weight_ratio"] return cls(primary_flag, energy, awr) @classmethod @@ -888,7 +889,7 @@ def from_ace(cls, ace, idx): """ primary_flag = int(ace.xss[idx]) - energy = ace.xss[idx + 1]*EV_PER_MEV + energy = ace.xss[idx + 1] * EV_PER_MEV return cls(primary_flag, energy, ace.atomic_weight_ratio) @@ -922,7 +923,7 @@ def threshold(self): @threshold.setter def threshold(self, threshold): - cv.check_type('level inelastic threhsold', threshold, Real) + cv.check_type("level inelastic threhsold", threshold, Real) self._threshold = threshold @property @@ -931,7 +932,7 @@ def mass_ratio(self): @mass_ratio.setter def mass_ratio(self, mass_ratio): - cv.check_type('level inelastic mass ratio', mass_ratio, Real) + cv.check_type("level inelastic mass ratio", mass_ratio, Real) self._mass_ratio = mass_ratio def to_hdf5(self, group): @@ -944,9 +945,9 @@ def to_hdf5(self, group): """ - group.attrs['type'] = np.bytes_('level') - group.attrs['threshold'] = self.threshold - group.attrs['mass_ratio'] = self.mass_ratio + group.attrs["type"] = np.bytes_("level") + group.attrs["threshold"] = self.threshold + group.attrs["mass_ratio"] = self.mass_ratio @classmethod def from_hdf5(cls, group): @@ -963,8 +964,8 @@ def from_hdf5(cls, group): Level inelastic scattering distribution """ - threshold = group.attrs['threshold'] - mass_ratio = group.attrs['mass_ratio'] + threshold = group.attrs["threshold"] + mass_ratio = group.attrs["mass_ratio"] return cls(threshold, mass_ratio) @classmethod @@ -984,7 +985,7 @@ def from_ace(cls, ace, idx): Level inelastic scattering distribution """ - threshold = ace.xss[idx]*EV_PER_MEV + threshold = ace.xss[idx] * EV_PER_MEV mass_ratio = ace.xss[idx + 1] return cls(threshold, mass_ratio) @@ -1029,8 +1030,7 @@ def breakpoints(self): @breakpoints.setter def breakpoints(self, breakpoints): - cv.check_type('continuous tabular breakpoints', breakpoints, - Iterable, Integral) + cv.check_type("continuous tabular breakpoints", breakpoints, Iterable, Integral) self._breakpoints = breakpoints @property @@ -1039,8 +1039,9 @@ def interpolation(self): @interpolation.setter def interpolation(self, interpolation): - cv.check_type('continuous tabular interpolation', interpolation, - Iterable, Integral) + cv.check_type( + "continuous tabular interpolation", interpolation, Iterable, Integral + ) self._interpolation = interpolation @property @@ -1049,8 +1050,7 @@ def energy(self): @energy.setter def energy(self, energy): - cv.check_type('continuous tabular incoming energy', energy, - Iterable, Real) + cv.check_type("continuous tabular incoming energy", energy, Iterable, Real) self._energy = energy @property @@ -1059,8 +1059,9 @@ def energy_out(self): @energy_out.setter def energy_out(self, energy_out): - cv.check_type('continuous tabular outgoing energy', energy_out, - Iterable, Univariate) + cv.check_type( + "continuous tabular outgoing energy", energy_out, Iterable, Univariate + ) self._energy_out = energy_out def to_hdf5(self, group): @@ -1073,11 +1074,10 @@ def to_hdf5(self, group): """ - group.attrs['type'] = np.bytes_('continuous') + group.attrs["type"] = np.bytes_("continuous") - dset = group.create_dataset('energy', data=self.energy) - dset.attrs['interpolation'] = np.vstack((self.breakpoints, - self.interpolation)) + dset = group.create_dataset("energy", data=self.energy) + dset.attrs["interpolation"] = np.vstack((self.breakpoints, self.interpolation)) # Determine total number of (E,p) pairs and create array n_pairs = sum(len(d) for d in self.energy_out) @@ -1097,32 +1097,32 @@ def to_hdf5(self, group): if isinstance(eout, Mixture): discrete, continuous = eout.distribution n_discrete_lines[i] = m = len(discrete) - interpolation[i] = 1 if continuous.interpolation == 'histogram' else 2 - pairs[0, j:j+m] = discrete.x - pairs[1, j:j+m] = discrete.p - pairs[2, j:j+m] = discrete.c - pairs[0, j+m:j+n] = continuous.x - pairs[1, j+m:j+n] = continuous.p - pairs[2, j+m:j+n] = continuous.c + interpolation[i] = 1 if continuous.interpolation == "histogram" else 2 + pairs[0, j : j + m] = discrete.x + pairs[1, j : j + m] = discrete.p + pairs[2, j : j + m] = discrete.c + pairs[0, j + m : j + n] = continuous.x + pairs[1, j + m : j + n] = continuous.p + pairs[2, j + m : j + n] = continuous.c else: if isinstance(eout, Tabular): n_discrete_lines[i] = 0 - interpolation[i] = 1 if eout.interpolation == 'histogram' else 2 + interpolation[i] = 1 if eout.interpolation == "histogram" else 2 elif isinstance(eout, Discrete): n_discrete_lines[i] = n interpolation[i] = 1 - pairs[0, j:j+n] = eout.x - pairs[1, j:j+n] = eout.p - pairs[2, j:j+n] = eout.c + pairs[0, j : j + n] = eout.x + pairs[1, j : j + n] = eout.p + pairs[2, j : j + n] = eout.c j += n # Create dataset for distributions - dset = group.create_dataset('distribution', data=pairs) + dset = group.create_dataset("distribution", data=pairs) # Write interpolation as attribute - dset.attrs['offsets'] = offsets - dset.attrs['interpolation'] = interpolation - dset.attrs['n_discrete_lines'] = n_discrete_lines + dset.attrs["offsets"] = offsets + dset.attrs["interpolation"] = interpolation + dset.attrs["n_discrete_lines"] = n_discrete_lines @classmethod def from_hdf5(cls, group): @@ -1139,15 +1139,15 @@ def from_hdf5(cls, group): Continuous tabular energy distribution """ - interp_data = group['energy'].attrs['interpolation'] + interp_data = group["energy"].attrs["interpolation"] energy_breakpoints = interp_data[0, :] energy_interpolation = interp_data[1, :] - energy = group['energy'][()] + energy = group["energy"][()] - data = group['distribution'] - offsets = data.attrs['offsets'] - interpolation = data.attrs['interpolation'] - n_discrete_lines = data.attrs['n_discrete_lines'] + data = group["distribution"] + offsets = data.attrs["offsets"] + interpolation = data.attrs["interpolation"] + n_discrete_lines = data.attrs["n_discrete_lines"] energy_out = [] n_energy = len(energy) @@ -1156,22 +1156,24 @@ def from_hdf5(cls, group): # discrete lines j = offsets[i] if i < n_energy - 1: - n = offsets[i+1] - j + n = offsets[i + 1] - j else: n = data.shape[1] - j m = n_discrete_lines[i] # Create discrete distribution if lines are present if m > 0: - eout_discrete = Discrete(data[0, j:j+m], data[1, j:j+m]) - eout_discrete.c = data[2, j:j+m] + eout_discrete = Discrete(data[0, j : j + m], data[1, j : j + m]) + eout_discrete.c = data[2, j : j + m] p_discrete = eout_discrete.c[-1] # Create continuous distribution if m < n: interp = INTERPOLATION_SCHEME[interpolation[i]] - eout_continuous = Tabular(data[0, j+m:j+n], data[1, j+m:j+n], interp) - eout_continuous.c = data[2, j+m:j+n] + eout_continuous = Tabular( + data[0, j + m : j + n], data[1, j + m : j + n], interp + ) + eout_continuous.c = data[2, j + m : j + n] # If both continuous and discrete are present, create a mixture # distribution @@ -1180,12 +1182,12 @@ def from_hdf5(cls, group): elif m == n: eout_i = eout_discrete else: - eout_i = Mixture([p_discrete, 1. - p_discrete], - [eout_discrete, eout_continuous]) + eout_i = Mixture( + [p_discrete, 1.0 - p_discrete], [eout_discrete, eout_continuous] + ) energy_out.append(eout_i) - return cls(energy_breakpoints, energy_interpolation, - energy, energy_out) + return cls(energy_breakpoints, energy_interpolation, energy, energy_out) @classmethod def from_ace(cls, ace, idx, ldis): @@ -1210,24 +1212,24 @@ def from_ace(cls, ace, idx, ldis): """ # Read number of interpolation regions and incoming energies n_regions = int(ace.xss[idx]) - n_energy_in = int(ace.xss[idx + 1 + 2*n_regions]) + n_energy_in = int(ace.xss[idx + 1 + 2 * n_regions]) # Get interpolation information idx += 1 if n_regions > 0: - breakpoints = ace.xss[idx:idx + n_regions].astype(int) - interpolation = ace.xss[idx + n_regions:idx + 2*n_regions].astype(int) + breakpoints = ace.xss[idx : idx + n_regions].astype(int) + interpolation = ace.xss[idx + n_regions : idx + 2 * n_regions].astype(int) else: breakpoints = np.array([n_energy_in]) interpolation = np.array([2]) # Incoming energies at which distributions exist - idx += 2*n_regions + 1 - energy = ace.xss[idx:idx + n_energy_in]*EV_PER_MEV + idx += 2 * n_regions + 1 + energy = ace.xss[idx : idx + n_energy_in] * EV_PER_MEV # Location of distributions idx += n_energy_in - loc_dist = ace.xss[idx:idx + n_energy_in].astype(int) + loc_dist = ace.xss[idx : idx + n_energy_in].astype(int) # Initialize variables energy_out = [] @@ -1239,34 +1241,40 @@ def from_ace(cls, ace, idx, ldis): # intt = interpolation scheme (1=hist, 2=lin-lin) INTTp = int(ace.xss[idx]) intt = INTTp % 10 - n_discrete_lines = (INTTp - intt)//10 + n_discrete_lines = (INTTp - intt) // 10 if intt not in (1, 2): - warn("Interpolation scheme for continuous tabular distribution " - "is not histogram or linear-linear.") + warn( + "Interpolation scheme for continuous tabular distribution " + "is not histogram or linear-linear." + ) intt = 2 n_energy_out = int(ace.xss[idx + 1]) - data = ace.xss[idx + 2:idx + 2 + 3*n_energy_out].copy() + data = ace.xss[idx + 2 : idx + 2 + 3 * n_energy_out].copy() data.shape = (3, n_energy_out) - data[0,:] *= EV_PER_MEV + data[0, :] *= EV_PER_MEV # Create continuous distribution - eout_continuous = Tabular(data[0][n_discrete_lines:], - data[1][n_discrete_lines:]/EV_PER_MEV, - INTERPOLATION_SCHEME[intt]) + eout_continuous = Tabular( + data[0][n_discrete_lines:], + data[1][n_discrete_lines:] / EV_PER_MEV, + INTERPOLATION_SCHEME[intt], + ) eout_continuous.c = data[2][n_discrete_lines:] # If discrete lines are present, create a mixture distribution if n_discrete_lines > 0: - eout_discrete = Discrete(data[0][:n_discrete_lines], - data[1][:n_discrete_lines]) + eout_discrete = Discrete( + data[0][:n_discrete_lines], data[1][:n_discrete_lines] + ) eout_discrete.c = data[2][:n_discrete_lines] if n_discrete_lines == n_energy_out: eout_i = eout_discrete else: p_discrete = min(sum(eout_discrete.p), 1.0) - eout_i = Mixture([p_discrete, 1. - p_discrete], - [eout_discrete, eout_continuous]) + eout_i = Mixture( + [p_discrete, 1.0 - p_discrete], [eout_discrete, eout_continuous] + ) else: eout_i = eout_continuous diff --git a/openmc/data/fission_energy.py b/openmc/data/fission_energy.py index 870881dbaf7..94f8abb974a 100644 --- a/openmc/data/fission_energy.py +++ b/openmc/data/fission_energy.py @@ -10,9 +10,15 @@ _NAMES = ( - 'fragments', 'prompt_neutrons', 'delayed_neutrons', - 'prompt_photons', 'delayed_photons', 'betas', - 'neutrinos', 'recoverable', 'total' + "fragments", + "prompt_neutrons", + "delayed_neutrons", + "prompt_photons", + "delayed_photons", + "betas", + "neutrinos", + "recoverable", + "total", ) @@ -86,8 +92,17 @@ class from the usual OpenMC HDF5 data files. - incident neutron energy). """ - def __init__(self, fragments, prompt_neutrons, delayed_neutrons, - prompt_photons, delayed_photons, betas, neutrinos): + + def __init__( + self, + fragments, + prompt_neutrons, + delayed_neutrons, + prompt_photons, + delayed_photons, + betas, + neutrinos, + ): self.fragments = fragments self.prompt_neutrons = prompt_neutrons self.delayed_neutrons = delayed_neutrons @@ -102,7 +117,7 @@ def fragments(self): @fragments.setter def fragments(self, energy_release): - cv.check_type('fragments', energy_release, Callable) + cv.check_type("fragments", energy_release, Callable) self._fragments = energy_release @property @@ -111,7 +126,7 @@ def prompt_neutrons(self): @prompt_neutrons.setter def prompt_neutrons(self, energy_release): - cv.check_type('prompt_neutrons', energy_release, Callable) + cv.check_type("prompt_neutrons", energy_release, Callable) self._prompt_neutrons = energy_release @property @@ -120,7 +135,7 @@ def delayed_neutrons(self): @delayed_neutrons.setter def delayed_neutrons(self, energy_release): - cv.check_type('delayed_neutrons', energy_release, Callable) + cv.check_type("delayed_neutrons", energy_release, Callable) self._delayed_neutrons = energy_release @property @@ -129,7 +144,7 @@ def prompt_photons(self): @prompt_photons.setter def prompt_photons(self, energy_release): - cv.check_type('prompt_photons', energy_release, Callable) + cv.check_type("prompt_photons", energy_release, Callable) self._prompt_photons = energy_release @property @@ -138,7 +153,7 @@ def delayed_photons(self): @delayed_photons.setter def delayed_photons(self, energy_release): - cv.check_type('delayed_photons', energy_release, Callable) + cv.check_type("delayed_photons", energy_release, Callable) self._delayed_photons = energy_release @property @@ -147,7 +162,7 @@ def betas(self): @betas.setter def betas(self, energy_release): - cv.check_type('betas', energy_release, Callable) + cv.check_type("betas", energy_release, Callable) self._betas = energy_release @property @@ -156,27 +171,43 @@ def neutrinos(self): @neutrinos.setter def neutrinos(self, energy_release): - cv.check_type('neutrinos', energy_release, Callable) + cv.check_type("neutrinos", energy_release, Callable) self._neutrinos = energy_release @property def recoverable(self): - components = ['fragments', 'prompt_neutrons', 'delayed_neutrons', - 'prompt_photons', 'delayed_photons', 'betas'] + components = [ + "fragments", + "prompt_neutrons", + "delayed_neutrons", + "prompt_photons", + "delayed_photons", + "betas", + ] return sum_functions(getattr(self, c) for c in components) @property def total(self): - components = ['fragments', 'prompt_neutrons', 'delayed_neutrons', - 'prompt_photons', 'delayed_photons', 'betas', - 'neutrinos'] + components = [ + "fragments", + "prompt_neutrons", + "delayed_neutrons", + "prompt_photons", + "delayed_photons", + "betas", + "neutrinos", + ] return sum_functions(getattr(self, c) for c in components) @property def q_prompt(self): # Use a polynomial to subtract incident energy. - funcs = [self.fragments, self.prompt_neutrons, self.prompt_photons, - Polynomial((0.0, -1.0))] + funcs = [ + self.fragments, + self.prompt_neutrons, + self.prompt_photons, + Polynomial((0.0, -1.0)), + ] return sum_functions(funcs) @property @@ -206,23 +237,29 @@ def from_endf(cls, ev, incident_neutron): Fission energy release data """ - cv.check_type('evaluation', ev, Evaluation) + cv.check_type("evaluation", ev, Evaluation) # Check to make sure this ENDF file matches the expected isomer. - if ev.target['atomic_number'] != incident_neutron.atomic_number: - raise ValueError('The atomic number of the ENDF evaluation does ' - 'not match the given IncidentNeutron.') - if ev.target['mass_number'] != incident_neutron.mass_number: - raise ValueError('The atomic mass of the ENDF evaluation does ' - 'not match the given IncidentNeutron.') - if ev.target['isomeric_state'] != incident_neutron.metastable: - raise ValueError('The metastable state of the ENDF evaluation ' - 'does not match the given IncidentNeutron.') - if not ev.target['fissionable']: - raise ValueError('The ENDF evaluation is not fissionable.') + if ev.target["atomic_number"] != incident_neutron.atomic_number: + raise ValueError( + "The atomic number of the ENDF evaluation does " + "not match the given IncidentNeutron." + ) + if ev.target["mass_number"] != incident_neutron.mass_number: + raise ValueError( + "The atomic mass of the ENDF evaluation does " + "not match the given IncidentNeutron." + ) + if ev.target["isomeric_state"] != incident_neutron.metastable: + raise ValueError( + "The metastable state of the ENDF evaluation " + "does not match the given IncidentNeutron." + ) + if not ev.target["fissionable"]: + raise ValueError("The ENDF evaluation is not fissionable.") if (1, 458) not in ev.section: - raise ValueError('ENDF evaluation does not have MF=1, MT=458.') + raise ValueError("ENDF evaluation does not have MF=1, MT=458.") file_obj = StringIO(ev.section[1, 458]) @@ -239,10 +276,10 @@ def from_endf(cls, ev, incident_neutron): # Associate each set of values and uncertainties with its label. functions = {} for i, name in enumerate(_NAMES): - coeffs = data[2*i::18] + coeffs = data[2 * i :: 18] # Ignore recoverable and total since we recalculate those directly - if name in ('recoverable', 'total'): + if name in ("recoverable", "total"): continue # In ENDF/B-VII.1, data for 2nd-order coefficients were mistakenly @@ -252,7 +289,7 @@ def from_endf(cls, ev, incident_neutron): # If a 5 MeV neutron causes a change of more than 100 MeV, we # know something is wrong. second_order = coeffs[2] - if abs(second_order) * (5e6)**2 > 1e8: + if abs(second_order) * (5e6) ** 2 > 1e8: # If we found the error, reduce 2nd-order coeff by 10**6. coeffs[2] /= EV_PER_MEV @@ -265,48 +302,54 @@ def from_endf(cls, ev, incident_neutron): # If a single coefficient was given, we need to use the Sher-Beck # formula for energy dependence zeroth_order = coeffs[0] - if name in ('delayed_photons', 'betas'): + if name in ("delayed_photons", "betas"): func = Polynomial((zeroth_order, -0.075)) - elif name == 'neutrinos': + elif name == "neutrinos": func = Polynomial((zeroth_order, -0.105)) - elif name == 'prompt_neutrons': + elif name == "prompt_neutrons": # Prompt neutrons require nu-data. It is not clear from # ENDF-102 whether prompt or total nu value should be used, but # the delayed neutron fraction is so small that the difference # is negligible. MT=18 (n, fission) might not be available so # try MT=19 (n, f) as well. if 18 in incident_neutron and not incident_neutron[18].redundant: - nu = [p.yield_ for p in incident_neutron[18].products - if p.particle == 'neutron' - and p.emission_mode in ('prompt', 'total')] + nu = [ + p.yield_ + for p in incident_neutron[18].products + if p.particle == "neutron" + and p.emission_mode in ("prompt", "total") + ] elif 19 in incident_neutron: - nu = [p.yield_ for p in incident_neutron[19].products - if p.particle == 'neutron' - and p.emission_mode in ('prompt', 'total')] + nu = [ + p.yield_ + for p in incident_neutron[19].products + if p.particle == "neutron" + and p.emission_mode in ("prompt", "total") + ] else: - raise ValueError('IncidentNeutron data has no fission ' - 'reaction.') + raise ValueError("IncidentNeutron data has no fission " "reaction.") if len(nu) == 0: raise ValueError( - 'Nu data is needed to compute fission energy ' - 'release with the Sher-Beck format.' + "Nu data is needed to compute fission energy " + "release with the Sher-Beck format." ) if len(nu) > 1: - raise ValueError('Ambiguous prompt/total nu value.') + raise ValueError("Ambiguous prompt/total nu value.") nu = nu[0] if isinstance(nu, Tabulated1D): # Evaluate Sher-Beck polynomial form at each tabulated value func = deepcopy(nu) - func.y = (zeroth_order + 1.307*nu.x - 8.07e6*(nu.y - nu.y[0])) + func.y = zeroth_order + 1.307 * nu.x - 8.07e6 * (nu.y - nu.y[0]) elif isinstance(nu, Polynomial): # Combine polynomials if len(nu) == 1: func = Polynomial([zeroth_order, 1.307]) else: func = Polynomial( - [zeroth_order, 1.307 - 8.07e6*nu.coef[1]] - + [-8.07e6*c for c in nu.coef[2:]]) + [zeroth_order, 1.307 - 8.07e6 * nu.coef[1]] + + [-8.07e6 * c for c in nu.coef[2:]] + ) else: func = Polynomial(coeffs) @@ -344,16 +387,23 @@ def from_hdf5(cls, group): """ - fragments = Function1D.from_hdf5(group['fragments']) - prompt_neutrons = Function1D.from_hdf5(group['prompt_neutrons']) - delayed_neutrons = Function1D.from_hdf5(group['delayed_neutrons']) - prompt_photons = Function1D.from_hdf5(group['prompt_photons']) - delayed_photons = Function1D.from_hdf5(group['delayed_photons']) - betas = Function1D.from_hdf5(group['betas']) - neutrinos = Function1D.from_hdf5(group['neutrinos']) - - return cls(fragments, prompt_neutrons, delayed_neutrons, prompt_photons, - delayed_photons, betas, neutrinos) + fragments = Function1D.from_hdf5(group["fragments"]) + prompt_neutrons = Function1D.from_hdf5(group["prompt_neutrons"]) + delayed_neutrons = Function1D.from_hdf5(group["delayed_neutrons"]) + prompt_photons = Function1D.from_hdf5(group["prompt_photons"]) + delayed_photons = Function1D.from_hdf5(group["delayed_photons"]) + betas = Function1D.from_hdf5(group["betas"]) + neutrinos = Function1D.from_hdf5(group["neutrinos"]) + + return cls( + fragments, + prompt_neutrons, + delayed_neutrons, + prompt_photons, + delayed_photons, + betas, + neutrinos, + ) def to_hdf5(self, group): """Write energy release data to an HDF5 group @@ -365,12 +415,12 @@ def to_hdf5(self, group): """ - self.fragments.to_hdf5(group, 'fragments') - self.prompt_neutrons.to_hdf5(group, 'prompt_neutrons') - self.delayed_neutrons.to_hdf5(group, 'delayed_neutrons') - self.prompt_photons.to_hdf5(group, 'prompt_photons') - self.delayed_photons.to_hdf5(group, 'delayed_photons') - self.betas.to_hdf5(group, 'betas') - self.neutrinos.to_hdf5(group, 'neutrinos') - self.q_prompt.to_hdf5(group, 'q_prompt') - self.q_recoverable.to_hdf5(group, 'q_recoverable') + self.fragments.to_hdf5(group, "fragments") + self.prompt_neutrons.to_hdf5(group, "prompt_neutrons") + self.delayed_neutrons.to_hdf5(group, "delayed_neutrons") + self.prompt_photons.to_hdf5(group, "prompt_photons") + self.delayed_photons.to_hdf5(group, "delayed_photons") + self.betas.to_hdf5(group, "betas") + self.neutrinos.to_hdf5(group, "neutrinos") + self.q_prompt.to_hdf5(group, "q_prompt") + self.q_recoverable.to_hdf5(group, "q_recoverable") diff --git a/openmc/data/function.py b/openmc/data/function.py index c5914f513d5..0ad8fedcb95 100644 --- a/openmc/data/function.py +++ b/openmc/data/function.py @@ -12,8 +12,13 @@ from openmc.mixin import EqualityMixin from .data import EV_PER_MEV -INTERPOLATION_SCHEME = {1: 'histogram', 2: 'linear-linear', 3: 'linear-log', - 4: 'log-linear', 5: 'log-log'} +INTERPOLATION_SCHEME = { + 1: "histogram", + 2: "linear-linear", + 3: "linear-log", + 4: "log-linear", + 5: "log-log", +} def sum_functions(funcs): @@ -39,8 +44,9 @@ def sum_functions(funcs): if isinstance(f, Tabulated1D): xs.append(f.x) if not np.all(f.interpolation == 2): - raise ValueError('Only linear-linear tabulated functions ' - 'can be combined') + raise ValueError( + "Only linear-linear tabulated functions " "can be combined" + ) if xs: # Take the union of all energies (sorted) @@ -58,11 +64,13 @@ def sum_functions(funcs): class Function1D(EqualityMixin, ABC): """A function of one independent variable with HDF5 support.""" + @abstractmethod - def __call__(self): pass + def __call__(self): + pass @abstractmethod - def to_hdf5(self, group, name='xy'): + def to_hdf5(self, group, name="xy"): """Write function to an HDF5 group Parameters @@ -91,10 +99,11 @@ def from_hdf5(cls, dataset): """ for subclass in cls.__subclasses__(): - if dataset.attrs['type'].decode() == subclass.__name__: + if dataset.attrs["type"].decode() == subclass.__name__: return subclass.from_hdf5(dataset) - raise ValueError("Unrecognized Function1D class: '" - + dataset.attrs['type'].decode() + "'") + raise ValueError( + "Unrecognized Function1D class: '" + dataset.attrs["type"].decode() + "'" + ) class Tabulated1D(Function1D): @@ -164,19 +173,19 @@ def __call__(self, x): y = np.zeros_like(x) # Get indices for interpolation - idx = np.searchsorted(self.x, x, side='right') - 1 + idx = np.searchsorted(self.x, x, side="right") - 1 # Loop over interpolation regions for k in range(len(self.breakpoints)): # Get indices for the begining and ending of this region - i_begin = self.breakpoints[k-1] - 1 if k > 0 else 0 + i_begin = self.breakpoints[k - 1] - 1 if k > 0 else 0 i_end = self.breakpoints[k] - 1 # Figure out which idx values lie within this region contained = (idx >= i_begin) & (idx < i_end) - xk = x[contained] # x values in this region - xi = self.x[idx[contained]] # low edge of corresponding bins + xk = x[contained] # x values in this region + xi = self.x[idx[contained]] # low edge of corresponding bins xi1 = self.x[idx[contained] + 1] # high edge of corresponding bins yi = self.y[idx[contained]] yi1 = self.y[idx[contained] + 1] @@ -187,20 +196,21 @@ def __call__(self, x): elif self.interpolation[k] == 2: # Linear-linear - y[contained] = yi + (xk - xi)/(xi1 - xi)*(yi1 - yi) + y[contained] = yi + (xk - xi) / (xi1 - xi) * (yi1 - yi) elif self.interpolation[k] == 3: # Linear-log - y[contained] = yi + np.log(xk/xi)/np.log(xi1/xi)*(yi1 - yi) + y[contained] = yi + np.log(xk / xi) / np.log(xi1 / xi) * (yi1 - yi) elif self.interpolation[k] == 4: # Log-linear - y[contained] = yi*np.exp((xk - xi)/(xi1 - xi)*np.log(yi1/yi)) + y[contained] = yi * np.exp((xk - xi) / (xi1 - xi) * np.log(yi1 / yi)) elif self.interpolation[k] == 5: # Log-log - y[contained] = (yi*np.exp(np.log(xk/xi)/np.log(xi1/xi) - *np.log(yi1/yi))) + y[contained] = yi * np.exp( + np.log(xk / xi) / np.log(xi1 / xi) * np.log(yi1 / yi) + ) # In some cases, x values might be outside the tabulated region due only # to precision, so we check if they're close and set them equal if so. @@ -216,14 +226,14 @@ def _interpolate_scalar(self, x): return self._y[-1] # Get the index for interpolation - idx = np.searchsorted(self._x, x, side='right') - 1 + idx = np.searchsorted(self._x, x, side="right") - 1 # Loop over interpolation regions for b, p in zip(self.breakpoints, self.interpolation): if idx < b - 1: break - xi = self._x[idx] # low edge of the corresponding bin + xi = self._x[idx] # low edge of the corresponding bin xi1 = self._x[idx + 1] # high edge of the corresponding bin yi = self._y[idx] yi1 = self._y[idx + 1] @@ -234,19 +244,19 @@ def _interpolate_scalar(self, x): elif p == 2: # Linear-linear - return yi + (x - xi)/(xi1 - xi)*(yi1 - yi) + return yi + (x - xi) / (xi1 - xi) * (yi1 - yi) elif p == 3: # Linear-log - return yi + log(x/xi)/log(xi1/xi)*(yi1 - yi) + return yi + log(x / xi) / log(xi1 / xi) * (yi1 - yi) elif p == 4: # Log-linear - return yi*exp((x - xi)/(xi1 - xi)*log(yi1/yi)) + return yi * exp((x - xi) / (xi1 - xi) * log(yi1 / yi)) elif p == 5: # Log-log - return yi*exp(log(x/xi)/log(xi1/xi)*log(yi1/yi)) + return yi * exp(log(x / xi) / log(xi1 / xi) * log(yi1 / yi)) def __len__(self): return len(self.x) @@ -257,7 +267,7 @@ def x(self): @x.setter def x(self, x): - cv.check_type('x values', x, Iterable, Real) + cv.check_type("x values", x, Iterable, Real) self._x = x @property @@ -266,7 +276,7 @@ def y(self): @y.setter def y(self, y): - cv.check_type('y values', y, Iterable, Real) + cv.check_type("y values", y, Iterable, Real) self._y = y @property @@ -275,7 +285,7 @@ def breakpoints(self): @breakpoints.setter def breakpoints(self, breakpoints): - cv.check_type('breakpoints', breakpoints, Iterable, Integral) + cv.check_type("breakpoints", breakpoints, Iterable, Integral) self._breakpoints = breakpoints @property @@ -284,7 +294,7 @@ def interpolation(self): @interpolation.setter def interpolation(self, interpolation): - cv.check_type('interpolation', interpolation, Iterable, Integral) + cv.check_type("interpolation", interpolation, Iterable, Integral) self._interpolation = interpolation @property @@ -316,42 +326,44 @@ def integral(self): # Get x values and bounding (x,y) pairs x0 = self.x[i_low:i_high] - x1 = self.x[i_low + 1:i_high + 1] + x1 = self.x[i_low + 1 : i_high + 1] y0 = self.y[i_low:i_high] - y1 = self.y[i_low + 1:i_high + 1] + y1 = self.y[i_low + 1 : i_high + 1] if self.interpolation[k] == 1: # Histogram - partial_sum[i_low:i_high] = y0*(x1 - x0) + partial_sum[i_low:i_high] = y0 * (x1 - x0) elif self.interpolation[k] == 2: # Linear-linear - m = (y1 - y0)/(x1 - x0) - partial_sum[i_low:i_high] = (y0 - m*x0)*(x1 - x0) + \ - m*(x1**2 - x0**2)/2 + m = (y1 - y0) / (x1 - x0) + partial_sum[i_low:i_high] = (y0 - m * x0) * (x1 - x0) + m * ( + x1**2 - x0**2 + ) / 2 elif self.interpolation[k] == 3: # Linear-log - logx = np.log(x1/x0) - m = (y1 - y0)/logx - partial_sum[i_low:i_high] = y0 + m*(x1*(logx - 1) + x0) + logx = np.log(x1 / x0) + m = (y1 - y0) / logx + partial_sum[i_low:i_high] = y0 + m * (x1 * (logx - 1) + x0) elif self.interpolation[k] == 4: # Log-linear - m = np.log(y1/y0)/(x1 - x0) - partial_sum[i_low:i_high] = y0/m*(np.exp(m*(x1 - x0)) - 1) + m = np.log(y1 / y0) / (x1 - x0) + partial_sum[i_low:i_high] = y0 / m * (np.exp(m * (x1 - x0)) - 1) elif self.interpolation[k] == 5: # Log-log - m = np.log(y1/y0)/np.log(x1/x0) - partial_sum[i_low:i_high] = y0/((m + 1)*x0**m)*( - x1**(m + 1) - x0**(m + 1)) + m = np.log(y1 / y0) / np.log(x1 / x0) + partial_sum[i_low:i_high] = ( + y0 / ((m + 1) * x0**m) * (x1 ** (m + 1) - x0 ** (m + 1)) + ) i_low = i_high - return np.concatenate(([0.], np.cumsum(partial_sum))) + return np.concatenate(([0.0], np.cumsum(partial_sum))) - def to_hdf5(self, group, name='xy'): + def to_hdf5(self, group, name="xy"): """Write tabulated function to an HDF5 group Parameters @@ -362,11 +374,10 @@ def to_hdf5(self, group, name='xy'): Name of the dataset to create """ - dataset = group.create_dataset(name, data=np.vstack( - [self.x, self.y])) - dataset.attrs['type'] = np.bytes_(type(self).__name__) - dataset.attrs['breakpoints'] = self.breakpoints - dataset.attrs['interpolation'] = self.interpolation + dataset = group.create_dataset(name, data=np.vstack([self.x, self.y])) + dataset.attrs["type"] = np.bytes_(type(self).__name__) + dataset.attrs["breakpoints"] = self.breakpoints + dataset.attrs["interpolation"] = self.interpolation @classmethod def from_hdf5(cls, dataset): @@ -383,14 +394,15 @@ def from_hdf5(cls, dataset): Function read from dataset """ - if dataset.attrs['type'].decode() != cls.__name__: - raise ValueError("Expected an HDF5 attribute 'type' equal to '" - + cls.__name__ + "'") + if dataset.attrs["type"].decode() != cls.__name__: + raise ValueError( + "Expected an HDF5 attribute 'type' equal to '" + cls.__name__ + "'" + ) x = dataset[0, :] y = dataset[1, :] - breakpoints = dataset.attrs['breakpoints'] - interpolation = dataset.attrs['interpolation'] + breakpoints = dataset.attrs["breakpoints"] + interpolation = dataset.attrs["interpolation"] return cls(x, y, breakpoints, interpolation) @classmethod @@ -416,22 +428,22 @@ def from_ace(cls, ace, idx=0, convert_units=True): # Get number of regions and pairs n_regions = int(ace.xss[idx]) - n_pairs = int(ace.xss[idx + 1 + 2*n_regions]) + n_pairs = int(ace.xss[idx + 1 + 2 * n_regions]) # Get interpolation information idx += 1 if n_regions > 0: - breakpoints = ace.xss[idx:idx + n_regions].astype(int) - interpolation = ace.xss[idx + n_regions:idx + 2*n_regions].astype(int) + breakpoints = ace.xss[idx : idx + n_regions].astype(int) + interpolation = ace.xss[idx + n_regions : idx + 2 * n_regions].astype(int) else: # 0 regions implies linear-linear interpolation by default breakpoints = np.array([n_pairs]) interpolation = np.array([2]) # Get (x,y) pairs - idx += 2*n_regions + 1 - x = ace.xss[idx:idx + n_pairs].copy() - y = ace.xss[idx + n_pairs:idx + 2*n_pairs].copy() + idx += 2 * n_regions + 1 + x = ace.xss[idx : idx + n_pairs].copy() + y = ace.xss[idx + n_pairs : idx + 2 * n_pairs].copy() if convert_units: x *= EV_PER_MEV @@ -448,7 +460,8 @@ class Polynomial(np.polynomial.Polynomial, Function1D): Polynomial coefficients in order of increasing degree """ - def to_hdf5(self, group, name='xy'): + + def to_hdf5(self, group, name="xy"): """Write polynomial function to an HDF5 group Parameters @@ -460,7 +473,7 @@ def to_hdf5(self, group, name='xy'): """ dataset = group.create_dataset(name, data=self.coef) - dataset.attrs['type'] = np.bytes_(type(self).__name__) + dataset.attrs["type"] = np.bytes_(type(self).__name__) @classmethod def from_hdf5(cls, dataset): @@ -477,9 +490,10 @@ def from_hdf5(cls, dataset): Function read from dataset """ - if dataset.attrs['type'].decode() != cls.__name__: - raise ValueError("Expected an HDF5 attribute 'type' equal to '" - + cls.__name__ + "'") + if dataset.attrs["type"].decode() != cls.__name__: + raise ValueError( + "Expected an HDF5 attribute 'type' equal to '" + cls.__name__ + "'" + ) return cls(dataset[()]) @@ -529,7 +543,7 @@ def functions(self): @functions.setter def functions(self, functions): - cv.check_type('functions', functions, Iterable, Callable) + cv.check_type("functions", functions, Iterable, Callable) self._functions = functions @property @@ -538,9 +552,9 @@ def operations(self): @operations.setter def operations(self, operations): - cv.check_type('operations', operations, Iterable, np.ufunc) + cv.check_type("operations", operations, Iterable, np.ufunc) length = len(self.functions) - 1 - cv.check_length('operations', operations, length, length_max=length) + cv.check_length("operations", operations, length, length_max=length) self._operations = operations @@ -575,10 +589,10 @@ def functions(self): @functions.setter def functions(self, functions): - cv.check_type('functions', functions, Iterable, Callable) + cv.check_type("functions", functions, Iterable, Callable) self._functions = functions - def to_hdf5(self, group, name='xy'): + def to_hdf5(self, group, name="xy"): """Write sum of functions to an HDF5 group .. versionadded:: 0.13.1 @@ -592,10 +606,10 @@ def to_hdf5(self, group, name='xy'): """ sum_group = group.create_group(name) - sum_group.attrs['type'] = np.bytes_(type(self).__name__) - sum_group.attrs['n'] = len(self.functions) + sum_group.attrs["type"] = np.bytes_(type(self).__name__) + sum_group.attrs["n"] = len(self.functions) for i, f in enumerate(self.functions): - f.to_hdf5(sum_group, f'func_{i+1}') + f.to_hdf5(sum_group, f"func_{i+1}") @classmethod def from_hdf5(cls, group): @@ -614,11 +628,8 @@ def from_hdf5(cls, group): Functions read from the group """ - n = group.attrs['n'] - functions = [ - Function1D.from_hdf5(group[f'func_{i+1}']) - for i in range(n) - ] + n = group.attrs["n"] + functions = [Function1D.from_hdf5(group[f"func_{i+1}"]) for i in range(n)] return cls(functions) @@ -666,7 +677,7 @@ def functions(self): @functions.setter def functions(self, functions): - cv.check_type('functions', functions, Iterable, Callable) + cv.check_type("functions", functions, Iterable, Callable) self._functions = functions @property @@ -675,7 +686,7 @@ def breakpoints(self): @breakpoints.setter def breakpoints(self, breakpoints): - cv.check_iterable_type('breakpoints', breakpoints, Real) + cv.check_iterable_type("breakpoints", breakpoints, Real) self._breakpoints = breakpoints @@ -702,7 +713,6 @@ class ResonancesWithBackground(EqualityMixin): """ - def __init__(self, resonances, background, mt): self.resonances = resonances self.background = background @@ -714,7 +724,7 @@ def background(self): @background.setter def background(self, background): - cv.check_type('background cross section', background, Callable) + cv.check_type("background cross section", background, Callable) self._background = background @property @@ -723,7 +733,7 @@ def mt(self): @mt.setter def mt(self, mt): - cv.check_type('MT value', mt, Integral) + cv.check_type("MT value", mt, Integral) self._mt = mt @property @@ -732,6 +742,7 @@ def resonances(self): @resonances.setter def resonances(self, resonances): - cv.check_type('resolved resonance parameters', resonances, - openmc.data.Resonances) + cv.check_type( + "resolved resonance parameters", resonances, openmc.data.Resonances + ) self._resonances = resonances diff --git a/openmc/data/grid.py b/openmc/data/grid.py index 33a5b7b00ea..fe37b62110c 100644 --- a/openmc/data/grid.py +++ b/openmc/data/grid.py @@ -42,14 +42,14 @@ def linearize(x, f, tolerance=0.001): y_high, y_low = y_stack[-2:] # Evaluate the function at the midpoint - x_mid = 0.5*(x_low + x_high) + x_mid = 0.5 * (x_low + x_high) y_mid = f(x_mid) # Linearly interpolate between the bounding points - y_interp = y_low + (y_high - y_low)/(x_high - x_low)*(x_mid - x_low) + y_interp = y_low + (y_high - y_low) / (x_high - x_low) * (x_mid - x_low) # Check the error on the interpolated point and compare to tolerance - error = abs((y_interp - y_mid)/y_mid) + error = abs((y_interp - y_mid) / y_mid) if error > tolerance: x_stack.insert(-1, x_mid) y_stack.insert(-1, y_mid) @@ -64,6 +64,7 @@ def linearize(x, f, tolerance=0.001): return np.array(x_out), np.array(y_out) + def thin(x, y, tolerance=0.001): """Check for (x,y) points that can be removed. @@ -93,15 +94,15 @@ def thin(x, y, tolerance=0.001): i_right = 2 while i_left < N - 2 and i_right < N: - m = (y[i_right] - y[i_left])/(x[i_right] - x[i_left]) + m = (y[i_right] - y[i_left]) / (x[i_right] - x[i_left]) for i in range(i_left + 1, i_right): # Determine error in interpolated point - y_interp = y[i_left] + m*(x[i] - x[i_left]) - if abs(y[i]) > 0.: - error = abs((y_interp - y[i])/y[i]) + y_interp = y[i_left] + m * (x[i] - x[i_left]) + if abs(y[i]) > 0.0: + error = abs((y_interp - y[i]) / y[i]) else: - error = 2*tolerance + error = 2 * tolerance if error > tolerance: for i_remove in range(i_left + 1, i_right - 1): diff --git a/openmc/data/kalbach_mann.py b/openmc/data/kalbach_mann.py index d92bf9c213a..b1ae9ba6480 100644 --- a/openmc/data/kalbach_mann.py +++ b/openmc/data/kalbach_mann.py @@ -42,15 +42,18 @@ class _AtomicRepresentation(EqualityMixin): number """ + def __init__(self, z, a): # Sanity checks on values - cv.check_type('z', z, Integral) - cv.check_greater_than('z', z, 0, equality=True) - cv.check_type('a', a, Integral) - cv.check_greater_than('a', a, 0, equality=True) + cv.check_type("z", z, Integral) + cv.check_greater_than("z", z, 0, equality=True) + cv.check_type("a", a, Integral) + cv.check_greater_than("a", a, 0, equality=True) if z > a: - raise ValueError(f"Number of protons ({z}) must be less than or " - f"equal to number of nucleons ({a}).") + raise ValueError( + f"Number of protons ({z}) must be less than or " + f"equal to number of nucleons ({a})." + ) self._z = z self._a = a @@ -140,25 +143,30 @@ def _separation_energy(compound, nucleus, particle): 1002: 2.224566, 1003: 8.481798, 2003: 7.718043, - 2004: 28.29566 + 2004: 28.29566, } I_a = za_to_breaking_energy[particle.za] # Eq. 4 in in doi:10.1103/PhysRevC.37.2350 or ENDF-6 Formats Manual section # 6.2.3.2 return ( - 15.68 * (A_c - A_a) - - 28.07 * ((N_c - Z_c)**2 / A_c - (N_a - Z_a)**2 / A_a) - - 18.56 * (A_c**(2./3.) - A_a**(2./3.)) + - 33.22 * ((N_c - Z_c)**2 / A_c**(4./3.) - (N_a - Z_a)**2 / A_a**(4./3.)) - - 0.717 * (Z_c**2 / A_c**(1./3.) - Z_a**2 / A_a**(1./3.)) + - 1.211 * (Z_c**2 / A_c - Z_a**2 / A_a) - - I_a + 15.68 * (A_c - A_a) + - 28.07 * ((N_c - Z_c) ** 2 / A_c - (N_a - Z_a) ** 2 / A_a) + - 18.56 * (A_c ** (2.0 / 3.0) - A_a ** (2.0 / 3.0)) + + 33.22 + * ( + (N_c - Z_c) ** 2 / A_c ** (4.0 / 3.0) + - (N_a - Z_a) ** 2 / A_a ** (4.0 / 3.0) + ) + - 0.717 * (Z_c**2 / A_c ** (1.0 / 3.0) - Z_a**2 / A_a ** (1.0 / 3.0)) + + 1.211 * (Z_c**2 / A_c - Z_a**2 / A_a) + - I_a ) -def kalbach_slope(energy_projectile, energy_emitted, za_projectile, - za_emitted, za_target): +def kalbach_slope( + energy_projectile, energy_emitted, za_projectile, za_emitted, za_target +): """Returns Kalbach-Mann slope from calculations. The associated reaction is defined as: @@ -207,9 +215,7 @@ def kalbach_slope(energy_projectile, energy_emitted, za_projectile, # TODO: develop for photons as projectile # TODO: test for other particles than neutron if za_projectile != 1: - raise NotImplementedError( - "Developed and tested for neutron projectile only." - ) + raise NotImplementedError("Developed and tested for neutron projectile only.") # Special handling of elemental carbon if za_emitted == 6000: @@ -226,8 +232,7 @@ def kalbach_slope(energy_projectile, energy_emitted, za_projectile, # Calculate entrance and emission channel energy in MeV, defined in section # 6.2.3.2 in the ENDF-6 Formats Manual epsilon_a = energy_projectile * target.a / (target.a + projectile.a) / EV_PER_MEV - epsilon_b = energy_emitted * (residual.a + emitted.a) \ - / (residual.a * EV_PER_MEV) + epsilon_b = energy_emitted * (residual.a + emitted.a) / (residual.a * EV_PER_MEV) # Calculate separation energies using Eq. 4 in doi:10.1103/PhysRevC.37.2350 # or ENDF-6 Formats Manual section 6.2.3.2 @@ -242,8 +247,8 @@ def kalbach_slope(energy_projectile, energy_emitted, za_projectile, m = za_to_m[emitted.za] e_a = epsilon_a + s_a e_b = epsilon_b + s_b - r_1 = min(e_a, 130.) - r_3 = min(e_a, 41.) + r_1 = min(e_a, 130.0) + r_3 = min(e_a, 41.0) x_1 = r_1 * e_b / e_a x_3 = r_3 * e_b / e_a return 0.04 * x_1 + 1.8e-6 * x_1**3 + 6.7e-7 * M * m * x_3**4 @@ -288,8 +293,9 @@ class KalbachMann(AngleEnergy): """ - def __init__(self, breakpoints, interpolation, energy, energy_out, - precompound, slope): + def __init__( + self, breakpoints, interpolation, energy, energy_out, precompound, slope + ): super().__init__() self.breakpoints = breakpoints self.interpolation = interpolation @@ -304,8 +310,7 @@ def breakpoints(self): @breakpoints.setter def breakpoints(self, breakpoints): - cv.check_type('Kalbach-Mann breakpoints', breakpoints, - Iterable, Integral) + cv.check_type("Kalbach-Mann breakpoints", breakpoints, Iterable, Integral) self._breakpoints = breakpoints @property @@ -314,8 +319,7 @@ def interpolation(self): @interpolation.setter def interpolation(self, interpolation): - cv.check_type('Kalbach-Mann interpolation', interpolation, - Iterable, Integral) + cv.check_type("Kalbach-Mann interpolation", interpolation, Iterable, Integral) self._interpolation = interpolation @property @@ -324,8 +328,7 @@ def energy(self): @energy.setter def energy(self, energy): - cv.check_type('Kalbach-Mann incoming energy', energy, - Iterable, Real) + cv.check_type("Kalbach-Mann incoming energy", energy, Iterable, Real) self._energy = energy @property @@ -334,8 +337,7 @@ def energy_out(self): @energy_out.setter def energy_out(self, energy_out): - cv.check_type('Kalbach-Mann distributions', energy_out, - Iterable, Univariate) + cv.check_type("Kalbach-Mann distributions", energy_out, Iterable, Univariate) self._energy_out = energy_out @property @@ -344,8 +346,9 @@ def precompound(self): @precompound.setter def precompound(self, precompound): - cv.check_type('Kalbach-Mann precompound factor', precompound, - Iterable, Tabulated1D) + cv.check_type( + "Kalbach-Mann precompound factor", precompound, Iterable, Tabulated1D + ) self._precompound = precompound @property @@ -354,7 +357,7 @@ def slope(self): @slope.setter def slope(self, slope): - cv.check_type('Kalbach-Mann slope', slope, Iterable, Tabulated1D) + cv.check_type("Kalbach-Mann slope", slope, Iterable, Tabulated1D) self._slope = slope def to_hdf5(self, group): @@ -366,11 +369,10 @@ def to_hdf5(self, group): HDF5 group to write to """ - group.attrs['type'] = np.bytes_('kalbach-mann') + group.attrs["type"] = np.bytes_("kalbach-mann") - dset = group.create_dataset('energy', data=self.energy) - dset.attrs['interpolation'] = np.vstack((self.breakpoints, - self.interpolation)) + dset = group.create_dataset("energy", data=self.energy) + dset.attrs["interpolation"] = np.vstack((self.breakpoints, self.interpolation)) # Determine total number of (E,p,r,a) tuples and create array n_tuple = sum(len(d) for d in self.energy_out) @@ -383,43 +385,44 @@ def to_hdf5(self, group): j = 0 # Populate offsets and distribution array - for i, (eout, km_r, km_a) in enumerate(zip( - self.energy_out, self.precompound, self.slope)): + for i, (eout, km_r, km_a) in enumerate( + zip(self.energy_out, self.precompound, self.slope) + ): n = len(eout) offsets[i] = j if isinstance(eout, Mixture): discrete, continuous = eout.distribution n_discrete_lines[i] = m = len(discrete) - interpolation[i] = 1 if continuous.interpolation == 'histogram' else 2 - distribution[0, j:j+m] = discrete.x - distribution[1, j:j+m] = discrete.p - distribution[2, j:j+m] = discrete.c - distribution[0, j+m:j+n] = continuous.x - distribution[1, j+m:j+n] = continuous.p - distribution[2, j+m:j+n] = continuous.c + interpolation[i] = 1 if continuous.interpolation == "histogram" else 2 + distribution[0, j : j + m] = discrete.x + distribution[1, j : j + m] = discrete.p + distribution[2, j : j + m] = discrete.c + distribution[0, j + m : j + n] = continuous.x + distribution[1, j + m : j + n] = continuous.p + distribution[2, j + m : j + n] = continuous.c else: if isinstance(eout, Tabular): n_discrete_lines[i] = 0 - interpolation[i] = 1 if eout.interpolation == 'histogram' else 2 + interpolation[i] = 1 if eout.interpolation == "histogram" else 2 elif isinstance(eout, Discrete): n_discrete_lines[i] = n interpolation[i] = 1 - distribution[0, j:j+n] = eout.x - distribution[1, j:j+n] = eout.p - distribution[2, j:j+n] = eout.c + distribution[0, j : j + n] = eout.x + distribution[1, j : j + n] = eout.p + distribution[2, j : j + n] = eout.c - distribution[3, j:j+n] = km_r.y - distribution[4, j:j+n] = km_a.y + distribution[3, j : j + n] = km_r.y + distribution[4, j : j + n] = km_a.y j += n # Create dataset for distributions - dset = group.create_dataset('distribution', data=distribution) + dset = group.create_dataset("distribution", data=distribution) # Write interpolation as attribute - dset.attrs['offsets'] = offsets - dset.attrs['interpolation'] = interpolation - dset.attrs['n_discrete_lines'] = n_discrete_lines + dset.attrs["offsets"] = offsets + dset.attrs["interpolation"] = interpolation + dset.attrs["n_discrete_lines"] = n_discrete_lines @classmethod def from_hdf5(cls, group): @@ -436,15 +439,15 @@ def from_hdf5(cls, group): Kalbach-Mann energy distribution """ - interp_data = group['energy'].attrs['interpolation'] + interp_data = group["energy"].attrs["interpolation"] energy_breakpoints = interp_data[0, :] energy_interpolation = interp_data[1, :] - energy = group['energy'][()] + energy = group["energy"][()] - data = group['distribution'] - offsets = data.attrs['offsets'] - interpolation = data.attrs['interpolation'] - n_discrete_lines = data.attrs['n_discrete_lines'] + data = group["distribution"] + offsets = data.attrs["offsets"] + interpolation = data.attrs["interpolation"] + n_discrete_lines = data.attrs["n_discrete_lines"] energy_out = [] precompound = [] @@ -455,22 +458,24 @@ def from_hdf5(cls, group): # discrete lines j = offsets[i] if i < n_energy - 1: - n = offsets[i+1] - j + n = offsets[i + 1] - j else: n = data.shape[1] - j m = n_discrete_lines[i] # Create discrete distribution if lines are present if m > 0: - eout_discrete = Discrete(data[0, j:j+m], data[1, j:j+m]) - eout_discrete.c = data[2, j:j+m] + eout_discrete = Discrete(data[0, j : j + m], data[1, j : j + m]) + eout_discrete.c = data[2, j : j + m] p_discrete = eout_discrete.c[-1] # Create continuous distribution if m < n: interp = INTERPOLATION_SCHEME[interpolation[i]] - eout_continuous = Tabular(data[0, j+m:j+n], data[1, j+m:j+n], interp) - eout_continuous.c = data[2, j+m:j+n] + eout_continuous = Tabular( + data[0, j + m : j + n], data[1, j + m : j + n], interp + ) + eout_continuous.c = data[2, j + m : j + n] # If both continuous and discrete are present, create a mixture # distribution @@ -479,19 +484,26 @@ def from_hdf5(cls, group): elif m == n: eout_i = eout_discrete else: - eout_i = Mixture([p_discrete, 1. - p_discrete], - [eout_discrete, eout_continuous]) + eout_i = Mixture( + [p_discrete, 1.0 - p_discrete], [eout_discrete, eout_continuous] + ) # Precompound factor and slope are on rows 3 and 4, respectively - km_r = Tabulated1D(data[0, j:j+n], data[3, j:j+n]) - km_a = Tabulated1D(data[0, j:j+n], data[4, j:j+n]) + km_r = Tabulated1D(data[0, j : j + n], data[3, j : j + n]) + km_a = Tabulated1D(data[0, j : j + n], data[4, j : j + n]) energy_out.append(eout_i) precompound.append(km_r) slope.append(km_a) - return cls(energy_breakpoints, energy_interpolation, - energy, energy_out, precompound, slope) + return cls( + energy_breakpoints, + energy_interpolation, + energy, + energy_out, + precompound, + slope, + ) @classmethod def from_ace(cls, ace, idx, ldis): @@ -516,24 +528,24 @@ def from_ace(cls, ace, idx, ldis): """ # Read number of interpolation regions and incoming energies n_regions = int(ace.xss[idx]) - n_energy_in = int(ace.xss[idx + 1 + 2*n_regions]) + n_energy_in = int(ace.xss[idx + 1 + 2 * n_regions]) # Get interpolation information idx += 1 if n_regions > 0: - breakpoints = ace.xss[idx:idx + n_regions].astype(int) - interpolation = ace.xss[idx + n_regions:idx + 2*n_regions].astype(int) + breakpoints = ace.xss[idx : idx + n_regions].astype(int) + interpolation = ace.xss[idx + n_regions : idx + 2 * n_regions].astype(int) else: breakpoints = np.array([n_energy_in]) interpolation = np.array([2]) # Incoming energies at which distributions exist - idx += 2*n_regions + 1 - energy = ace.xss[idx:idx + n_energy_in]*EV_PER_MEV + idx += 2 * n_regions + 1 + energy = ace.xss[idx : idx + n_energy_in] * EV_PER_MEV # Location of distributions idx += n_energy_in - loc_dist = ace.xss[idx:idx + n_energy_in].astype(int) + loc_dist = ace.xss[idx : idx + n_energy_in].astype(int) # Initialize variables energy_out = [] @@ -547,38 +559,43 @@ def from_ace(cls, ace, idx, ldis): # intt = interpolation scheme (1=hist, 2=lin-lin) INTTp = int(ace.xss[idx]) intt = INTTp % 10 - n_discrete_lines = (INTTp - intt)//10 + n_discrete_lines = (INTTp - intt) // 10 if intt not in (1, 2): - warn("Interpolation scheme for continuous tabular distribution " - "is not histogram or linear-linear.") + warn( + "Interpolation scheme for continuous tabular distribution " + "is not histogram or linear-linear." + ) intt = 2 n_energy_out = int(ace.xss[idx + 1]) - data = ace.xss[idx + 2:idx + 2 + 5*n_energy_out].copy() + data = ace.xss[idx + 2 : idx + 2 + 5 * n_energy_out].copy() data.shape = (5, n_energy_out) data[0, :] *= EV_PER_MEV # Create continuous distribution - eout_continuous = Tabular(data[0][n_discrete_lines:], - data[1][n_discrete_lines:]/EV_PER_MEV, - INTERPOLATION_SCHEME[intt], - ignore_negative=True) + eout_continuous = Tabular( + data[0][n_discrete_lines:], + data[1][n_discrete_lines:] / EV_PER_MEV, + INTERPOLATION_SCHEME[intt], + ignore_negative=True, + ) eout_continuous.c = data[2][n_discrete_lines:] if np.any(data[1][n_discrete_lines:] < 0.0): - warn("Kalbach-Mann energy distribution has negative " - "probabilities.") + warn("Kalbach-Mann energy distribution has negative " "probabilities.") # If discrete lines are present, create a mixture distribution if n_discrete_lines > 0: - eout_discrete = Discrete(data[0][:n_discrete_lines], - data[1][:n_discrete_lines]) + eout_discrete = Discrete( + data[0][:n_discrete_lines], data[1][:n_discrete_lines] + ) eout_discrete.c = data[2][:n_discrete_lines] if n_discrete_lines == n_energy_out: eout_i = eout_discrete else: p_discrete = min(sum(eout_discrete.p), 1.0) - eout_i = Mixture([p_discrete, 1. - p_discrete], - [eout_discrete, eout_continuous]) + eout_i = Mixture( + [p_discrete, 1.0 - p_discrete], [eout_discrete, eout_continuous] + ) else: eout_i = eout_continuous @@ -655,7 +672,7 @@ def from_endf(cls, file_obj, za_emitted, za_target, projectile_mass): calculated_slope.append(False) else: # Check if the projectile is not a neutron - if not np.isclose(projectile_mass, 1.0, atol=1.0e-12, rtol=0.): + if not np.isclose(projectile_mass, 1.0, atol=1.0e-12, rtol=0.0): warn( "Kalbach-Mann slope calculation is only available with " "neutrons as projectile. Slope coefficients are set to 0." @@ -666,19 +683,24 @@ def from_endf(cls, file_obj, za_emitted, za_target, projectile_mass): else: # TODO: retrieve ZA of the projectile za_projectile = 1 - a_i = [kalbach_slope(energy_projectile=energy[i], - energy_emitted=e, - za_projectile=za_projectile, - za_emitted=za_emitted, - za_target=za_target) - for e in eout_i] + a_i = [ + kalbach_slope( + energy_projectile=energy[i], + energy_emitted=e, + za_projectile=za_projectile, + za_emitted=za_emitted, + za_target=za_target, + ) + for e in eout_i + ] calculated_slope.append(True) precompound.append(Tabulated1D(eout_i, r_i)) slope.append(Tabulated1D(eout_i, a_i)) - km_distribution = cls(tab2.breakpoints, tab2.interpolation, energy, - energy_out, precompound, slope) + km_distribution = cls( + tab2.breakpoints, tab2.interpolation, energy, energy_out, precompound, slope + ) # List of bool to indicate slope calculation by OpenMC km_distribution._calculated_slope = calculated_slope diff --git a/openmc/data/laboratory.py b/openmc/data/laboratory.py index c20b4596846..d4bcd51ee6a 100644 --- a/openmc/data/laboratory.py +++ b/openmc/data/laboratory.py @@ -56,8 +56,9 @@ def breakpoints(self): @breakpoints.setter def breakpoints(self, breakpoints): - cv.check_type('laboratory angle-energy breakpoints', breakpoints, - Iterable, Integral) + cv.check_type( + "laboratory angle-energy breakpoints", breakpoints, Iterable, Integral + ) self._breakpoints = breakpoints @property @@ -66,8 +67,9 @@ def interpolation(self): @interpolation.setter def interpolation(self, interpolation): - cv.check_type('laboratory angle-energy interpolation', interpolation, - Iterable, Integral) + cv.check_type( + "laboratory angle-energy interpolation", interpolation, Iterable, Integral + ) self._interpolation = interpolation @property @@ -76,8 +78,7 @@ def energy(self): @energy.setter def energy(self, energy): - cv.check_type('laboratory angle-energy incoming energy', energy, - Iterable, Real) + cv.check_type("laboratory angle-energy incoming energy", energy, Iterable, Real) self._energy = energy @property @@ -86,8 +87,9 @@ def mu(self): @mu.setter def mu(self, mu): - cv.check_type('laboratory angle-energy outgoing cosine', mu, - Iterable, Univariate) + cv.check_type( + "laboratory angle-energy outgoing cosine", mu, Iterable, Univariate + ) self._mu = mu @property @@ -96,8 +98,9 @@ def energy_out(self): @energy_out.setter def energy_out(self, energy_out): - cv.check_iterable_type('laboratory angle-energy outgoing energy', - energy_out, Univariate, 2, 2) + cv.check_iterable_type( + "laboratory angle-energy outgoing energy", energy_out, Univariate, 2, 2 + ) self._energy_out = energy_out @classmethod diff --git a/openmc/data/library.py b/openmc/data/library.py index a6ce1bbd32c..7a6d401e7c1 100644 --- a/openmc/data/library.py +++ b/openmc/data/library.py @@ -29,7 +29,7 @@ def libraries(self): # For backwards compatibility return self - def get_by_material(self, name, data_type='neutron'): + def get_by_material(self, name, data_type="neutron"): """Return the library dictionary containing a given material. Parameters @@ -49,11 +49,11 @@ def get_by_material(self, name, data_type='neutron'): """ for library in self: - if name in library['materials'] and data_type in library['type']: + if name in library["materials"] and data_type in library["type"]: return library return None - def remove_by_material(self, name: str, data_type='neutron'): + def remove_by_material(self, name: str, data_type="neutron"): """Remove the library dictionary containing a specific material Parameters @@ -84,21 +84,22 @@ def register_file(self, filename): else: path = filename - if path.suffix == '.xml': - filetype = 'depletion_chain' + if path.suffix == ".xml": + filetype = "depletion_chain" materials = [] - elif path.suffix == '.h5': - with h5py.File(path, 'r') as h5file: - filetype = h5file.attrs['filetype'].decode()[5:] + elif path.suffix == ".h5": + with h5py.File(path, "r") as h5file: + filetype = h5file.attrs["filetype"].decode()[5:] materials = list(h5file) else: raise ValueError( - f"File type {path.name} not supported by {self.__class__.__name__}") + f"File type {path.name} not supported by {self.__class__.__name__}" + ) - library = {'path': str(path), 'type': filetype, 'materials': materials} + library = {"path": str(path), "type": filetype, "materials": materials} self.append(library) - def export_to_xml(self, path='cross_sections.xml'): + def export_to_xml(self, path="cross_sections.xml"): """Export cross section data library to an XML file. Parameters @@ -107,26 +108,27 @@ def export_to_xml(self, path='cross_sections.xml'): Path to file to write. Defaults to 'cross_sections.xml'. """ - root = ET.Element('cross_sections') + root = ET.Element("cross_sections") # Determine common directory for library paths - common_dir = os.path.dirname(os.path.commonprefix( - [lib['path'] for lib in self])) - if common_dir == '': - common_dir = '.' + common_dir = os.path.dirname( + os.path.commonprefix([lib["path"] for lib in self]) + ) + if common_dir == "": + common_dir = "." - if os.path.relpath(common_dir, os.path.dirname(str(path))) != '.': + if os.path.relpath(common_dir, os.path.dirname(str(path))) != ".": dir_element = ET.SubElement(root, "directory") dir_element.text = os.path.realpath(common_dir) for library in self: - if library['type'] == "depletion_chain": + if library["type"] == "depletion_chain": lib_element = ET.SubElement(root, "depletion_chain") else: lib_element = ET.SubElement(root, "library") - lib_element.set('materials', ' '.join(library['materials'])) - lib_element.set('path', os.path.relpath(library['path'], common_dir)) - lib_element.set('type', library['type']) + lib_element.set("materials", " ".join(library["materials"])) + lib_element.set("path", os.path.relpath(library["path"], common_dir)) + lib_element.set("type", library["type"]) # Clean the indentation to be user-readable clean_indentation(root) @@ -134,8 +136,7 @@ def export_to_xml(self, path='cross_sections.xml'): # Write XML file reorder_attributes(root) # TODO: Remove when support is Python 3.8+ tree = ET.ElementTree(root) - tree.write(str(path), xml_declaration=True, encoding='utf-8', - method='xml') + tree.write(str(path), xml_declaration=True, encoding="utf-8", method="xml") @classmethod def from_xml(cls, path=None): @@ -158,34 +159,33 @@ def from_xml(cls, path=None): # If path is None, get the cross sections from the global configuration if path is None: - path = openmc.config.get('cross_sections') + path = openmc.config.get("cross_sections") # Check to make sure we picked up cross sections if path is None: - raise ValueError("Either path or openmc.config['cross_sections'] " - "must be set") + raise ValueError( + "Either path or openmc.config['cross_sections'] " "must be set" + ) tree = ET.parse(path) root = tree.getroot() - if root.find('directory') is not None: - directory = root.find('directory').text + if root.find("directory") is not None: + directory = root.find("directory").text else: directory = os.path.dirname(path) - for lib_element in root.findall('library'): - filename = os.path.join(directory, lib_element.attrib['path']) - filetype = lib_element.attrib['type'] - materials = lib_element.attrib['materials'].split() - library = {'path': filename, 'type': filetype, - 'materials': materials} + for lib_element in root.findall("library"): + filename = os.path.join(directory, lib_element.attrib["path"]) + filetype = lib_element.attrib["type"] + materials = lib_element.attrib["materials"].split() + library = {"path": filename, "type": filetype, "materials": materials} data.libraries.append(library) # get depletion chain data dep_node = root.find("depletion_chain") if dep_node is not None: - filename = os.path.join(directory, dep_node.attrib['path']) - library = {'path': filename, 'type': 'depletion_chain', - 'materials': []} + filename = os.path.join(directory, dep_node.attrib["path"]) + library = {"path": filename, "type": "depletion_chain", "materials": []} data.libraries.append(library) return data diff --git a/openmc/data/multipole.py b/openmc/data/multipole.py index dd14e0d1945..b27ed341af1 100644 --- a/openmc/data/multipole.py +++ b/openmc/data/multipole.py @@ -18,17 +18,17 @@ # Constants that determine which value to access -_MP_EA = 0 # Pole +_MP_EA = 0 # Pole # Residue indices -_MP_RS = 1 # Residue scattering -_MP_RA = 2 # Residue absorption -_MP_RF = 3 # Residue fission +_MP_RS = 1 # Residue scattering +_MP_RA = 2 # Residue absorption +_MP_RF = 3 # Residue fission # Polynomial fit indices -_FIT_S = 0 # Scattering -_FIT_A = 1 # Absorption -_FIT_F = 2 # Fission +_FIT_S = 0 # Scattering +_FIT_A = 1 # Absorption +_FIT_F = 2 # Fission # Upper temperature limit (K) TEMPERATURE_LIMIT = 3000 @@ -77,6 +77,7 @@ def _faddeeva(z): """ from scipy.special import wofz + if np.angle(z) > 0: return wofz(z) else: @@ -116,7 +117,7 @@ def _broaden_wmp_polynomials(E, dopp, n): exp_m_beta2 = 0.0 else: erf_beta = erf(beta) - exp_m_beta2 = exp(-beta**2) + exp_m_beta2 = exp(-(beta**2)) # Assume that, for sure, we'll use a second order (1/E, 1/V, const) # fit, and no less. @@ -125,24 +126,35 @@ def _broaden_wmp_polynomials(E, dopp, n): factors[0] = erf_beta / E factors[1] = 1.0 / sqrtE - factors[2] = (factors[0] * (half_inv_dopp2 + E) - + exp_m_beta2 / (beta * sqrt(pi))) + factors[2] = factors[0] * (half_inv_dopp2 + E) + exp_m_beta2 / (beta * sqrt(pi)) # Perform recursive broadening of high order components. range(1, n-2) # replaces a do i = 1, n-3. All indices are reduced by one due to the # 1-based vs. 0-based indexing. - for i in range(1, n-2): + for i in range(1, n - 2): if i != 1: - factors[i+2] = (-factors[i-2] * (i - 1.0) * i * quarter_inv_dopp4 - + factors[i] * (E + (1.0 + 2.0 * i) * half_inv_dopp2)) + factors[i + 2] = -factors[i - 2] * ( + i - 1.0 + ) * i * quarter_inv_dopp4 + factors[i] * ( + E + (1.0 + 2.0 * i) * half_inv_dopp2 + ) else: - factors[i+2] = factors[i]*(E + (1.0 + 2.0 * i) * half_inv_dopp2) + factors[i + 2] = factors[i] * (E + (1.0 + 2.0 * i) * half_inv_dopp2) return factors -def _vectfit_xs(energy, ce_xs, mts, rtol=1e-3, atol=1e-5, orders=None, - n_vf_iter=30, log=False, path_out=None): +def _vectfit_xs( + energy, + ce_xs, + mts, + rtol=1e-3, + atol=1e-5, + orders=None, + n_vf_iter=30, + log=False, + path_out=None, +): """Convert point-wise cross section to multipole data via vector fitting. Parameters @@ -181,13 +193,14 @@ def _vectfit_xs(energy, ce_xs, mts, rtol=1e-3, atol=1e-5, orders=None, ne = energy.size nmt = len(mts) if ce_xs.shape != (nmt, ne): - raise ValueError('Inconsistent cross section data.') + raise ValueError("Inconsistent cross section data.") # construct test data: interpolate xs with finer grids n_finer = 10 - ne_test = (ne - 1)*n_finer + 1 - test_energy = np.interp(np.arange(ne_test), - np.arange(ne_test, step=n_finer), energy) + ne_test = (ne - 1) * n_finer + 1 + test_energy = np.interp( + np.arange(ne_test), np.arange(ne_test, step=n_finer), energy + ) test_energy[[0, -1]] = energy[[0, -1]] # avoid numerical issue test_xs_ref = np.zeros((nmt, ne_test)) for i in range(nmt): @@ -205,8 +218,8 @@ def _vectfit_xs(energy, ce_xs, mts, rtol=1e-3, atol=1e-5, orders=None, # inverse weighting is used for minimizing the relative deviation instead of # absolute deviation in vector fitting - with np.errstate(divide='ignore'): - weight = 1.0/f + with np.errstate(divide="ignore"): + weight = 1.0 / f # avoid too large weights which will harm the fitting accuracy min_cross_section = 1e-7 @@ -214,18 +227,19 @@ def _vectfit_xs(energy, ce_xs, mts, rtol=1e-3, atol=1e-5, orders=None, if np.all(ce_xs[i] <= min_cross_section): weight[i] = 1.0 elif np.any(ce_xs[i] <= min_cross_section): - weight[i, ce_xs[i] <= min_cross_section] = \ - max(weight[i, ce_xs[i] > min_cross_section]) + weight[i, ce_xs[i] <= min_cross_section] = max( + weight[i, ce_xs[i] > min_cross_section] + ) # detect peaks (resonances) and determine VF order search range peaks, _ = find_peaks(ce_xs[0] + ce_xs[1]) n_peaks = peaks.size if orders is not None: # make sure orders are even integers - orders = list(set([int(i/2)*2 for i in orders if i >= 2])) + orders = list(set([int(i / 2) * 2 for i in orders if i >= 2])) else: - lowest_order = max(2, 2*n_peaks) - highest_order = max(200, 4*n_peaks) + lowest_order = max(2, 2 * n_peaks) + highest_order = max(200, 4 * n_peaks) orders = list(range(lowest_order, highest_order + 1, 2)) if log: @@ -240,8 +254,8 @@ def _vectfit_xs(energy, ce_xs, mts, rtol=1e-3, atol=1e-5, orders=None, if log: print(f"Order={order}({i}/{len(orders)})") # initial guessed poles - poles_r = np.linspace(s[0], s[-1], order//2) - poles = poles_r + poles_r*0.01j + poles_r = np.linspace(s[0], s[-1], order // 2) + poles = poles_r + poles_r * 0.01j poles = np.sort(np.append(poles, np.conj(poles))) found_better = False @@ -258,8 +272,8 @@ def _vectfit_xs(energy, ce_xs, mts, rtol=1e-3, atol=1e-5, orders=None, new_poles = [] for p in poles: p_r, p_i = np.real(p), np.imag(p) - if (s[0] <= p_r <= s[-1]) and p_i == 0.: - new_poles += [p_r+p_r*0.01j, p_r-p_r*0.01j] + if (s[0] <= p_r <= s[-1]) and p_i == 0.0: + new_poles += [p_r + p_r * 0.01j, p_r - p_r * 0.01j] n_real_poles += 1 else: new_poles += [p] @@ -268,28 +282,31 @@ def _vectfit_xs(energy, ce_xs, mts, rtol=1e-3, atol=1e-5, orders=None, if n_real_poles > 0: if log >= DETAILED_LOGGING: print(f" # real poles: {n_real_poles}") - new_poles, residues, cf, f_fit, rms = \ - vf.vectfit(f, s, new_poles, weight, skip_pole=True) + new_poles, residues, cf, f_fit, rms = vf.vectfit( + f, s, new_poles, weight, skip_pole=True + ) # assess the result on test grid test_xs = vf.evaluate(test_s, new_poles, residues) / test_energy abserr = np.abs(test_xs - test_xs_ref) - with np.errstate(invalid='ignore', divide='ignore'): + with np.errstate(invalid="ignore", divide="ignore"): relerr = abserr / test_xs_ref if np.any(np.isnan(abserr)): maxre, ratio, ratio2 = np.inf, -np.inf, -np.inf elif np.all(abserr <= atol): - maxre, ratio, ratio2 = 0., 1., 1. + maxre, ratio, ratio2 = 0.0, 1.0, 1.0 else: maxre = np.max(relerr[abserr > atol]) ratio = np.sum((relerr < rtol) | (abserr < atol)) / relerr.size - ratio2 = np.sum((relerr < 10*rtol) | (abserr < atol)) / relerr.size + ratio2 = ( + np.sum((relerr < 10 * rtol) | (abserr < atol)) / relerr.size + ) # define a metric for choosing the best fitting results # basically, it is preferred to have more points within accuracy # tolerance, smaller maximum deviation and fewer poles - #TODO: improve the metric with clearer basis - quality = ratio + ratio2 - min(0.1*maxre, 1) - 0.001*new_poles.size + # TODO: improve the metric with clearer basis + quality = ratio + ratio2 - min(0.1 * maxre, 1) - 0.001 * new_poles.size if np.any(test_xs < -atol): quality = -np.inf @@ -323,7 +340,7 @@ def _vectfit_xs(energy, ce_xs, mts, rtol=1e-3, atol=1e-5, orders=None, if found_better: n_discarded = 0 else: - if order > max(2*n_peaks, 50) and best_ratio > 0.7: + if order > max(2 * n_peaks, 50) and best_ratio > 0.7: n_discarded += 1 if n_discarded >= 10 or (n_discarded >= 5 and best_ratio > 0.9): if log >= DETAILED_LOGGING: @@ -338,7 +355,7 @@ def _vectfit_xs(energy, ce_xs, mts, rtol=1e-3, atol=1e-5, orders=None, if found_conj: found_conj = False continue - if np.imag(p) == 0.: + if np.imag(p) == 0.0: real_idx.append(i) else: if i < best_poles.size and np.conj(p) == best_poles[i + 1]: @@ -347,11 +364,18 @@ def _vectfit_xs(energy, ce_xs, mts, rtol=1e-3, atol=1e-5, orders=None, else: raise RuntimeError("Complex poles are not conjugate!") if log: - print("Found {} real poles and {} conjugate complex pairs.".format( - len(real_idx), len(conj_idx))) + print( + "Found {} real poles and {} conjugate complex pairs.".format( + len(real_idx), len(conj_idx) + ) + ) mp_poles = best_poles[real_idx + conj_idx] - mp_residues = np.concatenate((best_residues[:, real_idx], - best_residues[:, conj_idx]*2), axis=1)/1j + mp_residues = ( + np.concatenate( + (best_residues[:, real_idx], best_residues[:, conj_idx] * 2), axis=1 + ) + / 1j + ) if log: print(f"Final number of poles: {mp_poles.size}") @@ -362,25 +386,28 @@ def _vectfit_xs(energy, ce_xs, mts, rtol=1e-3, atol=1e-5, orders=None, if not test_xs_ref[i].any(): continue import matplotlib.pyplot as plt + fig, ax1 = plt.subplots() - lns1 = ax1.loglog(test_energy, test_xs_ref[i], 'g', label="ACE xs") - lns2 = ax1.loglog(test_energy, best_test_xs[i], 'b', label="VF xs") + lns1 = ax1.loglog(test_energy, test_xs_ref[i], "g", label="ACE xs") + lns2 = ax1.loglog(test_energy, best_test_xs[i], "b", label="VF xs") ax2 = ax1.twinx() - lns3 = ax2.loglog(test_energy, best_relerr[i], 'r', - label="Relative error", alpha=0.5) + lns3 = ax2.loglog( + test_energy, best_relerr[i], "r", label="Relative error", alpha=0.5 + ) lns = lns1 + lns2 + lns3 labels = [l.get_label() for l in lns] - ax1.legend(lns, labels, loc='best') - ax1.set_xlabel('energy (eV)') - ax1.set_ylabel('cross section (b)', color='b') - ax1.tick_params('y', colors='b') - ax2.set_ylabel('relative error', color='r') - ax2.tick_params('y', colors='r') + ax1.legend(lns, labels, loc="best") + ax1.set_xlabel("energy (eV)") + ax1.set_ylabel("cross section (b)", color="b") + ax1.tick_params("y", colors="b") + ax2.set_ylabel("relative error", color="r") + ax2.tick_params("y", colors="r") plt.title(f"MT {mt} vector fitted with {mp_poles.size} poles") fig.tight_layout() - fig_file = os.path.join(path_out, "{:.0f}-{:.0f}_MT{}.png".format( - energy[0], energy[-1], mt)) + fig_file = os.path.join( + path_out, "{:.0f}-{:.0f}_MT{}.png".format(energy[0], energy[-1], mt) + ) plt.savefig(fig_file) plt.close() if log: @@ -389,8 +416,15 @@ def _vectfit_xs(energy, ce_xs, mts, rtol=1e-3, atol=1e-5, orders=None, return (mp_poles, mp_residues) -def vectfit_nuclide(endf_file, njoy_error=5e-4, vf_pieces=None, - log=False, path_out=None, mp_filename=None, **kwargs): +def vectfit_nuclide( + endf_file, + njoy_error=5e-4, + vf_pieces=None, + log=False, + path_out=None, + mp_filename=None, + **kwargs, +): r"""Generate multipole data for a nuclide from ENDF. Parameters @@ -424,45 +458,52 @@ def vectfit_nuclide(endf_file, njoy_error=5e-4, vf_pieces=None, if log: print(f"Running NJOY to get 0K point-wise data (error={njoy_error})...") - nuc_ce = IncidentNeutron.from_njoy(endf_file, temperatures=[0.0], - error=njoy_error, broadr=False, heatr=False, purr=False) + nuc_ce = IncidentNeutron.from_njoy( + endf_file, + temperatures=[0.0], + error=njoy_error, + broadr=False, + heatr=False, + purr=False, + ) if log: print("Parsing cross sections within resolved resonance range...") # Determine upper energy: the lower of RRR upper bound and first threshold endf_res = IncidentNeutron.from_endf(endf_file).resonances - if hasattr(endf_res, 'resolved') and \ - hasattr(endf_res.resolved, 'energy_max') and \ - type(endf_res.resolved) is not ResonanceRange: + if ( + hasattr(endf_res, "resolved") + and hasattr(endf_res.resolved, "energy_max") + and type(endf_res.resolved) is not ResonanceRange + ): E_max = endf_res.resolved.energy_max - elif hasattr(endf_res, 'unresolved') and \ - hasattr(endf_res.unresolved, 'energy_min'): + elif hasattr(endf_res, "unresolved") and hasattr(endf_res.unresolved, "energy_min"): E_max = endf_res.unresolved.energy_min else: - E_max = nuc_ce.energy['0K'][-1] - E_max_idx = np.searchsorted(nuc_ce.energy['0K'], E_max, side='right') - 1 + E_max = nuc_ce.energy["0K"][-1] + E_max_idx = np.searchsorted(nuc_ce.energy["0K"], E_max, side="right") - 1 for mt in nuc_ce.reactions: - if hasattr(nuc_ce.reactions[mt].xs['0K'], '_threshold_idx'): - threshold_idx = nuc_ce.reactions[mt].xs['0K']._threshold_idx + if hasattr(nuc_ce.reactions[mt].xs["0K"], "_threshold_idx"): + threshold_idx = nuc_ce.reactions[mt].xs["0K"]._threshold_idx if 0 < threshold_idx < E_max_idx: E_max_idx = threshold_idx # parse energy and cross sections - energy = nuc_ce.energy['0K'][:E_max_idx + 1] + energy = nuc_ce.energy["0K"][: E_max_idx + 1] E_min, E_max = energy[0], energy[-1] n_points = energy.size - total_xs = nuc_ce[1].xs['0K'](energy) - elastic_xs = nuc_ce[2].xs['0K'](energy) + total_xs = nuc_ce[1].xs["0K"](energy) + elastic_xs = nuc_ce[2].xs["0K"](energy) try: - absorption_xs = nuc_ce[27].xs['0K'](energy) + absorption_xs = nuc_ce[27].xs["0K"](energy) except KeyError: absorption_xs = np.zeros_like(total_xs) fissionable = False try: - fission_xs = nuc_ce[18].xs['0K'](energy) + fission_xs = nuc_ce[18].xs["0K"](energy) fissionable = True except KeyError: pass @@ -486,13 +527,13 @@ def vectfit_nuclide(endf_file, njoy_error=5e-4, vf_pieces=None, # divide into pieces for complex nuclides peaks, _ = find_peaks(total_xs) n_peaks = peaks.size - if n_peaks > 200 or n_points > 30000 or n_peaks * n_points > 100*10000: - vf_pieces = max(5, n_peaks // 50, n_points // 2000) + if n_peaks > 200 or n_points > 30000 or n_peaks * n_points > 100 * 10000: + vf_pieces = max(5, n_peaks // 50, n_points // 2000) else: vf_pieces = 1 piece_width = (sqrt(E_max) - sqrt(E_min)) / vf_pieces - alpha = nuc_ce.atomic_weight_ratio/(K_BOLTZMANN*TEMPERATURE_LIMIT) + alpha = nuc_ce.atomic_weight_ratio / (K_BOLTZMANN * TEMPERATURE_LIMIT) poles, residues = [], [] # VF piece by piece @@ -500,32 +541,35 @@ def vectfit_nuclide(endf_file, njoy_error=5e-4, vf_pieces=None, if log: print(f"Vector fitting piece {i_piece + 1}/{vf_pieces}...") # start E of this piece - e_bound = (sqrt(E_min) + piece_width*(i_piece-0.5))**2 - if i_piece == 0 or sqrt(alpha*e_bound) < 4.0: + e_bound = (sqrt(E_min) + piece_width * (i_piece - 0.5)) ** 2 + if i_piece == 0 or sqrt(alpha * e_bound) < 4.0: e_start = E_min e_start_idx = 0 else: - e_start = max(E_min, (sqrt(alpha*e_bound) - 4.0)**2/alpha) - e_start_idx = np.searchsorted(energy, e_start, side='right') - 1 + e_start = max(E_min, (sqrt(alpha * e_bound) - 4.0) ** 2 / alpha) + e_start_idx = np.searchsorted(energy, e_start, side="right") - 1 # end E of this piece - e_bound = (sqrt(E_min) + piece_width*(i_piece + 1))**2 - e_end = min(E_max, (sqrt(alpha*e_bound) + 4.0)**2/alpha) - e_end_idx = np.searchsorted(energy, e_end, side='left') + 1 + e_bound = (sqrt(E_min) + piece_width * (i_piece + 1)) ** 2 + e_end = min(E_max, (sqrt(alpha * e_bound) + 4.0) ** 2 / alpha) + e_end_idx = np.searchsorted(energy, e_end, side="left") + 1 e_idx = range(e_start_idx, min(e_end_idx + 1, n_points)) - p, r = _vectfit_xs(energy[e_idx], ce_xs[:, e_idx], mts, log=log, - path_out=path_out, **kwargs) + p, r = _vectfit_xs( + energy[e_idx], ce_xs[:, e_idx], mts, log=log, path_out=path_out, **kwargs + ) poles.append(p) residues.append(r) # collect multipole data into a dictionary - mp_data = {"name": nuc_ce.name, - "AWR": nuc_ce.atomic_weight_ratio, - "E_min": E_min, - "E_max": E_max, - "poles": poles, - "residues": residues} + mp_data = { + "name": nuc_ce.name, + "AWR": nuc_ce.atomic_weight_ratio, + "E_min": E_min, + "E_max": E_max, + "poles": poles, + "residues": residues, + } # dump multipole data to file if path_out: @@ -534,7 +578,7 @@ def vectfit_nuclide(endf_file, njoy_error=5e-4, vf_pieces=None, if not mp_filename: mp_filename = f"{nuc_ce.name}_mp.pickle" mp_filename = os.path.join(path_out, mp_filename) - with open(mp_filename, 'wb') as f: + with open(mp_filename, "wb") as f: pickle.dump(mp_data, f) if log: print(f"Dumped multipole data to file: {mp_filename}") @@ -542,8 +586,9 @@ def vectfit_nuclide(endf_file, njoy_error=5e-4, vf_pieces=None, return mp_data -def _windowing(mp_data, n_cf, rtol=1e-3, atol=1e-5, n_win=None, spacing=None, - log=False): +def _windowing( + mp_data, n_cf, rtol=1e-3, atol=1e-5, n_win=None, spacing=None, log=False +): """Generate windowed multipole library from multipole data with specific settings of window size, curve fit order, etc. @@ -585,21 +630,21 @@ def _windowing(mp_data, n_cf, rtol=1e-3, atol=1e-5, n_win=None, spacing=None, n_pieces = len(mp_poles) piece_width = (sqrt(E_max) - sqrt(E_min)) / n_pieces - alpha = awr / (K_BOLTZMANN*TEMPERATURE_LIMIT) + alpha = awr / (K_BOLTZMANN * TEMPERATURE_LIMIT) # determine window size if n_win is None: if spacing is not None: # ensure the windows are within the multipole energy range n_win = int((sqrt(E_max) - sqrt(E_min)) / spacing) - E_max = (sqrt(E_min) + n_win*spacing)**2 + E_max = (sqrt(E_min) + n_win * spacing) ** 2 else: n_win = 1000 # inner window size spacing = (sqrt(E_max) - sqrt(E_min)) / n_win # make sure inner window size is smaller than energy piece size if spacing > piece_width: - raise ValueError('Window spacing cannot be larger than piece spacing.') + raise ValueError("Window spacing cannot be larger than piece spacing.") if log: print("Windowing:") @@ -627,28 +672,28 @@ def _windowing(mp_data, n_cf, rtol=1e-3, atol=1e-5, n_win=None, spacing=None, inend = inbegin + spacing incenter = (inbegin + inend) / 2.0 # extend window energy range for Doppler broadening - if iw == 0 or sqrt(alpha)*inbegin < 4.0: + if iw == 0 or sqrt(alpha) * inbegin < 4.0: e_start = inbegin**2 else: - e_start = max(E_min, (sqrt(alpha)*inbegin - 4.0)**2/alpha) - e_end = min(E_max, (sqrt(alpha)*inend + 4.0)**2/alpha) + e_start = max(E_min, (sqrt(alpha) * inbegin - 4.0) ** 2 / alpha) + e_end = min(E_max, (sqrt(alpha) * inend + 4.0) ** 2 / alpha) # locate piece and relevant poles - i_piece = min(n_pieces - 1, int((inbegin - sqrt(E_min))/piece_width + 0.5)) + i_piece = min(n_pieces - 1, int((inbegin - sqrt(E_min)) / piece_width + 0.5)) poles, residues = mp_poles[i_piece], mp_residues[i_piece] n_poles = poles.size # generate energy points for fitting: equally spaced in momentum - n_points = min(max(100, int((e_end - e_start)*4)), 10000) + n_points = min(max(100, int((e_end - e_start) * 4)), 10000) energy_sqrt = np.linspace(np.sqrt(e_start), np.sqrt(e_end), n_points) energy = energy_sqrt**2 # reference xs from multipole form, note the residue terms in the # multipole and vector fitting representations differ by a 1j - xs_ref = vf.evaluate(energy_sqrt, poles, residues*1j) / energy + xs_ref = vf.evaluate(energy_sqrt, poles, residues * 1j) / energy # curve fit matrix - matrix = np.vstack([energy**(0.5*i - 1) for i in range(n_cf + 1)]).T + matrix = np.vstack([energy ** (0.5 * i - 1) for i in range(n_cf + 1)]).T # start from 0 poles, initialize pointers to the center nearest pole center_pole_ind = np.argmin((np.fabs(poles.real - incenter))) @@ -659,8 +704,10 @@ def _windowing(mp_data, n_cf, rtol=1e-3, atol=1e-5, n_win=None, spacing=None, # calculate the cross sections contributed by the windowed poles if rp > lp: - xs_wp = vf.evaluate(energy_sqrt, poles[lp:rp], - residues[:, lp:rp]*1j) / energy + xs_wp = ( + vf.evaluate(energy_sqrt, poles[lp:rp], residues[:, lp:rp] * 1j) + / energy + ) else: xs_wp = np.zeros_like(xs_ref) @@ -670,13 +717,18 @@ def _windowing(mp_data, n_cf, rtol=1e-3, atol=1e-5, n_win=None, spacing=None, # assess the result abserr = np.abs(xs_fit + xs_wp - xs_ref) - with np.errstate(invalid='ignore', divide='ignore'): + with np.errstate(invalid="ignore", divide="ignore"): relerr = abserr / xs_ref if not np.any(np.isnan(abserr)): re = relerr[abserr > atol] - if re.size == 0 or np.all(re <= rtol) or \ - (re.max() <= 2*rtol and (re > rtol).sum() <= 0.01*relerr.size) or \ - (iw == 0 and np.all(relerr.mean(axis=1) <= rtol)): + if ( + re.size == 0 + or np.all(re <= rtol) + or ( + re.max() <= 2 * rtol and (re > rtol).sum() <= 0.01 * relerr.size + ) + or (iw == 0 and np.all(relerr.mean(axis=1) <= rtol)) + ): # meet tolerances if log >= DETAILED_LOGGING: print("Accuracy satisfied.") @@ -685,7 +737,7 @@ def _windowing(mp_data, n_cf, rtol=1e-3, atol=1e-5, n_win=None, spacing=None, # we expect pure curvefit will succeed for the first window # TODO: find the energy boundary below which no poles are allowed if iw == 0: - raise RuntimeError('Pure curvefit failed for the first window!') + raise RuntimeError("Pure curvefit failed for the first window!") # try to include one more pole (next center nearest) if rp >= n_poles: @@ -704,7 +756,7 @@ def _windowing(mp_data, n_cf, rtol=1e-3, atol=1e-5, n_win=None, spacing=None, # flatten and shrink by removing unused poles data = [] # used poles and residues for ip in range(n_pieces): - used = (poles_unused[ip] == 0) + used = poles_unused[ip] == 0 # stack poles and residues for library format data.append(np.vstack([mp_poles[ip][used], mp_residues[ip][:, used]]).T) # stack poles/residues in sequence vertically @@ -716,8 +768,10 @@ def _windowing(mp_data, n_cf, rtol=1e-3, atol=1e-5, n_win=None, spacing=None, ip, lp, rp, coefs = win_data[iw] # adjust indices and change to 1-based for the library format n_prev_poles = sum([poles_unused[i].size for i in range(ip)]) - n_unused = sum([(poles_unused[i] == 1).sum() for i in range(ip)]) + \ - (poles_unused[ip][:lp] == 1).sum() + n_unused = ( + sum([(poles_unused[i] == 1).sum() for i in range(ip)]) + + (poles_unused[ip][:lp] == 1).sum() + ) lp += n_prev_poles - n_unused + 1 rp += n_prev_poles - n_unused windows.append([lp, rp]) @@ -781,6 +835,7 @@ class WindowedMultipole(EqualityMixin): a/E + b/sqrt(E) + c + d sqrt(E) + ... """ + def __init__(self, name): self.name = name self.spacing = None @@ -798,7 +853,7 @@ def name(self): @name.setter def name(self, name): - cv.check_type('name', name, str) + cv.check_type("name", name, str) self._name = name @property @@ -828,8 +883,8 @@ def spacing(self): @spacing.setter def spacing(self, spacing): if spacing is not None: - cv.check_type('spacing', spacing, Real) - cv.check_greater_than('spacing', spacing, 0.0, equality=False) + cv.check_type("spacing", spacing, Real) + cv.check_greater_than("spacing", spacing, 0.0, equality=False) self._spacing = spacing @property @@ -839,8 +894,8 @@ def sqrtAWR(self): @sqrtAWR.setter def sqrtAWR(self, sqrtAWR): if sqrtAWR is not None: - cv.check_type('sqrtAWR', sqrtAWR, Real) - cv.check_greater_than('sqrtAWR', sqrtAWR, 0.0, equality=False) + cv.check_type("sqrtAWR", sqrtAWR, Real) + cv.check_greater_than("sqrtAWR", sqrtAWR, 0.0, equality=False) self._sqrtAWR = sqrtAWR @property @@ -850,8 +905,8 @@ def E_min(self): @E_min.setter def E_min(self, E_min): if E_min is not None: - cv.check_type('E_min', E_min, Real) - cv.check_greater_than('E_min', E_min, 0.0, equality=True) + cv.check_type("E_min", E_min, Real) + cv.check_greater_than("E_min", E_min, 0.0, equality=True) self._E_min = E_min @property @@ -861,8 +916,8 @@ def E_max(self): @E_max.setter def E_max(self, E_max): if E_max is not None: - cv.check_type('E_max', E_max, Real) - cv.check_greater_than('E_max', E_max, 0.0, equality=False) + cv.check_type("E_max", E_max, Real) + cv.check_greater_than("E_max", E_max, 0.0, equality=False) self._E_max = E_max @property @@ -872,16 +927,17 @@ def data(self): @data.setter def data(self, data): if data is not None: - cv.check_type('data', data, np.ndarray) + cv.check_type("data", data, np.ndarray) if len(data.shape) != 2: - raise ValueError('Multipole data arrays must be 2D') + raise ValueError("Multipole data arrays must be 2D") if data.shape[1] not in (3, 4): raise ValueError( - 'data.shape[1] must be 3 or 4. One value for the pole.' - ' One each for the scattering and absorption residues. ' - 'Possibly one more for a fission residue.') + "data.shape[1] must be 3 or 4. One value for the pole." + " One each for the scattering and absorption residues. " + "Possibly one more for a fission residue." + ) if not np.issubdtype(data.dtype, np.complexfloating): - raise TypeError('Multipole data arrays must be complex dtype') + raise TypeError("Multipole data arrays must be complex dtype") self._data = data @property @@ -891,12 +947,11 @@ def windows(self): @windows.setter def windows(self, windows): if windows is not None: - cv.check_type('windows', windows, np.ndarray) + cv.check_type("windows", windows, np.ndarray) if len(windows.shape) != 2: - raise ValueError('Multipole windows arrays must be 2D') + raise ValueError("Multipole windows arrays must be 2D") if not np.issubdtype(windows.dtype, np.integer): - raise TypeError('Multipole windows arrays must be integer' - ' dtype') + raise TypeError("Multipole windows arrays must be integer" " dtype") self._windows = windows @property @@ -906,12 +961,13 @@ def broaden_poly(self): @broaden_poly.setter def broaden_poly(self, broaden_poly): if broaden_poly is not None: - cv.check_type('broaden_poly', broaden_poly, np.ndarray) + cv.check_type("broaden_poly", broaden_poly, np.ndarray) if len(broaden_poly.shape) != 1: - raise ValueError('Multipole broaden_poly arrays must be 1D') + raise ValueError("Multipole broaden_poly arrays must be 1D") if not np.issubdtype(broaden_poly.dtype, np.bool_): - raise TypeError('Multipole broaden_poly arrays must be boolean' - ' dtype') + raise TypeError( + "Multipole broaden_poly arrays must be boolean" " dtype" + ) self._broaden_poly = broaden_poly @property @@ -921,14 +977,16 @@ def curvefit(self): @curvefit.setter def curvefit(self, curvefit): if curvefit is not None: - cv.check_type('curvefit', curvefit, np.ndarray) + cv.check_type("curvefit", curvefit, np.ndarray) if len(curvefit.shape) != 3: - raise ValueError('Multipole curvefit arrays must be 3D') + raise ValueError("Multipole curvefit arrays must be 3D") if curvefit.shape[2] not in (2, 3): # sig_s, sig_a (maybe sig_f) - raise ValueError('The third dimension of multipole curvefit' - ' arrays must have a length of 2 or 3') + raise ValueError( + "The third dimension of multipole curvefit" + " arrays must have a length of 2 or 3" + ) if not np.issubdtype(curvefit.dtype, np.floating): - raise TypeError('Multipole curvefit arrays must be float dtype') + raise TypeError("Multipole curvefit arrays must be float dtype") self._curvefit = curvefit @classmethod @@ -954,22 +1012,25 @@ def from_hdf5(cls, group_or_filename): group = group_or_filename need_to_close = False else: - h5file = h5py.File(str(group_or_filename), 'r') + h5file = h5py.File(str(group_or_filename), "r") need_to_close = True # Make sure version matches - if 'version' in h5file.attrs: - major, minor = h5file.attrs['version'] + if "version" in h5file.attrs: + major, minor = h5file.attrs["version"] if major != WMP_VERSION_MAJOR: raise DataError( - 'WMP data format uses version {}. {} whereas your ' - 'installation of the OpenMC Python API expects version ' - '{}.x.'.format(major, minor, WMP_VERSION_MAJOR)) + "WMP data format uses version {}. {} whereas your " + "installation of the OpenMC Python API expects version " + "{}.x.".format(major, minor, WMP_VERSION_MAJOR) + ) else: raise DataError( - 'WMP data does not indicate a version. Your installation of ' - 'the OpenMC Python API expects version {}.x data.' - .format(WMP_VERSION_MAJOR)) + "WMP data does not indicate a version. Your installation of " + "the OpenMC Python API expects version {}.x data.".format( + WMP_VERSION_MAJOR + ) + ) group = list(h5file.values())[0] @@ -978,31 +1039,33 @@ def from_hdf5(cls, group_or_filename): # Read scalars. - out.spacing = group['spacing'][()] - out.sqrtAWR = group['sqrtAWR'][()] - out.E_min = group['E_min'][()] - out.E_max = group['E_max'][()] + out.spacing = group["spacing"][()] + out.sqrtAWR = group["sqrtAWR"][()] + out.E_min = group["E_min"][()] + out.E_max = group["E_max"][()] # Read arrays. err = "WMP '{}' array shape is not consistent with the '{}' array shape" - out.data = group['data'][()] + out.data = group["data"][()] - out.windows = group['windows'][()] + out.windows = group["windows"][()] - out.broaden_poly = group['broaden_poly'][...].astype(bool) + out.broaden_poly = group["broaden_poly"][...].astype(bool) if out.broaden_poly.shape[0] != out.windows.shape[0]: - raise ValueError(err.format('broaden_poly', 'windows')) + raise ValueError(err.format("broaden_poly", "windows")) - out.curvefit = group['curvefit'][()] + out.curvefit = group["curvefit"][()] if out.curvefit.shape[0] != out.windows.shape[0]: - raise ValueError(err.format('curvefit', 'windows')) + raise ValueError(err.format("curvefit", "windows")) # _broaden_wmp_polynomials assumes the curve fit has at least 3 terms. if out.fit_order < 2: - raise ValueError("Windowed multipole is only supported for " - "curvefits with 3 or more terms.") + raise ValueError( + "Windowed multipole is only supported for " + "curvefits with 3 or more terms." + ) # If HDF5 file was opened here, make sure it gets closed if need_to_close: @@ -1079,11 +1142,11 @@ def from_multipole(cls, mp_data, search=None, log=False, **kwargs): if isinstance(mp_data, str): # load multipole data from file - with open(mp_data, 'rb') as f: + with open(mp_data, "rb") as f: mp_data = pickle.load(f) if search is None: - if 'n_cf' in kwargs and ('n_win' in kwargs or 'spacing' in kwargs): + if "n_cf" in kwargs and ("n_win" in kwargs or "spacing" in kwargs): search = False else: search = True @@ -1091,7 +1154,7 @@ def from_multipole(cls, mp_data, search=None, log=False, **kwargs): # windowing with specific options if not search: # set default value for curvefit order if not specified - if 'n_cf' not in kwargs: + if "n_cf" not in kwargs: kwargs.update(n_cf=5) return _windowing(mp_data, log=log, **kwargs) @@ -1115,14 +1178,17 @@ def from_multipole(cls, mp_data, search=None, log=False, **kwargs): wmp = _windowing(mp_data, log=log, **kwargs) except Exception as e: if log: - print('Failed: ' + str(e)) + print("Failed: " + str(e)) break # select wmp library with metric: # - performance: average # used poles per window and CF order # - memory: # windows - metric = -(wmp.poles_per_window * 10. + wmp.fit_order * 1. + - wmp.n_windows * 0.01) + metric = -( + wmp.poles_per_window * 10.0 + + wmp.fit_order * 1.0 + + wmp.n_windows * 0.01 + ) if best_wmp is None or metric > best_metric: if log: print("Best library so far.") @@ -1131,9 +1197,15 @@ def from_multipole(cls, mp_data, search=None, log=False, **kwargs): # return the best wmp library if log: - print("Final library: {} poles, {} windows, {:.2g} poles per window, " - "{} CF order".format(best_wmp.n_poles, best_wmp.n_windows, - best_wmp.poles_per_window, best_wmp.fit_order)) + print( + "Final library: {} poles, {} windows, {:.2g} poles per window, " + "{} CF order".format( + best_wmp.n_poles, + best_wmp.n_windows, + best_wmp.poles_per_window, + best_wmp.fit_order, + ) + ) return best_wmp @@ -1155,8 +1227,10 @@ def _evaluate(self, E, T): """ - if E < self.E_min: return (0, 0, 0) - if E > self.E_max: return (0, 0, 0) + if E < self.E_min: + return (0, 0, 0) + if E > self.E_max: + return (0, 0, 0) # ====================================================================== # Bookkeeping @@ -1171,8 +1245,9 @@ def _evaluate(self, E, T): # indexing. Similarly startw needs to be decreased by 1. endw does # not need to be decreased because range(startw, endw) does not include # endw. - i_window = min(self.n_windows - 1, - int(np.floor((sqrtE - sqrt(self.E_min)) / self.spacing))) + i_window = min( + self.n_windows - 1, int(np.floor((sqrtE - sqrt(self.E_min)) / self.spacing)) + ) startw = self.windows[i_window, 0] - 1 endw = self.windows[i_window, 1] @@ -1187,16 +1262,23 @@ def _evaluate(self, E, T): if sqrtkT != 0 and self.broaden_poly[i_window]: # Broaden the curvefit. dopp = self.sqrtAWR / sqrtkT - broadened_polynomials = _broaden_wmp_polynomials(E, dopp, - self.fit_order + 1) + broadened_polynomials = _broaden_wmp_polynomials( + E, dopp, self.fit_order + 1 + ) for i_poly in range(self.fit_order + 1): - sig_s += (self.curvefit[i_window, i_poly, _FIT_S] - * broadened_polynomials[i_poly]) - sig_a += (self.curvefit[i_window, i_poly, _FIT_A] - * broadened_polynomials[i_poly]) + sig_s += ( + self.curvefit[i_window, i_poly, _FIT_S] + * broadened_polynomials[i_poly] + ) + sig_a += ( + self.curvefit[i_window, i_poly, _FIT_A] + * broadened_polynomials[i_poly] + ) if self.fissionable: - sig_f += (self.curvefit[i_window, i_poly, _FIT_F] - * broadened_polynomials[i_poly]) + sig_f += ( + self.curvefit[i_window, i_poly, _FIT_F] + * broadened_polynomials[i_poly] + ) else: temp = invE for i_poly in range(self.fit_order + 1): @@ -1253,7 +1335,7 @@ def __call__(self, E, T): fun = np.vectorize(lambda x: self._evaluate(x, T)) return fun(E) - def export_to_hdf5(self, path, mode='a', libver='earliest'): + def export_to_hdf5(self, path, mode="a", libver="earliest"): """Export windowed multipole data to an HDF5 file. Parameters @@ -1271,20 +1353,19 @@ def export_to_hdf5(self, path, mode='a', libver='earliest'): # Open file and write version. with h5py.File(str(path), mode, libver=libver) as f: - f.attrs['filetype'] = np.bytes_('data_wmp') - f.attrs['version'] = np.array(WMP_VERSION) + f.attrs["filetype"] = np.bytes_("data_wmp") + f.attrs["version"] = np.array(WMP_VERSION) g = f.create_group(self.name) # Write scalars. - g.create_dataset('spacing', data=np.array(self.spacing)) - g.create_dataset('sqrtAWR', data=np.array(self.sqrtAWR)) - g.create_dataset('E_min', data=np.array(self.E_min)) - g.create_dataset('E_max', data=np.array(self.E_max)) + g.create_dataset("spacing", data=np.array(self.spacing)) + g.create_dataset("sqrtAWR", data=np.array(self.sqrtAWR)) + g.create_dataset("E_min", data=np.array(self.E_min)) + g.create_dataset("E_max", data=np.array(self.E_max)) # Write arrays. - g.create_dataset('data', data=self.data) - g.create_dataset('windows', data=self.windows) - g.create_dataset('broaden_poly', - data=self.broaden_poly.astype(np.int8)) - g.create_dataset('curvefit', data=self.curvefit) + g.create_dataset("data", data=self.data) + g.create_dataset("windows", data=self.windows) + g.create_dataset("broaden_poly", data=self.broaden_poly.astype(np.int8)) + g.create_dataset("curvefit", data=self.curvefit) diff --git a/openmc/data/nbody.py b/openmc/data/nbody.py index ec1ac25c0fd..aea106e3d6c 100644 --- a/openmc/data/nbody.py +++ b/openmc/data/nbody.py @@ -6,6 +6,7 @@ from .angle_energy import AngleEnergy from .endf import get_cont_record + class NBodyPhaseSpace(AngleEnergy): """N-body phase space distribution @@ -45,9 +46,9 @@ def total_mass(self): @total_mass.setter def total_mass(self, total_mass): - name = 'N-body phase space total mass' + name = "N-body phase space total mass" cv.check_type(name, total_mass, Real) - cv.check_greater_than(name, total_mass, 0.) + cv.check_greater_than(name, total_mass, 0.0) self._total_mass = total_mass @property @@ -56,7 +57,7 @@ def n_particles(self): @n_particles.setter def n_particles(self, n_particles): - name = 'N-body phase space number of particles' + name = "N-body phase space number of particles" cv.check_type(name, n_particles, Integral) cv.check_greater_than(name, n_particles, 0) self._n_particles = n_particles @@ -67,7 +68,7 @@ def atomic_weight_ratio(self): @atomic_weight_ratio.setter def atomic_weight_ratio(self, atomic_weight_ratio): - name = 'N-body phase space atomic weight ratio' + name = "N-body phase space atomic weight ratio" cv.check_type(name, atomic_weight_ratio, Real) cv.check_greater_than(name, atomic_weight_ratio, 0.0) self._atomic_weight_ratio = atomic_weight_ratio @@ -78,7 +79,7 @@ def q_value(self): @q_value.setter def q_value(self, q_value): - name = 'N-body phase space Q value' + name = "N-body phase space Q value" cv.check_type(name, q_value, Real) self._q_value = q_value @@ -91,11 +92,11 @@ def to_hdf5(self, group): HDF5 group to write to """ - group.attrs['type'] = np.bytes_('nbody') - group.attrs['total_mass'] = self.total_mass - group.attrs['n_particles'] = self.n_particles - group.attrs['atomic_weight_ratio'] = self.atomic_weight_ratio - group.attrs['q_value'] = self.q_value + group.attrs["type"] = np.bytes_("nbody") + group.attrs["total_mass"] = self.total_mass + group.attrs["n_particles"] = self.n_particles + group.attrs["atomic_weight_ratio"] = self.atomic_weight_ratio + group.attrs["q_value"] = self.q_value @classmethod def from_hdf5(cls, group): @@ -112,10 +113,10 @@ def from_hdf5(cls, group): N-body phase space distribution """ - total_mass = group.attrs['total_mass'] - n_particles = group.attrs['n_particles'] - awr = group.attrs['atomic_weight_ratio'] - q_value = group.attrs['q_value'] + total_mass = group.attrs["total_mass"] + n_particles = group.attrs["n_particles"] + awr = group.attrs["atomic_weight_ratio"] + q_value = group.attrs["q_value"] return cls(total_mass, n_particles, awr, q_value) @classmethod diff --git a/openmc/data/neutron.py b/openmc/data/neutron.py index 95a3424ea4d..44ad1cf3d46 100644 --- a/openmc/data/neutron.py +++ b/openmc/data/neutron.py @@ -13,7 +13,12 @@ from .ace import Library, Table, get_table, get_metadata from .data import ATOMIC_SYMBOL, K_BOLTZMANN, EV_PER_MEV from .endf import ( - Evaluation, SUM_RULES, get_head_record, get_tab1_record, get_evaluations) + Evaluation, + SUM_RULES, + get_head_record, + get_tab1_record, + get_evaluations, +) from .fission_energy import FissionEnergyRelease from .function import Tabulated1D, Sum, ResonancesWithBackground from .njoy import make_ace, make_pendf @@ -95,8 +100,9 @@ class IncidentNeutron(EqualityMixin): """ - def __init__(self, name, atomic_number, mass_number, metastable, - atomic_weight_ratio, kTs): + def __init__( + self, name, atomic_number, mass_number, metastable, atomic_weight_ratio, kTs + ): self.name = name self.atomic_number = atomic_number self.mass_number = mass_number @@ -121,7 +127,7 @@ def __getitem__(self, mt): if len(mts) > 0: return self._get_redundant_reaction(mt, mts) else: - raise KeyError(f'No reaction with MT={mt}.') + raise KeyError(f"No reaction with MT={mt}.") def __repr__(self): return f"" @@ -135,7 +141,7 @@ def name(self): @name.setter def name(self, name): - cv.check_type('name', name, str) + cv.check_type("name", name, str) self._name = name @property @@ -144,8 +150,8 @@ def atomic_number(self): @atomic_number.setter def atomic_number(self, atomic_number): - cv.check_type('atomic number', atomic_number, Integral) - cv.check_greater_than('atomic number', atomic_number, 0, True) + cv.check_type("atomic number", atomic_number, Integral) + cv.check_greater_than("atomic number", atomic_number, 0, True) self._atomic_number = atomic_number @property @@ -154,8 +160,8 @@ def mass_number(self): @mass_number.setter def mass_number(self, mass_number): - cv.check_type('mass number', mass_number, Integral) - cv.check_greater_than('mass number', mass_number, 0, True) + cv.check_type("mass number", mass_number, Integral) + cv.check_greater_than("mass number", mass_number, 0, True) self._mass_number = mass_number @property @@ -164,8 +170,8 @@ def metastable(self): @metastable.setter def metastable(self, metastable): - cv.check_type('metastable', metastable, Integral) - cv.check_greater_than('metastable', metastable, 0, True) + cv.check_type("metastable", metastable, Integral) + cv.check_greater_than("metastable", metastable, 0, True) self._metastable = metastable @property @@ -174,8 +180,8 @@ def atomic_weight_ratio(self): @atomic_weight_ratio.setter def atomic_weight_ratio(self, atomic_weight_ratio): - cv.check_type('atomic weight ratio', atomic_weight_ratio, Real) - cv.check_greater_than('atomic weight ratio', atomic_weight_ratio, 0.0) + cv.check_type("atomic weight ratio", atomic_weight_ratio, Real) + cv.check_greater_than("atomic weight ratio", atomic_weight_ratio, 0.0) self._atomic_weight_ratio = atomic_weight_ratio @property @@ -184,8 +190,7 @@ def fission_energy(self): @fission_energy.setter def fission_energy(self, fission_energy): - cv.check_type('fission energy release', fission_energy, - FissionEnergyRelease) + cv.check_type("fission energy release", fission_energy, FissionEnergyRelease) self._fission_energy = fission_energy @property @@ -194,7 +199,7 @@ def reactions(self): @reactions.setter def reactions(self, reactions): - cv.check_type('reactions', reactions, Mapping) + cv.check_type("reactions", reactions, Mapping) self._reactions = reactions @property @@ -203,7 +208,7 @@ def resonances(self): @resonances.setter def resonances(self, resonances): - cv.check_type('resonances', resonances, res.Resonances) + cv.check_type("resonances", resonances, res.Resonances) self._resonances = resonances @property @@ -212,8 +217,9 @@ def resonance_covariance(self): @resonance_covariance.setter def resonance_covariance(self, resonance_covariance): - cv.check_type('resonance covariance', resonance_covariance, - res_cov.ResonanceCovariances) + cv.check_type( + "resonance covariance", resonance_covariance, res_cov.ResonanceCovariances + ) self._resonance_covariance = resonance_covariance @property @@ -222,10 +228,10 @@ def urr(self): @urr.setter def urr(self, urr): - cv.check_type('probability table dictionary', urr, MutableMapping) + cv.check_type("probability table dictionary", urr, MutableMapping) for key, value in urr: - cv.check_type('probability table temperature', key, str) - cv.check_type('probability tables', value, ProbabilityTables) + cv.check_type("probability table temperature", key, str) + cv.check_type("probability tables", value, ProbabilityTables) self._urr = urr @property @@ -236,7 +242,7 @@ def temperatures(self): def atomic_symbol(self): return ATOMIC_SYMBOL[self.atomic_number] - def add_temperature_from_ace(self, ace_or_filename, metastable_scheme='nndc'): + def add_temperature_from_ace(self, ace_or_filename, metastable_scheme="nndc"): """Append data from an ACE file at a different temperature. Parameters @@ -260,12 +266,12 @@ def add_temperature_from_ace(self, ace_or_filename, metastable_scheme='nndc'): # Check if temprature already exists strT = data.temperatures[0] if strT in self.temperatures: - warn(f'Cross sections at T={strT} already exist.') + warn(f"Cross sections at T={strT} already exist.") return # Check that name matches if data.name != self.name: - raise ValueError('Data provided for an incorrect nuclide.') + raise ValueError("Data provided for an incorrect nuclide.") # Add temperature self.kTs += data.kTs @@ -278,8 +284,10 @@ def add_temperature_from_ace(self, ace_or_filename, metastable_scheme='nndc'): if mt in self: self[mt].xs[strT] = data[mt].xs[strT] else: - warn("Tried to add cross sections for MT={} at T={} but this " - "reaction doesn't exist.".format(mt, strT)) + warn( + "Tried to add cross sections for MT={} at T={} but this " + "reaction doesn't exist.".format(mt, strT) + ) # Add probability tables if strT in data.urr: @@ -307,14 +315,14 @@ def add_elastic_0K_from_endf(self, filename, overwrite=False, **kwargs): """ # Check for existing data - if '0K' in self.energy and not overwrite: - raise ValueError('0 K data already exists for this nuclide.') + if "0K" in self.energy and not overwrite: + raise ValueError("0 K data already exists for this nuclide.") with tempfile.TemporaryDirectory() as tmpdir: # Set arguments for make_pendf - pendf_path = os.path.join(tmpdir, 'pendf') - kwargs.setdefault('output_dir', tmpdir) - kwargs.setdefault('pendf', pendf_path) + pendf_path = os.path.join(tmpdir, "pendf") + kwargs.setdefault("output_dir", tmpdir) + kwargs.setdefault("pendf", pendf_path) # Run NJOY to create a pointwise ENDF file make_pendf(filename, **kwargs) @@ -324,8 +332,8 @@ def add_elastic_0K_from_endf(self, filename, overwrite=False, **kwargs): file_obj = StringIO(pendf.section[3, 2]) get_head_record(file_obj) params, xs = get_tab1_record(file_obj) - self.energy['0K'] = xs.x - self[2].xs['0K'] = xs + self.energy["0K"] = xs.x + self[2].xs["0K"] = xs def get_reaction_components(self, mt): """Determine what reactions make up redundant reaction. @@ -351,7 +359,7 @@ def get_reaction_components(self, mt): else: return [mt] if mt in self else [] - def export_to_hdf5(self, path, mode='a', libver='earliest'): + def export_to_hdf5(self, path, mode="a", libver="earliest"): """Export incident neutron data to an HDF5 file. Parameters @@ -367,66 +375,83 @@ def export_to_hdf5(self, path, mode='a', libver='earliest'): """ # If data come from ENDF, don't allow exporting to HDF5 - if hasattr(self, '_evaluation'): - raise NotImplementedError('Cannot export incident neutron data that ' - 'originated from an ENDF file.') + if hasattr(self, "_evaluation"): + raise NotImplementedError( + "Cannot export incident neutron data that " + "originated from an ENDF file." + ) # Open file and write version with h5py.File(str(path), mode, libver=libver) as f: - f.attrs['filetype'] = np.bytes_('data_neutron') - f.attrs['version'] = np.array(HDF5_VERSION) + f.attrs["filetype"] = np.bytes_("data_neutron") + f.attrs["version"] = np.array(HDF5_VERSION) # Write basic data g = f.create_group(self.name) - g.attrs['Z'] = self.atomic_number - g.attrs['A'] = self.mass_number - g.attrs['metastable'] = self.metastable - g.attrs['atomic_weight_ratio'] = self.atomic_weight_ratio - ktg = g.create_group('kTs') + g.attrs["Z"] = self.atomic_number + g.attrs["A"] = self.mass_number + g.attrs["metastable"] = self.metastable + g.attrs["atomic_weight_ratio"] = self.atomic_weight_ratio + ktg = g.create_group("kTs") for i, temperature in enumerate(self.temperatures): ktg.create_dataset(temperature, data=self.kTs[i]) # Write energy grid - eg = g.create_group('energy') + eg = g.create_group("energy") for temperature in self.temperatures: eg.create_dataset(temperature, data=self.energy[temperature]) # Write 0K energy grid if needed - if '0K' in self.energy and '0K' not in eg: - eg.create_dataset('0K', data=self.energy['0K']) + if "0K" in self.energy and "0K" not in eg: + eg.create_dataset("0K", data=self.energy["0K"]) # Write reaction data - rxs_group = g.create_group('reactions') + rxs_group = g.create_group("reactions") for rx in self.reactions.values(): # Skip writing redundant reaction if it doesn't have photon # production or is a summed transmutation reaction. MT=4 is also # sometimes needed for probability tables. Also write gas # production, heating, and damage energy production. if rx.redundant: - photon_rx = any(p.particle == 'photon' for p in rx.products) - keep_mts = (4, 16, 103, 104, 105, 106, 107, - 203, 204, 205, 206, 207, 301, 444, 901) + photon_rx = any(p.particle == "photon" for p in rx.products) + keep_mts = ( + 4, + 16, + 103, + 104, + 105, + 106, + 107, + 203, + 204, + 205, + 206, + 207, + 301, + 444, + 901, + ) if not (photon_rx or rx.mt in keep_mts): continue - rx_group = rxs_group.create_group(f'reaction_{rx.mt:03}') + rx_group = rxs_group.create_group(f"reaction_{rx.mt:03}") rx.to_hdf5(rx_group) # Write total nu data if available - if len(rx.derived_products) > 0 and 'total_nu' not in g: - tgroup = g.create_group('total_nu') + if len(rx.derived_products) > 0 and "total_nu" not in g: + tgroup = g.create_group("total_nu") rx.derived_products[0].to_hdf5(tgroup) # Write unresolved resonance probability tables if self.urr: - urr_group = g.create_group('urr') + urr_group = g.create_group("urr") for temperature, urr in self.urr.items(): tgroup = urr_group.create_group(temperature) urr.to_hdf5(tgroup) # Write fission energy release data if self.fission_energy is not None: - fer_group = g.create_group('fission_energy_release') + fer_group = g.create_group("fission_energy_release") self.fission_energy.to_hdf5(fer_group) @classmethod @@ -449,65 +474,68 @@ def from_hdf5(cls, group_or_filename): if isinstance(group_or_filename, h5py.Group): group = group_or_filename else: - h5file = h5py.File(str(group_or_filename), 'r') + h5file = h5py.File(str(group_or_filename), "r") # Make sure version matches - if 'version' in h5file.attrs: - major, minor = h5file.attrs['version'] + if "version" in h5file.attrs: + major, minor = h5file.attrs["version"] # For now all versions of HDF5 data can be read else: raise IOError( - 'HDF5 data does not indicate a version. Your installation of ' - 'the OpenMC Python API expects version {}.x data.' - .format(HDF5_VERSION_MAJOR)) + "HDF5 data does not indicate a version. Your installation of " + "the OpenMC Python API expects version {}.x data.".format( + HDF5_VERSION_MAJOR + ) + ) group = list(h5file.values())[0] name = group.name[1:] - atomic_number = group.attrs['Z'] - mass_number = group.attrs['A'] - metastable = group.attrs['metastable'] - atomic_weight_ratio = group.attrs['atomic_weight_ratio'] - kTg = group['kTs'] + atomic_number = group.attrs["Z"] + mass_number = group.attrs["A"] + metastable = group.attrs["metastable"] + atomic_weight_ratio = group.attrs["atomic_weight_ratio"] + kTg = group["kTs"] kTs = [] for temp in kTg: kTs.append(kTg[temp][()]) - data = cls(name, atomic_number, mass_number, metastable, - atomic_weight_ratio, kTs) + data = cls( + name, atomic_number, mass_number, metastable, atomic_weight_ratio, kTs + ) # Read energy grid - e_group = group['energy'] + e_group = group["energy"] for temperature, dset in e_group.items(): data.energy[temperature] = dset[()] # Read reaction data - rxs_group = group['reactions'] + rxs_group = group["reactions"] for name, obj in sorted(rxs_group.items()): - if name.startswith('reaction_'): + if name.startswith("reaction_"): rx = Reaction.from_hdf5(obj, data.energy) data.reactions[rx.mt] = rx # Read total nu data if available - if rx.mt in FISSION_MTS and 'total_nu' in group: - tgroup = group['total_nu'] + if rx.mt in FISSION_MTS and "total_nu" in group: + tgroup = group["total_nu"] rx.derived_products.append(Product.from_hdf5(tgroup)) # Read unresolved resonance probability tables - if 'urr' in group: - urr_group = group['urr'] + if "urr" in group: + urr_group = group["urr"] for temperature, tgroup in urr_group.items(): data.urr[temperature] = ProbabilityTables.from_hdf5(tgroup) # Read fission energy release data - if 'fission_energy_release' in group: - fer_group = group['fission_energy_release'] + if "fission_energy_release" in group: + fer_group = group["fission_energy_release"] data.fission_energy = FissionEnergyRelease.from_hdf5(fer_group) return data @classmethod - def from_ace(cls, ace_or_filename, metastable_scheme='nndc'): + def from_ace(cls, ace_or_filename, metastable_scheme="nndc"): """Generate incident neutron continuous-energy data from an ACE table Parameters @@ -538,18 +566,17 @@ def from_ace(cls, ace_or_filename, metastable_scheme='nndc'): ace = get_table(ace_or_filename) # If mass number hasn't been specified, make an educated guess - zaid, xs = ace.name.split('.') - if not xs.endswith('c'): - raise TypeError( - f"{ace} is not a continuous-energy neutron ACE table.") - name, element, Z, mass_number, metastable = \ - get_metadata(int(zaid), metastable_scheme) + zaid, xs = ace.name.split(".") + if not xs.endswith("c"): + raise TypeError(f"{ace} is not a continuous-energy neutron ACE table.") + name, element, Z, mass_number, metastable = get_metadata( + int(zaid), metastable_scheme + ) # Assign temperature to the running list - kTs = [ace.temperature*EV_PER_MEV] + kTs = [ace.temperature * EV_PER_MEV] - data = cls(name, Z, mass_number, metastable, - ace.atomic_weight_ratio, kTs) + data = cls(name, Z, mass_number, metastable, ace.atomic_weight_ratio, kTs) # Get string of temperature to use as a dictionary key strT = data.temperatures[0] @@ -557,11 +584,11 @@ def from_ace(cls, ace_or_filename, metastable_scheme='nndc'): # Read energy grid n_energy = ace.nxs[3] i = ace.jxs[1] - energy = ace.xss[i : i + n_energy]*EV_PER_MEV + energy = ace.xss[i : i + n_energy] * EV_PER_MEV data.energy[strT] = energy - total_xs = ace.xss[i + n_energy : i + 2*n_energy] - absorption_xs = ace.xss[i + 2*n_energy : i + 3*n_energy] - heating_number = ace.xss[i + 4*n_energy : i + 5*n_energy]*EV_PER_MEV + total_xs = ace.xss[i + n_energy : i + 2 * n_energy] + absorption_xs = ace.xss[i + 2 * n_energy : i + 3 * n_energy] + heating_number = ace.xss[i + 4 * n_energy : i + 5 * n_energy] * EV_PER_MEV # Create redundant reaction for total (MT=1) total = Reaction(1) @@ -578,7 +605,7 @@ def from_ace(cls, ace_or_filename, metastable_scheme='nndc'): # Create redundant reaction for heating (MT=301) heating = Reaction(301) - heating.xs[strT] = Tabulated1D(energy, heating_number*total_xs) + heating.xs[strT] = Tabulated1D(energy, heating_number * total_xs) heating.redundant = True data.reactions[301] = heating @@ -592,21 +619,24 @@ def from_ace(cls, ace_or_filename, metastable_scheme='nndc'): # exist, usually MT=4. In this case, we create a new reaction and add # them n_photon_reactions = ace.nxs[6] - photon_mts = ace.xss[ace.jxs[13]:ace.jxs[13] + - n_photon_reactions].astype(int) + photon_mts = ace.xss[ace.jxs[13] : ace.jxs[13] + n_photon_reactions].astype(int) for mt in np.unique(photon_mts // 1000): if mt not in data: if mt not in SUM_RULES: - warn('Photon production is present for MT={} but no ' - 'cross section is given.'.format(mt)) + warn( + "Photon production is present for MT={} but no " + "cross section is given.".format(mt) + ) continue # Create redundant reaction with appropriate cross section mts = data.get_reaction_components(mt) if len(mts) == 0: - warn('Photon production is present for MT={} but no ' - 'reaction components exist.'.format(mt)) + warn( + "Photon production is present for MT={} but no " + "reaction components exist.".format(mt) + ) continue # Determine redundant cross section @@ -671,29 +701,35 @@ def from_endf(cls, ev_or_filename, covariance=False): else: ev = Evaluation(ev_or_filename) - atomic_number = ev.target['atomic_number'] - mass_number = ev.target['mass_number'] - metastable = ev.target['isomeric_state'] - atomic_weight_ratio = ev.target['mass'] - temperature = ev.target['temperature'] + atomic_number = ev.target["atomic_number"] + mass_number = ev.target["mass_number"] + metastable = ev.target["isomeric_state"] + atomic_weight_ratio = ev.target["mass"] + temperature = ev.target["temperature"] # Determine name element = ATOMIC_SYMBOL[atomic_number] if metastable > 0: - name = f'{element}{mass_number}_m{metastable}' + name = f"{element}{mass_number}_m{metastable}" else: - name = f'{element}{mass_number}' + name = f"{element}{mass_number}" # Instantiate incident neutron data - data = cls(name, atomic_number, mass_number, metastable, - atomic_weight_ratio, [temperature]) + data = cls( + name, + atomic_number, + mass_number, + metastable, + atomic_weight_ratio, + [temperature], + ) if (2, 151) in ev.section: data.resonances = res.Resonances.from_endf(ev) if (32, 151) in ev.section and covariance: - data.resonance_covariance = ( - res_cov.ResonanceCovariances.from_endf(ev, data.resonances) + data.resonance_covariance = res_cov.ResonanceCovariances.from_endf( + ev, data.resonances ) # Read each reaction @@ -707,8 +743,9 @@ def from_endf(cls, ev_or_filename, covariance=False): for mt in (2, 102, 18): if mt in data.reactions: rx = data.reactions[mt] - rx.xs['0K'] = ResonancesWithBackground( - data.resonances, rx.xs['0K'], mt) + rx.xs["0K"] = ResonancesWithBackground( + data.resonances, rx.xs["0K"], mt + ) except ValueError: # Thrown if multiple resolved ranges (e.g. Pu239 in ENDF/B-VII.1) pass @@ -760,23 +797,23 @@ def from_njoy(cls, filename, temperatures=None, evaluation=None, **kwargs): kwargs.setdefault("output_dir", tmpdir) for key in ("acer", "pendf", "heatr", "broadr", "gaspr", "purr"): kwargs.setdefault(key, os.path.join(kwargs["output_dir"], key)) - kwargs['evaluation'] = evaluation + kwargs["evaluation"] = evaluation make_ace(filename, temperatures, **kwargs) # Create instance from ACE tables within library - lib = Library(kwargs['acer']) + lib = Library(kwargs["acer"]) data = cls.from_ace(lib.tables[0]) for table in lib.tables[1:]: data.add_temperature_from_ace(table) # Add 0K elastic scattering cross section - if '0K' not in data.energy: - pendf = Evaluation(kwargs['pendf']) + if "0K" not in data.energy: + pendf = Evaluation(kwargs["pendf"]) file_obj = StringIO(pendf.section[3, 2]) get_head_record(file_obj) params, xs = get_tab1_record(file_obj) - data.energy['0K'] = xs.x - data[2].xs['0K'] = xs + data.energy["0K"] = xs.x + data[2].xs["0K"] = xs # Add fission energy release data ev = evaluation if evaluation is not None else Evaluation(filename) @@ -816,7 +853,9 @@ def get_file3_xs(ev, mt, E): heatr_evals = get_evaluations(kwargs["heatr"]) heatr_local_evals = get_evaluations(kwargs["heatr"] + "_local") - for ev, ev_local, temp in zip(heatr_evals, heatr_local_evals, data.temperatures): + for ev, ev_local, temp in zip( + heatr_evals, heatr_local_evals, data.temperatures + ): # Get total KERMA (originally from ACE file) and energy grid kerma = data.reactions[301].xs[temp] E = kerma.x @@ -825,8 +864,11 @@ def get_file3_xs(ev, mt, E): # Replace fission KERMA with (EFR + EB)*sigma_f fission = data[18].xs[temp] kerma_fission = get_file3_xs(ev, 318, E) - kerma.y = kerma.y - kerma_fission + ( - f.fragments(E) + f.betas(E)) * fission(E) + kerma.y = ( + kerma.y + - kerma_fission + + (f.fragments(E) + f.betas(E)) * fission(E) + ) # For local KERMA, we first need to get the values from the # HEATR run with photon energy deposited locally and put @@ -837,9 +879,17 @@ def get_file3_xs(ev, mt, E): # When photons deposit their energy locally, we replace the # fission KERMA with (EFR + EGP + EGD + EB)*sigma_f kerma_fission_local = get_file3_xs(ev_local, 318, E) - kerma_local = kerma_local - kerma_fission_local + ( - f.fragments(E) + f.prompt_photons(E) - + f.delayed_photons(E) + f.betas(E))*fission(E) + kerma_local = ( + kerma_local + - kerma_fission_local + + ( + f.fragments(E) + + f.prompt_photons(E) + + f.delayed_photons(E) + + f.betas(E) + ) + * fission(E) + ) heating_local.xs[temp] = Tabulated1D(E, kerma_local) @@ -869,8 +919,12 @@ def _get_redundant_reaction(self, mt, mts): for strT in self.temperatures: energy = self.energy[strT] xss = [self.reactions[mt_i].xs[strT] for mt_i in mts] - idx = min([xs._threshold_idx if hasattr(xs, '_threshold_idx') - else 0 for xs in xss]) + idx = min( + [ + xs._threshold_idx if hasattr(xs, "_threshold_idx") else 0 + for xs in xss + ] + ) rx.xs[strT] = Tabulated1D(energy[idx:], Sum(xss)(energy[idx:])) rx.xs[strT]._threshold_idx = idx diff --git a/openmc/data/njoy.py b/openmc/data/njoy.py index 1bf44891ef4..b7a2679bd27 100644 --- a/openmc/data/njoy.py +++ b/openmc/data/njoy.py @@ -13,63 +13,69 @@ # For a given material, give a name for the ACE table and a list of ZAID # identifiers. -ThermalTuple = namedtuple('ThermalTuple', ['name', 'zaids', 'nmix']) +ThermalTuple = namedtuple("ThermalTuple", ["name", "zaids", "nmix"]) _THERMAL_DATA = { - 'c_Al27': ThermalTuple('al27', [13027], 1), - 'c_Al_in_Al2O3': ThermalTuple('asap00', [13027], 1), - 'c_Be': ThermalTuple('be', [4009], 1), - 'c_Be_in_BeO': ThermalTuple('bebeo', [4009], 1), - 'c_Be_in_Be2C': ThermalTuple('bebe2c', [4009], 1), - 'c_Be_in_FLiBe': ThermalTuple('beflib', [4009], 1), - 'c_C6H6': ThermalTuple('benz', [1001, 6000, 6012], 2), - 'c_C_in_SiC': ThermalTuple('csic', [6000, 6012, 6013], 1), - 'c_Ca_in_CaH2': ThermalTuple('cacah2', [20040, 20042, 20043, 20044, 20046, 20048], 1), - 'c_D_in_D2O': ThermalTuple('dd2o', [1002], 1), - 'c_D_in_D2O_solid': ThermalTuple('dice', [1002], 1), - 'c_F_in_FLiBe': ThermalTuple('fflibe', [9019], 1), - 'c_Fe56': ThermalTuple('fe56', [26056], 1), - 'c_Graphite': ThermalTuple('graph', [6000, 6012, 6013], 1), - 'c_Graphite_10p': ThermalTuple('grph10', [6000, 6012, 6013], 1), - 'c_Graphite_30p': ThermalTuple('grph30', [6000, 6012, 6013], 1), - 'c_H_in_C5O2H8': ThermalTuple('lucite', [1001], 1), - 'c_H_in_CaH2': ThermalTuple('hcah2', [1001], 1), - 'c_H_in_CH2': ThermalTuple('hch2', [1001], 1), - 'c_H_in_CH4_liquid': ThermalTuple('lch4', [1001], 1), - 'c_H_in_CH4_solid': ThermalTuple('sch4', [1001], 1), - 'c_H_in_CH4_solid_phase_II': ThermalTuple('sch4p2', [1001], 1), - 'c_H_in_H2O': ThermalTuple('hh2o', [1001], 1), - 'c_H_in_H2O_solid': ThermalTuple('hice', [1001], 1), - 'c_H_in_HF': ThermalTuple('hhf', [1001], 1), - 'c_H_in_Mesitylene': ThermalTuple('mesi00', [1001], 1), - 'c_H_in_ParaffinicOil': ThermalTuple('hparaf', [1001], 1), - 'c_H_in_Toluene': ThermalTuple('tol00', [1001], 1), - 'c_H_in_UH3': ThermalTuple('huh3', [1001], 1), - 'c_H_in_YH2': ThermalTuple('hyh2', [1001], 1), - 'c_H_in_ZrH': ThermalTuple('hzrh', [1001], 1), - 'c_H_in_ZrH2': ThermalTuple('hzrh2', [1001], 1), - 'c_H_in_ZrHx': ThermalTuple('hzrhx', [1001], 1), - 'c_Li_in_FLiBe': ThermalTuple('liflib', [3006, 3007], 1), - 'c_Mg24': ThermalTuple('mg24', [12024], 1), - 'c_N_in_UN': ThermalTuple('n-un', [7014, 7015], 1), - 'c_O_in_Al2O3': ThermalTuple('osap00', [8016, 8017, 8018], 1), - 'c_O_in_BeO': ThermalTuple('obeo', [8016, 8017, 8018], 1), - 'c_O_in_D2O': ThermalTuple('od2o', [8016, 8017, 8018], 1), - 'c_O_in_H2O_solid': ThermalTuple('oice', [8016, 8017, 8018], 1), - 'c_O_in_UO2': ThermalTuple('ouo2', [8016, 8017, 8018], 1), - 'c_ortho_D': ThermalTuple('orthod', [1002], 1), - 'c_ortho_H': ThermalTuple('orthoh', [1001], 1), - 'c_para_D': ThermalTuple('parad', [1002], 1), - 'c_para_H': ThermalTuple('parah', [1001], 1), - 'c_Si28': ThermalTuple('si00', [14028], 1), - 'c_Si_in_SiC': ThermalTuple('sisic', [14028, 14029, 14030], 1), - 'c_SiO2_alpha': ThermalTuple('sio2-a', [8016, 8017, 8018, 14028, 14029, 14030], 3), - 'c_SiO2_beta': ThermalTuple('sio2-b', [8016, 8017, 8018, 14028, 14029, 14030], 3), - 'c_U_in_UN': ThermalTuple('u-un', [92233, 92234, 92235, 92236, 92238], 1), - 'c_U_in_UO2': ThermalTuple('uuo2', [92233, 92234, 92235, 92236, 92238], 1), - 'c_Y_in_YH2': ThermalTuple('yyh2', [39089], 1), - 'c_Zr_in_ZrH': ThermalTuple('zrzrh', [40000, 40090, 40091, 40092, 40094, 40096], 1), - 'c_Zr_in_ZrH2': ThermalTuple('zrzrh2', [40000, 40090, 40091, 40092, 40094, 40096], 1), - 'c_Zr_in_ZrHx': ThermalTuple('zrzrhx', [40000, 40090, 40091, 40092, 40094, 40096], 1), + "c_Al27": ThermalTuple("al27", [13027], 1), + "c_Al_in_Al2O3": ThermalTuple("asap00", [13027], 1), + "c_Be": ThermalTuple("be", [4009], 1), + "c_Be_in_BeO": ThermalTuple("bebeo", [4009], 1), + "c_Be_in_Be2C": ThermalTuple("bebe2c", [4009], 1), + "c_Be_in_FLiBe": ThermalTuple("beflib", [4009], 1), + "c_C6H6": ThermalTuple("benz", [1001, 6000, 6012], 2), + "c_C_in_SiC": ThermalTuple("csic", [6000, 6012, 6013], 1), + "c_Ca_in_CaH2": ThermalTuple( + "cacah2", [20040, 20042, 20043, 20044, 20046, 20048], 1 + ), + "c_D_in_D2O": ThermalTuple("dd2o", [1002], 1), + "c_D_in_D2O_solid": ThermalTuple("dice", [1002], 1), + "c_F_in_FLiBe": ThermalTuple("fflibe", [9019], 1), + "c_Fe56": ThermalTuple("fe56", [26056], 1), + "c_Graphite": ThermalTuple("graph", [6000, 6012, 6013], 1), + "c_Graphite_10p": ThermalTuple("grph10", [6000, 6012, 6013], 1), + "c_Graphite_30p": ThermalTuple("grph30", [6000, 6012, 6013], 1), + "c_H_in_C5O2H8": ThermalTuple("lucite", [1001], 1), + "c_H_in_CaH2": ThermalTuple("hcah2", [1001], 1), + "c_H_in_CH2": ThermalTuple("hch2", [1001], 1), + "c_H_in_CH4_liquid": ThermalTuple("lch4", [1001], 1), + "c_H_in_CH4_solid": ThermalTuple("sch4", [1001], 1), + "c_H_in_CH4_solid_phase_II": ThermalTuple("sch4p2", [1001], 1), + "c_H_in_H2O": ThermalTuple("hh2o", [1001], 1), + "c_H_in_H2O_solid": ThermalTuple("hice", [1001], 1), + "c_H_in_HF": ThermalTuple("hhf", [1001], 1), + "c_H_in_Mesitylene": ThermalTuple("mesi00", [1001], 1), + "c_H_in_ParaffinicOil": ThermalTuple("hparaf", [1001], 1), + "c_H_in_Toluene": ThermalTuple("tol00", [1001], 1), + "c_H_in_UH3": ThermalTuple("huh3", [1001], 1), + "c_H_in_YH2": ThermalTuple("hyh2", [1001], 1), + "c_H_in_ZrH": ThermalTuple("hzrh", [1001], 1), + "c_H_in_ZrH2": ThermalTuple("hzrh2", [1001], 1), + "c_H_in_ZrHx": ThermalTuple("hzrhx", [1001], 1), + "c_Li_in_FLiBe": ThermalTuple("liflib", [3006, 3007], 1), + "c_Mg24": ThermalTuple("mg24", [12024], 1), + "c_N_in_UN": ThermalTuple("n-un", [7014, 7015], 1), + "c_O_in_Al2O3": ThermalTuple("osap00", [8016, 8017, 8018], 1), + "c_O_in_BeO": ThermalTuple("obeo", [8016, 8017, 8018], 1), + "c_O_in_D2O": ThermalTuple("od2o", [8016, 8017, 8018], 1), + "c_O_in_H2O_solid": ThermalTuple("oice", [8016, 8017, 8018], 1), + "c_O_in_UO2": ThermalTuple("ouo2", [8016, 8017, 8018], 1), + "c_ortho_D": ThermalTuple("orthod", [1002], 1), + "c_ortho_H": ThermalTuple("orthoh", [1001], 1), + "c_para_D": ThermalTuple("parad", [1002], 1), + "c_para_H": ThermalTuple("parah", [1001], 1), + "c_Si28": ThermalTuple("si00", [14028], 1), + "c_Si_in_SiC": ThermalTuple("sisic", [14028, 14029, 14030], 1), + "c_SiO2_alpha": ThermalTuple("sio2-a", [8016, 8017, 8018, 14028, 14029, 14030], 3), + "c_SiO2_beta": ThermalTuple("sio2-b", [8016, 8017, 8018, 14028, 14029, 14030], 3), + "c_U_in_UN": ThermalTuple("u-un", [92233, 92234, 92235, 92236, 92238], 1), + "c_U_in_UO2": ThermalTuple("uuo2", [92233, 92234, 92235, 92236, 92238], 1), + "c_Y_in_YH2": ThermalTuple("yyh2", [39089], 1), + "c_Zr_in_ZrH": ThermalTuple("zrzrh", [40000, 40090, 40091, 40092, 40094, 40096], 1), + "c_Zr_in_ZrH2": ThermalTuple( + "zrzrh2", [40000, 40090, 40091, 40092, 40094, 40096], 1 + ), + "c_Zr_in_ZrHx": ThermalTuple( + "zrzrhx", [40000, 40090, 40091, 40092, 40094, 40096], 1 + ), } @@ -155,8 +161,7 @@ """ -def run(commands, tapein, tapeout, input_filename=None, stdout=False, - njoy_exec='njoy'): +def run(commands, tapein, tapeout, input_filename=None, stdout=False, njoy_exec="njoy"): """Run NJOY with given commands Parameters @@ -182,18 +187,24 @@ def run(commands, tapein, tapeout, input_filename=None, stdout=False, """ if input_filename is not None: - with open(str(input_filename), 'w') as f: + with open(str(input_filename), "w") as f: f.write(commands) with tempfile.TemporaryDirectory() as tmpdir: # Copy evaluations to appropriates 'tapes' for tape_num, filename in tapein.items(): - tmpfilename = os.path.join(tmpdir, f'tape{tape_num}') + tmpfilename = os.path.join(tmpdir, f"tape{tape_num}") shutil.copy(str(filename), tmpfilename) # Start up NJOY process - njoy = Popen([njoy_exec], cwd=tmpdir, stdin=PIPE, stdout=PIPE, - stderr=STDOUT, universal_newlines=True) + njoy = Popen( + [njoy_exec], + cwd=tmpdir, + stdin=PIPE, + stdout=PIPE, + stderr=STDOUT, + universal_newlines=True, + ) njoy.stdin.write(commands) njoy.stdin.flush() @@ -207,21 +218,20 @@ def run(commands, tapein, tapeout, input_filename=None, stdout=False, lines.append(line) if stdout: # If user requested output, print to screen - print(line, end='') + print(line, end="") # Check for error if njoy.returncode != 0: - raise CalledProcessError(njoy.returncode, njoy_exec, - ''.join(lines)) + raise CalledProcessError(njoy.returncode, njoy_exec, "".join(lines)) # Copy output files back to original directory for tape_num, filename in tapeout.items(): - tmpfilename = os.path.join(tmpdir, f'tape{tape_num}') + tmpfilename = os.path.join(tmpdir, f"tape{tape_num}") if os.path.isfile(tmpfilename): shutil.move(tmpfilename, str(filename)) -def make_pendf(filename, pendf='pendf', **kwargs): +def make_pendf(filename, pendf="pendf", **kwargs): """Generate pointwise ENDF file from an ENDF file Parameters @@ -240,15 +250,27 @@ def make_pendf(filename, pendf='pendf', **kwargs): If the NJOY process returns with a non-zero status """ - for key in ('broadr', 'heatr', 'gaspr', 'purr', 'acer'): + for key in ("broadr", "heatr", "gaspr", "purr", "acer"): kwargs.setdefault(key, False) make_ace(filename, pendf=pendf, **kwargs) -def make_ace(filename, temperatures=None, acer=True, xsdir=None, - output_dir=None, pendf=False, error=0.001, broadr=True, - heatr=True, gaspr=True, purr=True, evaluation=None, - smoothing=True, **kwargs): +def make_ace( + filename, + temperatures=None, + acer=True, + xsdir=None, + output_dir=None, + pendf=False, + error=0.001, + broadr=True, + heatr=True, + gaspr=True, + purr=True, + evaluation=None, + smoothing=True, + **kwargs, +): """Generate incident neutron ACE file from an ENDF file File names can be passed to @@ -320,15 +342,15 @@ def make_ace(filename, temperatures=None, acer=True, xsdir=None, ev = evaluation if evaluation is not None else endf.Evaluation(filename) mat = ev.material - zsymam = ev.target['zsymam'] + zsymam = ev.target["zsymam"] # Determine name of library - library = '{}-{}.{}'.format(*ev.info['library']) + library = "{}-{}.{}".format(*ev.info["library"]) if temperatures is None: temperatures = [293.6] num_temp = len(temperatures) - temps = ' '.join(str(i) for i in temperatures) + temps = " ".join(str(i) for i in temperatures) # Create njoy commands by modules commands = "" @@ -354,8 +376,9 @@ def make_ace(filename, temperatures=None, acer=True, xsdir=None, if heatr: nheatr_in = nlast nheatr_local = nheatr_in + 1 - tapeout[nheatr_local] = (output_dir / "heatr_local") if heatr is True \ - else heatr + '_local' + tapeout[nheatr_local] = ( + (output_dir / "heatr_local") if heatr is True else heatr + "_local" + ) commands += _TEMPLATE_HEATR_LOCAL nheatr = nheatr_local + 1 tapeout[nheatr] = (output_dir / "heatr") if heatr is True else heatr @@ -386,28 +409,28 @@ def make_ace(filename, temperatures=None, acer=True, xsdir=None, nacer_in = nlast for i, temperature in enumerate(temperatures): # Extend input with an ACER run for each temperature - nace = nacer_in + 1 + 2*i + nace = nacer_in + 1 + 2 * i ndir = nace + 1 - ext = f'{i + 1:02}' + ext = f"{i + 1:02}" commands += _TEMPLATE_ACER.format(**locals()) # Indicate tapes to save for each ACER run tapeout[nace] = output_dir / f"ace_{temperature:.1f}" tapeout[ndir] = output_dir / f"xsdir_{temperature:.1f}" - commands += 'stop\n' + commands += "stop\n" run(commands, tapein, tapeout, **kwargs) if acer: ace = (output_dir / "ace") if acer is True else Path(acer) xsdir = (ace.parent / "xsdir") if xsdir is None else xsdir - with ace.open('w') as ace_file, xsdir.open('w') as xsdir_file: + with ace.open("w") as ace_file, xsdir.open("w") as xsdir_file: for temperature in temperatures: # Get contents of ACE file text = (output_dir / f"ace_{temperature:.1f}").read_text() # If the target is metastable, make sure that ZAID in the ACE # file reflects this by adding 400 - if ev.target['isomeric_state'] > 0: + if ev.target["isomeric_state"] > 0: mass_first_digit = int(text[3]) if mass_first_digit <= 2: text = text[:3] + str(mass_first_digit + 4) + text[4:] @@ -425,10 +448,22 @@ def make_ace(filename, temperatures=None, acer=True, xsdir=None, (output_dir / f"xsdir_{temperature:.1f}").unlink() -def make_ace_thermal(filename, filename_thermal, temperatures=None, - ace='ace', xsdir=None, output_dir=None, error=0.001, - iwt=2, evaluation=None, evaluation_thermal=None, - table_name=None, zaids=None, nmix=None, **kwargs): +def make_ace_thermal( + filename, + filename_thermal, + temperatures=None, + ace="ace", + xsdir=None, + output_dir=None, + error=0.001, + iwt=2, + evaluation=None, + evaluation_thermal=None, + table_name=None, + zaids=None, + nmix=None, + **kwargs, +): """Generate thermal scattering ACE file from ENDF files Parameters @@ -483,12 +518,15 @@ def make_ace_thermal(filename, filename_thermal, temperatures=None, ev = evaluation if evaluation is not None else endf.Evaluation(filename) mat = ev.material - zsymam = ev.target['zsymam'] + zsymam = ev.target["zsymam"] - ev_thermal = (evaluation_thermal if evaluation_thermal is not None - else endf.Evaluation(filename_thermal)) + ev_thermal = ( + evaluation_thermal + if evaluation_thermal is not None + else endf.Evaluation(filename_thermal) + ) mat_thermal = ev_thermal.material - zsymam_thermal = ev_thermal.target['zsymam'].strip() + zsymam_thermal = ev_thermal.target["zsymam"].strip() # Determine name, isotopes, and number of atom types if table_name and zaids and nmix: @@ -500,14 +538,15 @@ def make_ace_thermal(filename, filename_thermal, temperatures=None, raise RuntimeError( f"Thermal scattering material {zsymam_thermal} not " "recognized. Please contact OpenMC developers at " - "https://openmc.discourse.group.") + "https://openmc.discourse.group." + ) data = _THERMAL_DATA[proper_name] - zaids = ' '.join(str(zaid) for zaid in data.zaids) + zaids = " ".join(str(zaid) for zaid in data.zaids) nza = len(data.zaids) # Determine name of library - library = '{}-{}.{}'.format(*ev_thermal.info['library']) + library = "{}-{}.{}".format(*ev_thermal.info["library"]) # Determine if thermal elastic is present if (7, 2) in ev_thermal.section: @@ -546,7 +585,7 @@ def make_ace_thermal(filename, filename_thermal, temperatures=None, temperatures.append(endf.get_list_record(file_obj)[0][0]) num_temp = len(temperatures) - temps = ' '.join(str(i) for i in temperatures) + temps = " ".join(str(i) for i in temperatures) # Create njoy commands by modules commands = "" @@ -578,20 +617,20 @@ def make_ace_thermal(filename, filename_thermal, temperatures=None, nthermal_acer_in = nlast for i, temperature in enumerate(temperatures): # Extend input with an ACER run for each temperature - nace = nthermal_acer_in + 1 + 2*i + nace = nthermal_acer_in + 1 + 2 * i ndir = nace + 1 - ext = f'{i + 1:02}' + ext = f"{i + 1:02}" commands += _THERMAL_TEMPLATE_ACER.format(**locals()) # Indicate tapes to save for each ACER run tapeout[nace] = output_dir / f"ace_{temperature:.1f}" tapeout[ndir] = output_dir / f"xsdir_{temperature:.1f}" - commands += 'stop\n' + commands += "stop\n" run(commands, tapein, tapeout, **kwargs) ace = output_dir / ace xsdir = (ace.parent / "xsdir") if xsdir is None else Path(xsdir) - with ace.open('w') as ace_file, xsdir.open('w') as xsdir_file: + with ace.open("w") as ace_file, xsdir.open("w") as xsdir_file: # Concatenate ACE and xsdir files together for temperature in temperatures: ace_in = output_dir / f"ace_{temperature:.1f}" diff --git a/openmc/data/photon.py b/openmc/data/photon.py index 25ded24cbe3..864bf721c6a 100644 --- a/openmc/data/photon.py +++ b/openmc/data/photon.py @@ -28,62 +28,100 @@ R0 = CM_PER_ANGSTROM * PLANCK_C / (2.0 * pi * FINE_STRUCTURE * MASS_ELECTRON_EV) # Electron subshell labels -_SUBSHELLS = (None, 'K', 'L1', 'L2', 'L3', 'M1', 'M2', 'M3', 'M4', 'M5', - 'N1', 'N2', 'N3', 'N4', 'N5', 'N6', 'N7', 'O1', 'O2', 'O3', - 'O4', 'O5', 'O6', 'O7', 'O8', 'O9', 'P1', 'P2', 'P3', 'P4', - 'P5', 'P6', 'P7', 'P8', 'P9', 'P10', 'P11', 'Q1', 'Q2', 'Q3') +_SUBSHELLS = ( + None, + "K", + "L1", + "L2", + "L3", + "M1", + "M2", + "M3", + "M4", + "M5", + "N1", + "N2", + "N3", + "N4", + "N5", + "N6", + "N7", + "O1", + "O2", + "O3", + "O4", + "O5", + "O6", + "O7", + "O8", + "O9", + "P1", + "P2", + "P3", + "P4", + "P5", + "P6", + "P7", + "P8", + "P9", + "P10", + "P11", + "Q1", + "Q2", + "Q3", +) _REACTION_NAME = { - 501: ('Total photon interaction', 'total'), - 502: ('Photon coherent scattering', 'coherent'), - 504: ('Photon incoherent scattering', 'incoherent'), - 515: ('Pair production, electron field', 'pair_production_electron'), - 516: ('Total pair production', 'pair_production_total'), - 517: ('Pair production, nuclear field', 'pair_production_nuclear'), - 522: ('Photoelectric absorption', 'photoelectric'), - 525: ('Heating', 'heating'), - 526: ('Electro-atomic scattering', 'electro_atomic_scat'), - 527: ('Electro-atomic bremsstrahlung', 'electro_atomic_brem'), - 528: ('Electro-atomic excitation', 'electro_atomic_excit'), - 534: ('K (1s1/2) subshell photoelectric', 'K'), - 535: ('L1 (2s1/2) subshell photoelectric', 'L1'), - 536: ('L2 (2p1/2) subshell photoelectric', 'L2'), - 537: ('L3 (2p3/2) subshell photoelectric', 'L3'), - 538: ('M1 (3s1/2) subshell photoelectric', 'M1'), - 539: ('M2 (3p1/2) subshell photoelectric', 'M2'), - 540: ('M3 (3p3/2) subshell photoelectric', 'M3'), - 541: ('M4 (3d3/2) subshell photoelectric', 'M4'), - 542: ('M5 (3d5/2) subshell photoelectric', 'M5'), - 543: ('N1 (4s1/2) subshell photoelectric', 'N1'), - 544: ('N2 (4p1/2) subshell photoelectric', 'N2'), - 545: ('N3 (4p3/2) subshell photoelectric', 'N3'), - 546: ('N4 (4d3/2) subshell photoelectric', 'N4'), - 547: ('N5 (4d5/2) subshell photoelectric', 'N5'), - 548: ('N6 (4f5/2) subshell photoelectric', 'N6'), - 549: ('N7 (4f7/2) subshell photoelectric', 'N7'), - 550: ('O1 (5s1/2) subshell photoelectric', 'O1'), - 551: ('O2 (5p1/2) subshell photoelectric', 'O2'), - 552: ('O3 (5p3/2) subshell photoelectric', 'O3'), - 553: ('O4 (5d3/2) subshell photoelectric', 'O4'), - 554: ('O5 (5d5/2) subshell photoelectric', 'O5'), - 555: ('O6 (5f5/2) subshell photoelectric', 'O6'), - 556: ('O7 (5f7/2) subshell photoelectric', 'O7'), - 557: ('O8 (5g7/2) subshell photoelectric', 'O8'), - 558: ('O9 (5g9/2) subshell photoelectric', 'O9'), - 559: ('P1 (6s1/2) subshell photoelectric', 'P1'), - 560: ('P2 (6p1/2) subshell photoelectric', 'P2'), - 561: ('P3 (6p3/2) subshell photoelectric', 'P3'), - 562: ('P4 (6d3/2) subshell photoelectric', 'P4'), - 563: ('P5 (6d5/2) subshell photoelectric', 'P5'), - 564: ('P6 (6f5/2) subshell photoelectric', 'P6'), - 565: ('P7 (6f7/2) subshell photoelectric', 'P7'), - 566: ('P8 (6g7/2) subshell photoelectric', 'P8'), - 567: ('P9 (6g9/2) subshell photoelectric', 'P9'), - 568: ('P10 (6h9/2) subshell photoelectric', 'P10'), - 569: ('P11 (6h11/2) subshell photoelectric', 'P11'), - 570: ('Q1 (7s1/2) subshell photoelectric', 'Q1'), - 571: ('Q2 (7p1/2) subshell photoelectric', 'Q2'), - 572: ('Q3 (7p3/2) subshell photoelectric', 'Q3') + 501: ("Total photon interaction", "total"), + 502: ("Photon coherent scattering", "coherent"), + 504: ("Photon incoherent scattering", "incoherent"), + 515: ("Pair production, electron field", "pair_production_electron"), + 516: ("Total pair production", "pair_production_total"), + 517: ("Pair production, nuclear field", "pair_production_nuclear"), + 522: ("Photoelectric absorption", "photoelectric"), + 525: ("Heating", "heating"), + 526: ("Electro-atomic scattering", "electro_atomic_scat"), + 527: ("Electro-atomic bremsstrahlung", "electro_atomic_brem"), + 528: ("Electro-atomic excitation", "electro_atomic_excit"), + 534: ("K (1s1/2) subshell photoelectric", "K"), + 535: ("L1 (2s1/2) subshell photoelectric", "L1"), + 536: ("L2 (2p1/2) subshell photoelectric", "L2"), + 537: ("L3 (2p3/2) subshell photoelectric", "L3"), + 538: ("M1 (3s1/2) subshell photoelectric", "M1"), + 539: ("M2 (3p1/2) subshell photoelectric", "M2"), + 540: ("M3 (3p3/2) subshell photoelectric", "M3"), + 541: ("M4 (3d3/2) subshell photoelectric", "M4"), + 542: ("M5 (3d5/2) subshell photoelectric", "M5"), + 543: ("N1 (4s1/2) subshell photoelectric", "N1"), + 544: ("N2 (4p1/2) subshell photoelectric", "N2"), + 545: ("N3 (4p3/2) subshell photoelectric", "N3"), + 546: ("N4 (4d3/2) subshell photoelectric", "N4"), + 547: ("N5 (4d5/2) subshell photoelectric", "N5"), + 548: ("N6 (4f5/2) subshell photoelectric", "N6"), + 549: ("N7 (4f7/2) subshell photoelectric", "N7"), + 550: ("O1 (5s1/2) subshell photoelectric", "O1"), + 551: ("O2 (5p1/2) subshell photoelectric", "O2"), + 552: ("O3 (5p3/2) subshell photoelectric", "O3"), + 553: ("O4 (5d3/2) subshell photoelectric", "O4"), + 554: ("O5 (5d5/2) subshell photoelectric", "O5"), + 555: ("O6 (5f5/2) subshell photoelectric", "O6"), + 556: ("O7 (5f7/2) subshell photoelectric", "O7"), + 557: ("O8 (5g7/2) subshell photoelectric", "O8"), + 558: ("O9 (5g9/2) subshell photoelectric", "O9"), + 559: ("P1 (6s1/2) subshell photoelectric", "P1"), + 560: ("P2 (6p1/2) subshell photoelectric", "P2"), + 561: ("P3 (6p3/2) subshell photoelectric", "P3"), + 562: ("P4 (6d3/2) subshell photoelectric", "P4"), + 563: ("P5 (6d5/2) subshell photoelectric", "P5"), + 564: ("P6 (6f5/2) subshell photoelectric", "P6"), + 565: ("P7 (6f7/2) subshell photoelectric", "P7"), + 566: ("P8 (6g7/2) subshell photoelectric", "P8"), + 567: ("P9 (6g9/2) subshell photoelectric", "P9"), + 568: ("P10 (6h9/2) subshell photoelectric", "P10"), + 569: ("P11 (6h11/2) subshell photoelectric", "P11"), + 570: ("Q1 (7s1/2) subshell photoelectric", "Q1"), + 571: ("Q2 (7p1/2) subshell photoelectric", "Q2"), + 572: ("Q3 (7p3/2) subshell photoelectric", "Q3"), } # Compton profiles are read from a pre-generated HDF5 file when they are first @@ -157,6 +195,7 @@ class AtomicRelaxation(EqualityMixin): IncidentPhoton """ + def __init__(self, binding_energy, num_electrons, transitions): self.binding_energy = binding_energy self.num_electrons = num_electrons @@ -169,11 +208,11 @@ def binding_energy(self): @binding_energy.setter def binding_energy(self, binding_energy): - cv.check_type('binding energies', binding_energy, Mapping) + cv.check_type("binding energies", binding_energy, Mapping) for subshell, energy in binding_energy.items(): - cv.check_value('subshell', subshell, _SUBSHELLS) - cv.check_type('binding energy', energy, Real) - cv.check_greater_than('binding energy', energy, 0.0, True) + cv.check_value("subshell", subshell, _SUBSHELLS) + cv.check_type("binding energy", energy, Real) + cv.check_greater_than("binding energy", energy, 0.0, True) self._binding_energy = binding_energy @property @@ -182,11 +221,11 @@ def num_electrons(self): @num_electrons.setter def num_electrons(self, num_electrons): - cv.check_type('number of electrons', num_electrons, Mapping) + cv.check_type("number of electrons", num_electrons, Mapping) for subshell, num in num_electrons.items(): - cv.check_value('subshell', subshell, _SUBSHELLS) - cv.check_type('number of electrons', num, Real) - cv.check_greater_than('number of electrons', num, 0.0, True) + cv.check_value("subshell", subshell, _SUBSHELLS) + cv.check_type("number of electrons", num, Real) + cv.check_greater_than("number of electrons", num, 0.0, True) self._num_electrons = num_electrons @property @@ -199,10 +238,10 @@ def transitions(self): @transitions.setter def transitions(self, transitions): - cv.check_type('transitions', transitions, Mapping) + cv.check_type("transitions", transitions, Mapping) for subshell, df in transitions.items(): - cv.check_value('subshell', subshell, _SUBSHELLS) - cv.check_type('transitions', df, pd.DataFrame) + cv.check_value("subshell", subshell, _SUBSHELLS) + cv.check_type("transitions", df, pd.DataFrame) self._transitions = transitions @classmethod @@ -228,20 +267,20 @@ def from_ace(cls, ace): # Get shell designators n = ace.nxs[7] idx = ace.jxs[11] - shells = [_SUBSHELLS[int(i)] for i in ace.xss[idx : idx+n]] + shells = [_SUBSHELLS[int(i)] for i in ace.xss[idx : idx + n]] # Get number of electrons for each shell idx = ace.jxs[12] - for shell, num in zip(shells, ace.xss[idx : idx+n]): + for shell, num in zip(shells, ace.xss[idx : idx + n]): num_electrons[shell] = num # Get binding energy for each shell idx = ace.jxs[13] - for shell, e in zip(shells, ace.xss[idx : idx+n]): - binding_energy[shell] = e*EV_PER_MEV + for shell, e in zip(shells, ace.xss[idx : idx + n]): + binding_energy[shell] = e * EV_PER_MEV # Get transition table - columns = ['secondary', 'tertiary', 'energy (eV)', 'probability'] + columns = ["secondary", "tertiary", "energy (eV)", "probability"] idx = ace.jxs[18] for i, subi in enumerate(shells): n_transitions = int(ace.xss[ace.jxs[15] + i]) @@ -250,7 +289,7 @@ def from_ace(cls, ace): for j in range(n_transitions): subj = _SUBSHELLS[int(ace.xss[idx])] subk = _SUBSHELLS[int(ace.xss[idx + 1])] - etr = ace.xss[idx + 2]*EV_PER_MEV + etr = ace.xss[idx + 2] * EV_PER_MEV if j == 0: ftr = ace.xss[idx + 3] else: @@ -259,8 +298,7 @@ def from_ace(cls, ace): idx += 4 # Create dataframe for transitions - transitions[subi] = pd.DataFrame.from_records( - records, columns=columns) + transitions[subi] = pd.DataFrame.from_records(records, columns=columns) return cls(binding_energy, num_electrons, transitions) @@ -287,8 +325,10 @@ def from_endf(cls, ev_or_filename): # Atomic relaxation data is always MF=28, MT=533 if (28, 533) not in ev.section: - raise IOError('{} does not appear to be an atomic relaxation ' - 'sublibrary.'.format(ev)) + raise IOError( + "{} does not appear to be an atomic relaxation " + "sublibrary.".format(ev) + ) # Determine number of subshells file_obj = StringIO(ev.section[28, 533]) @@ -299,7 +339,7 @@ def from_endf(cls, ev_or_filename): binding_energy = {} num_electrons = {} transitions = {} - columns = ['secondary', 'tertiary', 'energy (eV)', 'probability'] + columns = ["secondary", "tertiary", "energy (eV)", "probability"] # Read data for each subshell for i in range(n_subshells): @@ -313,15 +353,14 @@ def from_endf(cls, ev_or_filename): # Read transition data records = [] for j in range(n_transitions): - subj = _SUBSHELLS[int(list_items[6*(j+1)])] - subk = _SUBSHELLS[int(list_items[6*(j+1) + 1])] - etr = list_items[6*(j+1) + 2] - ftr = list_items[6*(j+1) + 3] + subj = _SUBSHELLS[int(list_items[6 * (j + 1)])] + subk = _SUBSHELLS[int(list_items[6 * (j + 1) + 1])] + etr = list_items[6 * (j + 1) + 2] + ftr = list_items[6 * (j + 1) + 3] records.append((subj, subk, etr, ftr)) # Create dataframe for transitions - transitions[subi] = pd.DataFrame.from_records( - records, columns=columns) + transitions[subi] = pd.DataFrame.from_records(records, columns=columns) # Return instance of class return cls(binding_energy, num_electrons, transitions) @@ -346,26 +385,26 @@ def from_hdf5(cls, group): num_electrons = {} transitions = {} - designators = [s.decode() for s in group.attrs['designators']] - columns = ['secondary', 'tertiary', 'energy (eV)', 'probability'] + designators = [s.decode() for s in group.attrs["designators"]] + columns = ["secondary", "tertiary", "energy (eV)", "probability"] for shell in designators: # Shell group sub_group = group[shell] # Read subshell binding energy and number of electrons - if 'binding_energy' in sub_group.attrs: - binding_energy[shell] = sub_group.attrs['binding_energy'] - if 'num_electrons' in sub_group.attrs: - num_electrons[shell] = sub_group.attrs['num_electrons'] + if "binding_energy" in sub_group.attrs: + binding_energy[shell] = sub_group.attrs["binding_energy"] + if "num_electrons" in sub_group.attrs: + num_electrons[shell] = sub_group.attrs["num_electrons"] # Read transition data - if 'transitions' in sub_group: - df = pd.DataFrame(sub_group['transitions'][()], - columns=columns) + if "transitions" in sub_group: + df = pd.DataFrame(sub_group["transitions"][()], columns=columns) # Replace float indexes back to subshell strings - with pd.option_context('future.no_silent_downcasting', True): + with pd.option_context("future.no_silent_downcasting", True): df[columns[:2]] = df[columns[:2]].replace( - np.arange(float(len(_SUBSHELLS))), _SUBSHELLS) + np.arange(float(len(_SUBSHELLS))), _SUBSHELLS + ) transitions[shell] = df return cls(binding_energy, num_electrons, transitions) @@ -383,15 +422,14 @@ def to_hdf5(self, group, shell): """ # Write subshell binding energy and number of electrons - group.attrs['binding_energy'] = self.binding_energy[shell] - group.attrs['num_electrons'] = self.num_electrons[shell] + group.attrs["binding_energy"] = self.binding_energy[shell] + group.attrs["num_electrons"] = self.num_electrons[shell] # Write transition data with replacements if shell in self.transitions: - with pd.option_context('future.no_silent_downcasting', True): - df = self.transitions[shell].replace( - _SUBSHELLS, range(len(_SUBSHELLS))) - group.create_dataset('transitions', data=df.values.astype(float)) + with pd.option_context("future.no_silent_downcasting", True): + df = self.transitions[shell].replace(_SUBSHELLS, range(len(_SUBSHELLS))) + group.create_dataset("transitions", data=df.values.astype(float)) class IncidentPhoton(EqualityMixin): @@ -453,7 +491,7 @@ def __getitem__(self, mt): if mt in self.reactions: return self.reactions[mt] else: - raise KeyError(f'No reaction with MT={mt}.') + raise KeyError(f"No reaction with MT={mt}.") def __repr__(self): return f"" @@ -467,8 +505,8 @@ def atomic_number(self): @atomic_number.setter def atomic_number(self, atomic_number): - cv.check_type('atomic number', atomic_number, Integral) - cv.check_greater_than('atomic number', atomic_number, 0, True) + cv.check_type("atomic number", atomic_number, Integral) + cv.check_greater_than("atomic number", atomic_number, 0, True) self._atomic_number = atomic_number @property @@ -477,8 +515,7 @@ def atomic_relaxation(self): @atomic_relaxation.setter def atomic_relaxation(self, atomic_relaxation): - cv.check_type('atomic relaxation data', atomic_relaxation, - AtomicRelaxation) + cv.check_type("atomic relaxation data", atomic_relaxation, AtomicRelaxation) self._atomic_relaxation = atomic_relaxation @property @@ -508,8 +545,8 @@ def from_ace(cls, ace_or_filename): ace = get_table(ace_or_filename) # Get atomic number based on name of ACE table - zaid, xs = ace.name.split('.') - if not xs.endswith('p'): + zaid, xs = ace.name.split(".") + if not xs.endswith("p"): raise TypeError(f"{ace} is not a photoatomic transport ACE table.") Z = get_metadata(int(zaid))[2] @@ -520,20 +557,21 @@ def from_ace(cls, ace_or_filename): # Get heating cross sections [eV-barn] from factors [eV per collision] # by multiplying with total xs - data.reactions[525].xs.y *= sum([data.reactions[mt].xs.y for mt in - (502, 504, 517, 522)]) + data.reactions[525].xs.y *= sum( + [data.reactions[mt].xs.y for mt in (502, 504, 517, 522)] + ) # Compton profiles n_shell = ace.nxs[5] if n_shell != 0: # Get number of electrons in each shell idx = ace.jxs[6] - data.compton_profiles['num_electrons'] = ace.xss[idx : idx+n_shell] + data.compton_profiles["num_electrons"] = ace.xss[idx : idx + n_shell] # Get binding energy for each shell idx = ace.jxs[7] - e = ace.xss[idx : idx+n_shell]*EV_PER_MEV - data.compton_profiles['binding_energy'] = e + e = ace.xss[idx : idx + n_shell] * EV_PER_MEV + data.compton_profiles["binding_energy"] = e # Create Compton profile for each electron shell profiles = [] @@ -545,13 +583,13 @@ def from_ace(cls, ace_or_filename): # Read momentum and PDF idx = ace.jxs[10] + loca + 1 - pz = ace.xss[idx : idx+m] - pdf = ace.xss[idx+m : idx+2*m] + pz = ace.xss[idx : idx + m] + pdf = ace.xss[idx + m : idx + 2 * m] # Create proflie function J_k = Tabulated1D(pz, pdf, [m], [jj]) profiles.append(J_k) - data.compton_profiles['J'] = profiles + data.compton_profiles["J"] = profiles # Subshell photoelectric xs and atomic relaxation data if ace.nxs[7] > 0: @@ -560,12 +598,12 @@ def from_ace(cls, ace_or_filename): # Get subshell designators n_subshells = ace.nxs[7] idx = ace.jxs[11] - designators = [int(i) for i in ace.xss[idx : idx+n_subshells]] + designators = [int(i) for i in ace.xss[idx : idx + n_subshells]] # Get energy grid for subshell photoionization n_energy = ace.nxs[3] idx = ace.jxs[1] - energy = np.exp(ace.xss[idx : idx+n_energy])*EV_PER_MEV + energy = np.exp(ace.xss[idx : idx + n_energy]) * EV_PER_MEV # Get cross section for each subshell idx = ace.jxs[16] @@ -576,12 +614,13 @@ def from_ace(cls, ace_or_filename): data.reactions[mt] = rx # Store cross section, determining threshold - xs = ace.xss[idx : idx+n_energy].copy() - nonzero = (xs != 0.0) + xs = ace.xss[idx : idx + n_energy].copy() + nonzero = xs != 0.0 xs[nonzero] = np.exp(xs[nonzero]) threshold = np.where(xs > 0.0)[0][0] - rx.xs = Tabulated1D(energy[threshold:], xs[threshold:], - [n_energy - threshold], [5]) + rx.xs = Tabulated1D( + energy[threshold:], xs[threshold:], [n_energy - threshold], [5] + ) idx += n_energy # Copy binding energy @@ -589,9 +628,11 @@ def from_ace(cls, ace_or_filename): e = data.atomic_relaxation.binding_energy[shell] rx.subshell_binding_energy = e else: - raise ValueError("ACE table {} does not have subshell data. Only " - "newer ACE photoatomic libraries are supported " - "(e.g., eprdata14).".format(ace.name)) + raise ValueError( + "ACE table {} does not have subshell data. Only " + "newer ACE photoatomic libraries are supported " + "(e.g., eprdata14).".format(ace.name) + ) # Add bremsstrahlung DCS data data._add_bremsstrahlung() @@ -622,7 +663,7 @@ def from_endf(cls, photoatomic, relaxation=None): else: ev = Evaluation(photoatomic) - Z = ev.target['atomic_number'] + Z = ev.target["atomic_number"] data = cls(Z) # Read each reaction @@ -636,24 +677,26 @@ def from_endf(cls, photoatomic, relaxation=None): # If Compton profile data hasn't been loaded, do so if not _COMPTON_PROFILES: - filename = os.path.join(os.path.dirname(__file__), 'compton_profiles.h5') - with h5py.File(filename, 'r') as f: - _COMPTON_PROFILES['pz'] = f['pz'][()] + filename = os.path.join(os.path.dirname(__file__), "compton_profiles.h5") + with h5py.File(filename, "r") as f: + _COMPTON_PROFILES["pz"] = f["pz"][()] for i in range(1, 101): - group = f[f'{i:03}'] - num_electrons = group['num_electrons'][()] - binding_energy = group['binding_energy'][()]*EV_PER_MEV - J = group['J'][()] - _COMPTON_PROFILES[i] = {'num_electrons': num_electrons, - 'binding_energy': binding_energy, - 'J': J} + group = f[f"{i:03}"] + num_electrons = group["num_electrons"][()] + binding_energy = group["binding_energy"][()] * EV_PER_MEV + J = group["J"][()] + _COMPTON_PROFILES[i] = { + "num_electrons": num_electrons, + "binding_energy": binding_energy, + "J": J, + } # Add Compton profile data - pz = _COMPTON_PROFILES['pz'] + pz = _COMPTON_PROFILES["pz"] profile = _COMPTON_PROFILES[Z] - data.compton_profiles['num_electrons'] = profile['num_electrons'] - data.compton_profiles['binding_energy'] = profile['binding_energy'] - data.compton_profiles['J'] = [Tabulated1D(pz, J_k) for J_k in profile['J']] + data.compton_profiles["num_electrons"] = profile["num_electrons"] + data.compton_profiles["binding_energy"] = profile["binding_energy"] + data.compton_profiles["J"] = [Tabulated1D(pz, J_k) for J_k in profile["J"]] # Add bremsstrahlung DCS data data._add_bremsstrahlung() @@ -681,33 +724,35 @@ def from_hdf5(cls, group_or_filename): group = group_or_filename need_to_close = False else: - h5file = h5py.File(str(group_or_filename), 'r') + h5file = h5py.File(str(group_or_filename), "r") need_to_close = True # Make sure version matches - if 'version' in h5file.attrs: - major, minor = h5file.attrs['version'] + if "version" in h5file.attrs: + major, minor = h5file.attrs["version"] # For now all versions of HDF5 data can be read else: raise IOError( - 'HDF5 data does not indicate a version. Your installation ' - 'of the OpenMC Python API expects version {}.x data.' - .format(HDF5_VERSION_MAJOR)) + "HDF5 data does not indicate a version. Your installation " + "of the OpenMC Python API expects version {}.x data.".format( + HDF5_VERSION_MAJOR + ) + ) group = list(h5file.values())[0] - Z = group.attrs['Z'] + Z = group.attrs["Z"] data = cls(Z) # Read energy grid - energy = group['energy'][()] + energy = group["energy"][()] # Read cross section data for mt, (name, key) in _REACTION_NAME.items(): if key in group: rgroup = group[key] - elif key in group['subshells']: - rgroup = group['subshells'][key] + elif key in group["subshells"]: + rgroup = group["subshells"][key] else: continue @@ -718,29 +763,35 @@ def from_hdf5(cls, group_or_filename): assert mt in data, f"Reaction {mt} not found" # Read atomic relaxation - data.atomic_relaxation = AtomicRelaxation.from_hdf5(group['subshells']) + data.atomic_relaxation = AtomicRelaxation.from_hdf5(group["subshells"]) # Read Compton profiles - if 'compton_profiles' in group: - rgroup = group['compton_profiles'] + if "compton_profiles" in group: + rgroup = group["compton_profiles"] profile = data.compton_profiles - profile['num_electrons'] = rgroup['num_electrons'][()] - profile['binding_energy'] = rgroup['binding_energy'][()] + profile["num_electrons"] = rgroup["num_electrons"][()] + profile["binding_energy"] = rgroup["binding_energy"][()] # Get electron momentum values - pz = rgroup['pz'][()] - J = rgroup['J'][()] + pz = rgroup["pz"][()] + J = rgroup["J"][()] if pz.size != J.shape[1]: - raise ValueError("'J' array shape is not consistent with the " - "'pz' array shape") - profile['J'] = [Tabulated1D(pz, Jk) for Jk in J] + raise ValueError( + "'J' array shape is not consistent with the " "'pz' array shape" + ) + profile["J"] = [Tabulated1D(pz, Jk) for Jk in J] # Read bremsstrahlung - if 'bremsstrahlung' in group: - rgroup = group['bremsstrahlung'] - data.bremsstrahlung['I'] = rgroup.attrs['I'] - for key in ('dcs', 'electron_energy', 'ionization_energy', - 'num_electrons', 'photon_energy'): + if "bremsstrahlung" in group: + rgroup = group["bremsstrahlung"] + data.bremsstrahlung["I"] = rgroup.attrs["I"] + for key in ( + "dcs", + "electron_energy", + "ionization_energy", + "num_electrons", + "photon_energy", + ): data.bremsstrahlung[key] = rgroup[key][()] # If HDF5 file was opened here, make sure it gets closed @@ -749,7 +800,7 @@ def from_hdf5(cls, group_or_filename): return data - def export_to_hdf5(self, path, mode='a', libver='earliest'): + def export_to_hdf5(self, path, mode="a", libver="earliest"): """Export incident photon data to an HDF5 file. Parameters @@ -766,21 +817,21 @@ def export_to_hdf5(self, path, mode='a', libver='earliest'): """ with h5py.File(str(path), mode, libver=libver) as f: # Write filetype and version - f.attrs['filetype'] = np.bytes_('data_photon') - if 'version' not in f.attrs: - f.attrs['version'] = np.array(HDF5_VERSION) + f.attrs["filetype"] = np.bytes_("data_photon") + if "version" not in f.attrs: + f.attrs["version"] = np.array(HDF5_VERSION) group = f.create_group(self.name) - group.attrs['Z'] = Z = self.atomic_number + group.attrs["Z"] = Z = self.atomic_number # Determine union energy grid union_grid = np.array([]) for rx in self: union_grid = np.union1d(union_grid, rx.xs.x) - group.create_dataset('energy', data=union_grid) + group.create_dataset("energy", data=union_grid) # Write cross sections - shell_group = group.create_group('subshells') + shell_group = group.create_group("subshells") designators = [] for mt, rx in self.reactions.items(): name, key = _REACTION_NAME[mt] @@ -800,58 +851,58 @@ def export_to_hdf5(self, path, mode='a', libver='earliest'): rx.to_hdf5(sub_group, union_grid, Z) - shell_group.attrs['designators'] = np.array(designators, dtype='S') + shell_group.attrs["designators"] = np.array(designators, dtype="S") # Write Compton profiles if self.compton_profiles: - compton_group = group.create_group('compton_profiles') + compton_group = group.create_group("compton_profiles") profile = self.compton_profiles - compton_group.create_dataset('num_electrons', - data=profile['num_electrons']) - compton_group.create_dataset('binding_energy', - data=profile['binding_energy']) + compton_group.create_dataset( + "num_electrons", data=profile["num_electrons"] + ) + compton_group.create_dataset( + "binding_energy", data=profile["binding_energy"] + ) # Get electron momentum values - compton_group.create_dataset('pz', data=profile['J'][0].x) + compton_group.create_dataset("pz", data=profile["J"][0].x) # Create/write 2D array of profiles - J = np.array([Jk.y for Jk in profile['J']]) - compton_group.create_dataset('J', data=J) + J = np.array([Jk.y for Jk in profile["J"]]) + compton_group.create_dataset("J", data=J) # Write bremsstrahlung if self.bremsstrahlung: - brem_group = group.create_group('bremsstrahlung') + brem_group = group.create_group("bremsstrahlung") for key, value in self.bremsstrahlung.items(): - if key == 'I': + if key == "I": brem_group.attrs[key] = value else: brem_group.create_dataset(key, data=value) def _add_bremsstrahlung(self): - """Add the data used in the thick-target bremsstrahlung approximation - - """ + """Add the data used in the thick-target bremsstrahlung approximation""" # Load bremsstrahlung data if it has not yet been loaded if not _BREMSSTRAHLUNG: # Add data used for density effect correction - filename = os.path.join(os.path.dirname(__file__), 'density_effect.h5') - with h5py.File(filename, 'r') as f: + filename = os.path.join(os.path.dirname(__file__), "density_effect.h5") + with h5py.File(filename, "r") as f: for i in range(1, 101): - group = f[f'{i:03}'] + group = f[f"{i:03}"] _BREMSSTRAHLUNG[i] = { - 'I': group.attrs['I'], - 'num_electrons': group['num_electrons'][()], - 'ionization_energy': group['ionization_energy'][()] + "I": group.attrs["I"], + "num_electrons": group["num_electrons"][()], + "ionization_energy": group["ionization_energy"][()], } - filename = os.path.join(os.path.dirname(__file__), 'BREMX.DAT') - with open(filename, 'r') as fh: + filename = os.path.join(os.path.dirname(__file__), "BREMX.DAT") + with open(filename, "r") as fh: brem = fh.read().split() # Incident electron kinetic energy grid in eV - _BREMSSTRAHLUNG['electron_energy'] = np.logspace(3, 9, 200) - log_energy = np.log(_BREMSSTRAHLUNG['electron_energy']) + _BREMSSTRAHLUNG["electron_energy"] = np.logspace(3, 9, 200) + log_energy = np.log(_BREMSSTRAHLUNG["electron_energy"]) # Get number of tabulated electron and photon energy values n = int(brem[37]) @@ -863,11 +914,11 @@ def _add_bremsstrahlung(self): # Get log of incident electron kinetic energy values, used for # cubic spline interpolation in log energy. Units are in MeV, so # convert to eV. - logx = np.log(np.fromiter(brem[p:p+n], float, n)*EV_PER_MEV) + logx = np.log(np.fromiter(brem[p : p + n], float, n) * EV_PER_MEV) p += n # Get reduced photon energy values - _BREMSSTRAHLUNG['photon_energy'] = np.fromiter(brem[p:p+k], float, k) + _BREMSSTRAHLUNG["photon_energy"] = np.fromiter(brem[p : p + k], float, k) p += k for i in range(1, 101): @@ -876,8 +927,11 @@ def _add_bremsstrahlung(self): # Get the scaled cross section values for each electron energy # and reduced photon energy for this Z. Units are in mb, so # convert to b. - y = np.reshape(np.fromiter(brem[p:p+n*k], float, n*k), (n, k))*1.0e-3 - p += k*n + y = ( + np.reshape(np.fromiter(brem[p : p + n * k], float, n * k), (n, k)) + * 1.0e-3 + ) + p += k * n for j in range(k): # Cubic spline interpolation in log energy and linear DCS @@ -886,11 +940,11 @@ def _add_bremsstrahlung(self): # Get scaled DCS values (barns) on new energy grid dcs[:, j] = cs(log_energy) - _BREMSSTRAHLUNG[i]['dcs'] = dcs + _BREMSSTRAHLUNG[i]["dcs"] = dcs # Add bremsstrahlung DCS data - self.bremsstrahlung['electron_energy'] = _BREMSSTRAHLUNG['electron_energy'] - self.bremsstrahlung['photon_energy'] = _BREMSSTRAHLUNG['photon_energy'] + self.bremsstrahlung["electron_energy"] = _BREMSSTRAHLUNG["electron_energy"] + self.bremsstrahlung["photon_energy"] = _BREMSSTRAHLUNG["photon_energy"] self.bremsstrahlung.update(_BREMSSTRAHLUNG[self.atomic_number]) @@ -936,8 +990,9 @@ def anomalous_real(self): @anomalous_real.setter def anomalous_real(self, anomalous_real): - cv.check_type('real part of anomalous scattering factor', - anomalous_real, Callable) + cv.check_type( + "real part of anomalous scattering factor", anomalous_real, Callable + ) self._anomalous_real = anomalous_real @property @@ -946,8 +1001,9 @@ def anomalous_imag(self): @anomalous_imag.setter def anomalous_imag(self, anomalous_imag): - cv.check_type('imaginary part of anomalous scattering factor', - anomalous_imag, Callable) + cv.check_type( + "imaginary part of anomalous scattering factor", anomalous_imag, Callable + ) self._anomalous_imag = anomalous_imag @property @@ -956,7 +1012,7 @@ def scattering_factor(self): @scattering_factor.setter def scattering_factor(self, scattering_factor): - cv.check_type('scattering factor', scattering_factor, Callable) + cv.check_type("scattering factor", scattering_factor, Callable) self._scattering_factor = scattering_factor @property @@ -965,7 +1021,7 @@ def xs(self): @xs.setter def xs(self, xs): - cv.check_type('reaction cross section', xs, Callable) + cv.check_type("reaction cross section", xs, Callable) self._xs = xs @classmethod @@ -991,35 +1047,37 @@ def from_ace(cls, ace, mt): # Get energy grid (stored as logarithms) n = ace.nxs[3] idx = ace.jxs[1] - energy = np.exp(ace.xss[idx : idx+n])*EV_PER_MEV + energy = np.exp(ace.xss[idx : idx + n]) * EV_PER_MEV # Get index for appropriate reaction if mt == 502: # Coherent scattering - idx = ace.jxs[1] + 2*n + idx = ace.jxs[1] + 2 * n elif mt == 504: # Incoherent scattering idx = ace.jxs[1] + n elif mt == 517: # Pair production - idx = ace.jxs[1] + 4*n + idx = ace.jxs[1] + 4 * n elif mt == 522: # Photoelectric - idx = ace.jxs[1] + 3*n + idx = ace.jxs[1] + 3 * n elif mt == 525: # Heating idx = ace.jxs[5] else: - raise ValueError('ACE photoatomic cross sections do not have ' - 'data for MT={}.'.format(mt)) + raise ValueError( + "ACE photoatomic cross sections do not have " + "data for MT={}.".format(mt) + ) # Store cross section - xs = ace.xss[idx : idx+n].copy() + xs = ace.xss[idx : idx + n].copy() if mt == 525: # Get heating factors in [eV per collision] xs *= EV_PER_MEV else: - nonzero = (xs != 0.0) + nonzero = xs != 0.0 xs[nonzero] = np.exp(xs[nonzero]) # Replace zero elements to small non-zero to enable log-log @@ -1027,38 +1085,111 @@ def from_ace(cls, ace, mt): rx.xs = Tabulated1D(energy, xs, [n], [5]) # Get form factors for incoherent/coherent scattering - new_format = (ace.nxs[6] > 0) + new_format = ace.nxs[6] > 0 if mt == 502: idx = ace.jxs[3] if new_format: n = (ace.jxs[4] - ace.jxs[3]) // 3 - x = ace.xss[idx : idx+n] + x = ace.xss[idx : idx + n] idx += n else: - x = np.array([ - 0.0, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.08, 0.1, 0.12, - 0.15, 0.18, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, - 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, - 1.7, 1.8, 1.9, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, - 3.6, 3.8, 4.0, 4.2, 4.4, 4.6, 4.8, 5.0, 5.2, 5.4, 5.6, - 5.8, 6.0]) + x = np.array( + [ + 0.0, + 0.01, + 0.02, + 0.03, + 0.04, + 0.05, + 0.06, + 0.08, + 0.1, + 0.12, + 0.15, + 0.18, + 0.2, + 0.25, + 0.3, + 0.35, + 0.4, + 0.45, + 0.5, + 0.55, + 0.6, + 0.7, + 0.8, + 0.9, + 1.0, + 1.1, + 1.2, + 1.3, + 1.4, + 1.5, + 1.6, + 1.7, + 1.8, + 1.9, + 2.0, + 2.2, + 2.4, + 2.6, + 2.8, + 3.0, + 3.2, + 3.4, + 3.6, + 3.8, + 4.0, + 4.2, + 4.4, + 4.6, + 4.8, + 5.0, + 5.2, + 5.4, + 5.6, + 5.8, + 6.0, + ] + ) n = x.size - ff = ace.xss[idx+n : idx+2*n] + ff = ace.xss[idx + n : idx + 2 * n] rx.scattering_factor = Tabulated1D(x, ff) elif mt == 504: idx = ace.jxs[2] if new_format: n = (ace.jxs[3] - ace.jxs[2]) // 2 - x = ace.xss[idx : idx+n] + x = ace.xss[idx : idx + n] idx += n else: - x = np.array([ - 0.0, 0.005, 0.01, 0.05, 0.1, 0.15, 0.2, 0.3, 0.4, 0.5, 0.6, - 0.7, 0.8, 0.9, 1.0, 1.5, 2.0, 3.0, 4.0, 5.0, 8.0 - ]) + x = np.array( + [ + 0.0, + 0.005, + 0.01, + 0.05, + 0.1, + 0.15, + 0.2, + 0.3, + 0.4, + 0.5, + 0.6, + 0.7, + 0.8, + 0.9, + 1.0, + 1.5, + 2.0, + 3.0, + 4.0, + 5.0, + 8.0, + ] + ) n = x.size - ff = ace.xss[idx : idx+n] + ff = ace.xss[idx : idx + n] rx.scattering_factor = Tabulated1D(x, ff) return rx @@ -1137,27 +1268,27 @@ def from_hdf5(cls, group, mt, energy): rx = cls(mt) # Cross sections - xs = group['xs'][()] + xs = group["xs"][()] # Replace zero elements to small non-zero to enable log-log xs[xs == 0.0] = np.exp(-500.0) # Threshold threshold_idx = 0 - if 'threshold_idx' in group['xs'].attrs: - threshold_idx = group['xs'].attrs['threshold_idx'] + if "threshold_idx" in group["xs"].attrs: + threshold_idx = group["xs"].attrs["threshold_idx"] # Store cross section rx.xs = Tabulated1D(energy[threshold_idx:], xs, [len(xs)], [5]) # Check for anomalous scattering factor - if 'anomalous_real' in group: - rx.anomalous_real = Tabulated1D.from_hdf5(group['anomalous_real']) - if 'anomalous_imag' in group: - rx.anomalous_imag = Tabulated1D.from_hdf5(group['anomalous_imag']) + if "anomalous_real" in group: + rx.anomalous_real = Tabulated1D.from_hdf5(group["anomalous_real"]) + if "anomalous_imag" in group: + rx.anomalous_imag = Tabulated1D.from_hdf5(group["anomalous_imag"]) # Check for factors / scattering functions - if 'scattering_factor' in group: - rx.scattering_factor = Tabulated1D.from_hdf5(group['scattering_factor']) + if "scattering_factor" in group: + rx.scattering_factor = Tabulated1D.from_hdf5(group["scattering_factor"]) return rx @@ -1179,15 +1310,15 @@ def to_hdf5(self, group, energy, Z): if self.mt >= 534 and self.mt <= 572: # Determine threshold threshold = self.xs.x[0] - idx = np.searchsorted(energy, threshold, side='right') - 1 + idx = np.searchsorted(energy, threshold, side="right") - 1 # Interpolate cross section onto union grid and write photoionization = self.xs(energy[idx:]) - group.create_dataset('xs', data=photoionization) + group.create_dataset("xs", data=photoionization) assert len(energy) == len(photoionization) + idx - group['xs'].attrs['threshold_idx'] = idx + group["xs"].attrs["threshold_idx"] = idx else: - group.create_dataset('xs', data=self.xs(energy)) + group.create_dataset("xs", data=self.xs(energy)) # Write scattering factor if self.scattering_factor is not None: @@ -1195,11 +1326,11 @@ def to_hdf5(self, group, energy, Z): # Create integrated form factor ff = deepcopy(self.scattering_factor) ff.x *= ff.x - ff.y *= ff.y/Z**2 + ff.y *= ff.y / Z**2 int_ff = Tabulated1D(ff.x, ff.integral()) - int_ff.to_hdf5(group, 'integrated_scattering_factor') - self.scattering_factor.to_hdf5(group, 'scattering_factor') + int_ff.to_hdf5(group, "integrated_scattering_factor") + self.scattering_factor.to_hdf5(group, "scattering_factor") if self.anomalous_real is not None: - self.anomalous_real.to_hdf5(group, 'anomalous_real') + self.anomalous_real.to_hdf5(group, "anomalous_real") if self.anomalous_imag is not None: - self.anomalous_imag.to_hdf5(group, 'anomalous_imag') + self.anomalous_imag.to_hdf5(group, "anomalous_imag") diff --git a/openmc/data/product.py b/openmc/data/product.py index 88c83b81fd4..c2c66e80640 100644 --- a/openmc/data/product.py +++ b/openmc/data/product.py @@ -37,11 +37,11 @@ class Product(EqualityMixin): """ - def __init__(self, particle='neutron'): + def __init__(self, particle="neutron"): self.applicability = [] self.decay_rate = 0.0 self.distribution = [] - self.emission_mode = 'prompt' + self.emission_mode = "prompt" self.particle = particle self.yield_ = Polynomial((1,)) # 0-order polynomial, i.e., a constant @@ -49,13 +49,16 @@ def __repr__(self): if isinstance(self.yield_, Tabulated1D): if np.all(self.yield_.y == self.yield_.y[0]): return "".format( - self.particle, self.emission_mode, self.yield_.y[0]) + self.particle, self.emission_mode, self.yield_.y[0] + ) else: return "".format( - self.particle, self.emission_mode) + self.particle, self.emission_mode + ) else: return "".format( - self.particle, self.emission_mode) + self.particle, self.emission_mode + ) @property def applicability(self): @@ -63,8 +66,9 @@ def applicability(self): @applicability.setter def applicability(self, applicability): - cv.check_type('product distribution applicability', applicability, - Iterable, Tabulated1D) + cv.check_type( + "product distribution applicability", applicability, Iterable, Tabulated1D + ) self._applicability = applicability @property @@ -73,8 +77,8 @@ def decay_rate(self): @decay_rate.setter def decay_rate(self, decay_rate): - cv.check_type('product decay rate', decay_rate, Real) - cv.check_greater_than('product decay rate', decay_rate, 0.0, True) + cv.check_type("product decay rate", decay_rate, Real) + cv.check_greater_than("product decay rate", decay_rate, 0.0, True) self._decay_rate = decay_rate @property @@ -83,8 +87,9 @@ def distribution(self): @distribution.setter def distribution(self, distribution): - cv.check_type('product angle-energy distribution', distribution, - Iterable, AngleEnergy) + cv.check_type( + "product angle-energy distribution", distribution, Iterable, AngleEnergy + ) self._distribution = distribution @property @@ -93,8 +98,9 @@ def emission_mode(self): @emission_mode.setter def emission_mode(self, emission_mode): - cv.check_value('product emission mode', emission_mode, - ('prompt', 'delayed', 'total')) + cv.check_value( + "product emission mode", emission_mode, ("prompt", "delayed", "total") + ) self._emission_mode = emission_mode @property @@ -103,7 +109,7 @@ def particle(self): @particle.setter def particle(self, particle): - cv.check_type('product particle type', particle, str) + cv.check_type("product particle type", particle, str) self._particle = particle @property @@ -112,7 +118,7 @@ def yield_(self): @yield_.setter def yield_(self, yield_): - cv.check_type('product yield', yield_, Function1D) + cv.check_type("product yield", yield_, Function1D) self._yield = yield_ def to_hdf5(self, group): @@ -124,20 +130,20 @@ def to_hdf5(self, group): HDF5 group to write to """ - group.attrs['particle'] = np.bytes_(self.particle) - group.attrs['emission_mode'] = np.bytes_(self.emission_mode) + group.attrs["particle"] = np.bytes_(self.particle) + group.attrs["emission_mode"] = np.bytes_(self.emission_mode) if self.decay_rate > 0.0: - group.attrs['decay_rate'] = self.decay_rate + group.attrs["decay_rate"] = self.decay_rate # Write yield - self.yield_.to_hdf5(group, 'yield') + self.yield_.to_hdf5(group, "yield") # Write applicability/distribution - group.attrs['n_distribution'] = len(self.distribution) + group.attrs["n_distribution"] = len(self.distribution) for i, d in enumerate(self.distribution): - dgroup = group.create_group(f'distribution_{i}') + dgroup = group.create_group(f"distribution_{i}") if self.applicability: - self.applicability[i].to_hdf5(dgroup, 'applicability') + self.applicability[i].to_hdf5(dgroup, "applicability") d.to_hdf5(dgroup) @classmethod @@ -155,25 +161,24 @@ def from_hdf5(cls, group): Reaction product """ - particle = group.attrs['particle'].decode() + particle = group.attrs["particle"].decode() p = cls(particle) - p.emission_mode = group.attrs['emission_mode'].decode() - if 'decay_rate' in group.attrs: - p.decay_rate = group.attrs['decay_rate'] + p.emission_mode = group.attrs["emission_mode"].decode() + if "decay_rate" in group.attrs: + p.decay_rate = group.attrs["decay_rate"] # Read yield - p.yield_ = Function1D.from_hdf5(group['yield']) + p.yield_ = Function1D.from_hdf5(group["yield"]) # Read applicability/distribution - n_distribution = group.attrs['n_distribution'] + n_distribution = group.attrs["n_distribution"] distribution = [] applicability = [] for i in range(n_distribution): - dgroup = group[f'distribution_{i}'] - if 'applicability' in dgroup: - applicability.append(Tabulated1D.from_hdf5( - dgroup['applicability'])) + dgroup = group[f"distribution_{i}"] + if "applicability" in dgroup: + applicability.append(Tabulated1D.from_hdf5(dgroup["applicability"])) distribution.append(AngleEnergy.from_hdf5(dgroup)) p.distribution = distribution diff --git a/openmc/data/reaction.py b/openmc/data/reaction.py index 8f90d2de471..700afd956f4 100644 --- a/openmc/data/reaction.py +++ b/openmc/data/reaction.py @@ -13,10 +13,14 @@ from .angle_energy import AngleEnergy from .correlated import CorrelatedAngleEnergy from .data import ATOMIC_SYMBOL, K_BOLTZMANN, EV_PER_MEV -from .endf import get_head_record, get_tab1_record, get_list_record, \ - get_tab2_record, get_cont_record -from .energy_distribution import EnergyDistribution, LevelInelastic, \ - DiscretePhoton +from .endf import ( + get_head_record, + get_tab1_record, + get_list_record, + get_tab2_record, + get_cont_record, +) +from .energy_distribution import EnergyDistribution, LevelInelastic, DiscretePhoton from .function import Tabulated1D, Polynomial from .kalbach_mann import KalbachMann from .laboratory import LaboratoryAngleEnergy @@ -25,47 +29,128 @@ from .uncorrelated import UncorrelatedAngleEnergy -REACTION_NAME = {1: '(n,total)', 2: '(n,elastic)', 4: '(n,level)', - 5: '(n,misc)', 11: '(n,2nd)', 16: '(n,2n)', 17: '(n,3n)', - 18: '(n,fission)', 19: '(n,f)', 20: '(n,nf)', 21: '(n,2nf)', - 22: '(n,na)', 23: '(n,n3a)', 24: '(n,2na)', 25: '(n,3na)', - 27: '(n,absorption)', 28: '(n,np)', 29: '(n,n2a)', - 30: '(n,2n2a)', 32: '(n,nd)', 33: '(n,nt)', 34: '(n,n3He)', - 35: '(n,nd2a)', 36: '(n,nt2a)', 37: '(n,4n)', 38: '(n,3nf)', - 41: '(n,2np)', 42: '(n,3np)', 44: '(n,n2p)', 45: '(n,npa)', - 91: '(n,nc)', 101: '(n,disappear)', 102: '(n,gamma)', - 103: '(n,p)', 104: '(n,d)', 105: '(n,t)', 106: '(n,3He)', - 107: '(n,a)', 108: '(n,2a)', 109: '(n,3a)', 111: '(n,2p)', - 112: '(n,pa)', 113: '(n,t2a)', 114: '(n,d2a)', 115: '(n,pd)', - 116: '(n,pt)', 117: '(n,da)', 152: '(n,5n)', 153: '(n,6n)', - 154: '(n,2nt)', 155: '(n,ta)', 156: '(n,4np)', 157: '(n,3nd)', - 158: '(n,nda)', 159: '(n,2npa)', 160: '(n,7n)', 161: '(n,8n)', - 162: '(n,5np)', 163: '(n,6np)', 164: '(n,7np)', 165: '(n,4na)', - 166: '(n,5na)', 167: '(n,6na)', 168: '(n,7na)', 169: '(n,4nd)', - 170: '(n,5nd)', 171: '(n,6nd)', 172: '(n,3nt)', 173: '(n,4nt)', - 174: '(n,5nt)', 175: '(n,6nt)', 176: '(n,2n3He)', - 177: '(n,3n3He)', 178: '(n,4n3He)', 179: '(n,3n2p)', - 180: '(n,3n2a)', 181: '(n,3npa)', 182: '(n,dt)', - 183: '(n,npd)', 184: '(n,npt)', 185: '(n,ndt)', - 186: '(n,np3He)', 187: '(n,nd3He)', 188: '(n,nt3He)', - 189: '(n,nta)', 190: '(n,2n2p)', 191: '(n,p3He)', - 192: '(n,d3He)', 193: '(n,3Hea)', 194: '(n,4n2p)', - 195: '(n,4n2a)', 196: '(n,4npa)', 197: '(n,3p)', - 198: '(n,n3p)', 199: '(n,3n2pa)', 200: '(n,5n2p)', 203: '(n,Xp)', - 204: '(n,Xd)', 205: '(n,Xt)', 206: '(n,X3He)', 207: '(n,Xa)', - 301: 'heating', 444: 'damage-energy', - 649: '(n,pc)', 699: '(n,dc)', 749: '(n,tc)', 799: '(n,3Hec)', - 849: '(n,ac)', 891: '(n,2nc)', 901: 'heating-local'} -REACTION_NAME.update({i: f'(n,n{i - 50})' for i in range(51, 91)}) -REACTION_NAME.update({i: f'(n,p{i - 600})' for i in range(600, 649)}) -REACTION_NAME.update({i: f'(n,d{i - 650})' for i in range(650, 699)}) -REACTION_NAME.update({i: f'(n,t{i - 700})' for i in range(700, 749)}) -REACTION_NAME.update({i: f'(n,3He{i - 750})' for i in range(750, 799)}) -REACTION_NAME.update({i: f'(n,a{i - 800})' for i in range(800, 849)}) -REACTION_NAME.update({i: f'(n,2n{i - 875})' for i in range(875, 891)}) +REACTION_NAME = { + 1: "(n,total)", + 2: "(n,elastic)", + 4: "(n,level)", + 5: "(n,misc)", + 11: "(n,2nd)", + 16: "(n,2n)", + 17: "(n,3n)", + 18: "(n,fission)", + 19: "(n,f)", + 20: "(n,nf)", + 21: "(n,2nf)", + 22: "(n,na)", + 23: "(n,n3a)", + 24: "(n,2na)", + 25: "(n,3na)", + 27: "(n,absorption)", + 28: "(n,np)", + 29: "(n,n2a)", + 30: "(n,2n2a)", + 32: "(n,nd)", + 33: "(n,nt)", + 34: "(n,n3He)", + 35: "(n,nd2a)", + 36: "(n,nt2a)", + 37: "(n,4n)", + 38: "(n,3nf)", + 41: "(n,2np)", + 42: "(n,3np)", + 44: "(n,n2p)", + 45: "(n,npa)", + 91: "(n,nc)", + 101: "(n,disappear)", + 102: "(n,gamma)", + 103: "(n,p)", + 104: "(n,d)", + 105: "(n,t)", + 106: "(n,3He)", + 107: "(n,a)", + 108: "(n,2a)", + 109: "(n,3a)", + 111: "(n,2p)", + 112: "(n,pa)", + 113: "(n,t2a)", + 114: "(n,d2a)", + 115: "(n,pd)", + 116: "(n,pt)", + 117: "(n,da)", + 152: "(n,5n)", + 153: "(n,6n)", + 154: "(n,2nt)", + 155: "(n,ta)", + 156: "(n,4np)", + 157: "(n,3nd)", + 158: "(n,nda)", + 159: "(n,2npa)", + 160: "(n,7n)", + 161: "(n,8n)", + 162: "(n,5np)", + 163: "(n,6np)", + 164: "(n,7np)", + 165: "(n,4na)", + 166: "(n,5na)", + 167: "(n,6na)", + 168: "(n,7na)", + 169: "(n,4nd)", + 170: "(n,5nd)", + 171: "(n,6nd)", + 172: "(n,3nt)", + 173: "(n,4nt)", + 174: "(n,5nt)", + 175: "(n,6nt)", + 176: "(n,2n3He)", + 177: "(n,3n3He)", + 178: "(n,4n3He)", + 179: "(n,3n2p)", + 180: "(n,3n2a)", + 181: "(n,3npa)", + 182: "(n,dt)", + 183: "(n,npd)", + 184: "(n,npt)", + 185: "(n,ndt)", + 186: "(n,np3He)", + 187: "(n,nd3He)", + 188: "(n,nt3He)", + 189: "(n,nta)", + 190: "(n,2n2p)", + 191: "(n,p3He)", + 192: "(n,d3He)", + 193: "(n,3Hea)", + 194: "(n,4n2p)", + 195: "(n,4n2a)", + 196: "(n,4npa)", + 197: "(n,3p)", + 198: "(n,n3p)", + 199: "(n,3n2pa)", + 200: "(n,5n2p)", + 203: "(n,Xp)", + 204: "(n,Xd)", + 205: "(n,Xt)", + 206: "(n,X3He)", + 207: "(n,Xa)", + 301: "heating", + 444: "damage-energy", + 649: "(n,pc)", + 699: "(n,dc)", + 749: "(n,tc)", + 799: "(n,3Hec)", + 849: "(n,ac)", + 891: "(n,2nc)", + 901: "heating-local", +} +REACTION_NAME.update({i: f"(n,n{i - 50})" for i in range(51, 91)}) +REACTION_NAME.update({i: f"(n,p{i - 600})" for i in range(600, 649)}) +REACTION_NAME.update({i: f"(n,d{i - 650})" for i in range(650, 699)}) +REACTION_NAME.update({i: f"(n,t{i - 700})" for i in range(700, 749)}) +REACTION_NAME.update({i: f"(n,3He{i - 750})" for i in range(750, 799)}) +REACTION_NAME.update({i: f"(n,a{i - 800})" for i in range(800, 849)}) +REACTION_NAME.update({i: f"(n,2n{i - 875})" for i in range(875, 891)}) REACTION_MT = {name: mt for mt, name in REACTION_NAME.items()} -REACTION_MT['fission'] = 18 +REACTION_MT["fission"] = 18 FISSION_MTS = (18, 19, 20, 21, 38) @@ -98,8 +183,12 @@ def _get_products(ev, mt): # Read HEAD record items = get_head_record(file_obj) - reference_frame = {1: 'laboratory', 2: 'center-of-mass', - 3: 'light-heavy', 4: 'breakup'}[items[3]] + reference_frame = { + 1: "laboratory", + 2: "center-of-mass", + 3: "light-heavy", + 4: "breakup", + }[items[3]] n_products = items[4] products = [] @@ -112,14 +201,14 @@ def _get_products(ev, mt): law = params[3] if za == 0: - p = Product('photon') + p = Product("photon") elif za == 1: - p = Product('neutron') + p = Product("neutron") elif za == 1000: - p = Product('electron') + p = Product("electron") else: Z, A = divmod(za, 1000) - p = Product(f'{ATOMIC_SYMBOL[Z]}{A}') + p = Product(f"{ATOMIC_SYMBOL[Z]}{A}") p.yield_ = yield_ @@ -150,10 +239,10 @@ def _get_products(ev, mt): elif lang == 2: # Products need to be described in the center-of-mass system product_center_of_mass = False - if reference_frame == 'center-of-mass': + if reference_frame == "center-of-mass": product_center_of_mass = True - elif reference_frame == 'light-heavy': - product_center_of_mass = (awr <= 4.0) + elif reference_frame == "light-heavy": + product_center_of_mass = awr <= 4.0 # TODO: 'breakup' logic not implemented if product_center_of_mass is False: @@ -164,10 +253,9 @@ def _get_products(ev, mt): zat = ev.target["atomic_number"] * 1000 + ev.target["mass_number"] projectile_mass = ev.projectile["mass"] - p.distribution = [KalbachMann.from_endf(file_obj, - za, - zat, - projectile_mass)] + p.distribution = [ + KalbachMann.from_endf(file_obj, za, zat, projectile_mass) + ] elif law == 2: # Discrete two-body scattering @@ -184,8 +272,7 @@ def _get_products(ev, mt): elif lang == 12: mu.append(Tabular(values[::2], values[1::2])) elif lang == 14: - mu.append(Tabular(values[::2], values[1::2], - 'log-linear')) + mu.append(Tabular(values[::2], values[1::2], "log-linear")) angle_dist = AngleDistribution(energy, mu) dist = UncorrelatedAngleEnergy(angle_dist) @@ -243,19 +330,19 @@ def _get_fission_products_ace(ace): # Either prompt nu or total nu is given if ace.xss[ace.jxs[2]] > 0: - whichnu = 'prompt' if ace.jxs[24] > 0 else 'total' + whichnu = "prompt" if ace.jxs[24] > 0 else "total" - neutron = Product('neutron') + neutron = Product("neutron") neutron.emission_mode = whichnu idx = ace.jxs[2] LNU = int(ace.xss[idx]) if LNU == 1: # Polynomial function form of nu - NC = int(ace.xss[idx+1]) - coefficients = ace.xss[idx+2 : idx+2+NC].copy() + NC = int(ace.xss[idx + 1]) + coefficients = ace.xss[idx + 2 : idx + 2 + NC].copy() for i in range(coefficients.size): - coefficients[i] *= EV_PER_MEV**(-i) + coefficients[i] *= EV_PER_MEV ** (-i) neutron.yield_ = Polynomial(coefficients) elif LNU == 2: # Tabular data form of nu @@ -266,35 +353,35 @@ def _get_fission_products_ace(ace): # Both prompt nu and total nu elif ace.xss[ace.jxs[2]] < 0: # Read prompt neutron yield - prompt_neutron = Product('neutron') - prompt_neutron.emission_mode = 'prompt' + prompt_neutron = Product("neutron") + prompt_neutron.emission_mode = "prompt" idx = ace.jxs[2] + 1 LNU = int(ace.xss[idx]) if LNU == 1: # Polynomial function form of nu - NC = int(ace.xss[idx+1]) - coefficients = ace.xss[idx+2 : idx+2+NC].copy() + NC = int(ace.xss[idx + 1]) + coefficients = ace.xss[idx + 2 : idx + 2 + NC].copy() for i in range(coefficients.size): - coefficients[i] *= EV_PER_MEV**(-i) + coefficients[i] *= EV_PER_MEV ** (-i) prompt_neutron.yield_ = Polynomial(coefficients) elif LNU == 2: # Tabular data form of nu prompt_neutron.yield_ = Tabulated1D.from_ace(ace, idx + 1) # Read total neutron yield - total_neutron = Product('neutron') - total_neutron.emission_mode = 'total' + total_neutron = Product("neutron") + total_neutron.emission_mode = "total" idx = ace.jxs[2] + int(abs(ace.xss[ace.jxs[2]])) + 1 LNU = int(ace.xss[idx]) if LNU == 1: # Polynomial function form of nu - NC = int(ace.xss[idx+1]) - coefficients = ace.xss[idx+2 : idx+2+NC].copy() + NC = int(ace.xss[idx + 1]) + coefficients = ace.xss[idx + 2 : idx + 2 + NC].copy() for i in range(coefficients.size): - coefficients[i] *= EV_PER_MEV**(-i) + coefficients[i] *= EV_PER_MEV ** (-i) total_neutron.yield_ = Polynomial(coefficients) elif LNU == 2: # Tabular data form of nu @@ -310,13 +397,13 @@ def _get_fission_products_ace(ace): # Delayed neutron precursor distribution idx = ace.jxs[25] n_group = ace.nxs[8] - total_group_probability = 0. + total_group_probability = 0.0 for group in range(n_group): - delayed_neutron = Product('neutron') - delayed_neutron.emission_mode = 'delayed' + delayed_neutron = Product("neutron") + delayed_neutron.emission_mode = "delayed" # Convert units of inverse shakes to inverse seconds - delayed_neutron.decay_rate = ace.xss[idx] * 1.e8 + delayed_neutron.decay_rate = ace.xss[idx] * 1.0e8 group_probability = Tabulated1D.from_ace(ace, idx + 1) if np.all(group_probability.y == group_probability.y[0]): @@ -336,20 +423,21 @@ def _get_fission_products_ace(ace): # Advance position nr = int(ace.xss[idx + 1]) - ne = int(ace.xss[idx + 2 + 2*nr]) - idx += 3 + 2*nr + 2*ne + ne = int(ace.xss[idx + 2 + 2 * nr]) + idx += 3 + 2 * nr + 2 * ne # Energy distribution for delayed fission neutrons location_start = int(ace.xss[ace.jxs[26] + group]) delayed_neutron.distribution.append( - AngleEnergy.from_ace(ace, ace.jxs[27], location_start)) + AngleEnergy.from_ace(ace, ace.jxs[27], location_start) + ) products.append(delayed_neutron) # Renormalize delayed neutron yields to reflect fact that in ACE # file, the sum of the group probabilities is not exactly one for product in products[1:]: - if total_group_probability > 0.: + if total_group_probability > 0.0: product.yield_.y /= total_group_probability return products, derived_products @@ -374,8 +462,8 @@ def _get_fission_products_endf(ev): derived_products = [] if (1, 456) in ev.section: - prompt_neutron = Product('neutron') - prompt_neutron.emission_mode = 'prompt' + prompt_neutron = Product("neutron") + prompt_neutron.emission_mode = "prompt" # Prompt nu values file_obj = StringIO(ev.section[1, 456]) @@ -391,8 +479,8 @@ def _get_fission_products_endf(ev): products.append(prompt_neutron) if (1, 452) in ev.section: - total_neutron = Product('neutron') - total_neutron.emission_mode = 'total' + total_neutron = Product("neutron") + total_neutron.emission_mode = "total" # Total nu values file_obj = StringIO(ev.section[1, 452]) @@ -422,14 +510,15 @@ def _get_fission_products_endf(ev): # Delayed-group constants energy independent items, decay_constants = get_list_record(file_obj) for constant in decay_constants: - delayed_neutron = Product('neutron') - delayed_neutron.emission_mode = 'delayed' + delayed_neutron = Product("neutron") + delayed_neutron.emission_mode = "delayed" delayed_neutron.decay_rate = constant products.append(delayed_neutron) elif ldg == 1: # Delayed-group constants energy dependent - raise NotImplementedError('Delayed neutron with energy-dependent ' - 'group constants.') + raise NotImplementedError( + "Delayed neutron with energy-dependent " "group constants." + ) # In MF=1, MT=455, the delayed-group abundances are actually not # specified if the group constants are energy-independent. In this case, @@ -458,9 +547,11 @@ def _get_fission_products_endf(ev): products.append(deepcopy(products[1])) elif nk != len(decay_constants): raise ValueError( - 'Number of delayed neutron fission spectra ({}) does not ' - 'match number of delayed neutron precursors ({}).'.format( - nk, len(decay_constants))) + "Number of delayed neutron fission spectra ({}) does not " + "match number of delayed neutron precursors ({}).".format( + nk, len(decay_constants) + ) + ) for i in range(nk): params, applicability = get_tab1_record(file_obj) dist = UncorrelatedAngleEnergy() @@ -494,8 +585,9 @@ def _get_fission_products_endf(ev): yield_.coef[0] *= applicability.y[0] else: raise NotImplementedError( - 'Total delayed neutron yield and delayed group ' - 'probability are both energy-dependent.') + "Total delayed neutron yield and delayed group " + "probability are both energy-dependent." + ) delayed_neutron.distribution.append(dist) @@ -524,7 +616,7 @@ def _get_activation_products(ev, rx): # decay sublibrary items = get_head_record(file_obj) n_states = items[4] - decay_sublib = (items[5] == 1) + decay_sublib = items[5] == 1 # Determine if file 9/10 are present present = {9: False, 10: False} @@ -557,9 +649,9 @@ def _get_activation_products(ev, rx): # Get GNDS name for product symbol = ATOMIC_SYMBOL[Z] if excited_state > 0: - name = f'{symbol}{A}_e{excited_state}' + name = f"{symbol}{A}_e{excited_state}" else: - name = f'{symbol}{A}' + name = f"{symbol}{A}" p = Product(name) if mf == 9: @@ -567,9 +659,9 @@ def _get_activation_products(ev, rx): else: # Re-interpolate production cross section and neutron cross # section to union energy grid - energy = np.union1d(xs.x, rx.xs['0K'].x) + energy = np.union1d(xs.x, rx.xs["0K"].x) prod_xs = xs(energy) - neutron_xs = rx.xs['0K'](energy) + neutron_xs = rx.xs["0K"](energy) idx = np.where(neutron_xs > 0) # Calculate yield as ratio @@ -606,8 +698,7 @@ def _get_photon_products_ace(ace, rx): """ n_photon_reactions = ace.nxs[6] - photon_mts = ace.xss[ace.jxs[13]:ace.jxs[13] + - n_photon_reactions].astype(int) + photon_mts = ace.xss[ace.jxs[13] : ace.jxs[13] + n_photon_reactions].astype(int) photons = [] for i in range(n_photon_reactions): @@ -618,7 +709,7 @@ def _get_photon_products_ace(ace, rx): continue # Create photon product and assign to reactions - photon = Product('photon') + photon = Product("photon") # ================================================================== # Photon yield / production cross section @@ -642,13 +733,17 @@ def _get_photon_products_ace(ace, rx): # Energy grid index at which data starts threshold_idx = int(ace.xss[idx]) - 1 n_energy = int(ace.xss[idx + 1]) - energy = ace.xss[ace.jxs[1] + threshold_idx: - ace.jxs[1] + threshold_idx + n_energy]*EV_PER_MEV + energy = ( + ace.xss[ + ace.jxs[1] + threshold_idx : ace.jxs[1] + threshold_idx + n_energy + ] + * EV_PER_MEV + ) # Get photon production cross section - photon_prod_xs = ace.xss[idx + 2:idx + 2 + n_energy] + photon_prod_xs = ace.xss[idx + 2 : idx + 2 + n_energy] neutron_xs = list(rx.xs.values())[0](energy) - idx = np.where(neutron_xs > 0.) + idx = np.where(neutron_xs > 0.0) # Calculate photon yield yield_ = np.zeros_like(photon_prod_xs) @@ -673,9 +768,8 @@ def _get_photon_products_ace(ace, rx): # No angular distribution data are given for this reaction, # isotropic scattering is asssumed in LAB energy = np.array([photon.yield_.x[0], photon.yield_.x[-1]]) - mu_isotropic = Uniform(-1., 1.) - distribution.angle = AngleDistribution( - energy, [mu_isotropic, mu_isotropic]) + mu_isotropic = Uniform(-1.0, 1.0) + distribution.angle = AngleDistribution(energy, [mu_isotropic, mu_isotropic]) else: distribution.angle = AngleDistribution.from_ace(ace, ace.jxs[17], loc) @@ -716,7 +810,7 @@ def _get_photon_products_endf(ev, rx): if n_discrete_photon > 1: items, total_yield = get_tab1_record(file_obj) for k in range(n_discrete_photon): - photon = Product('photon') + photon = Product("photon") # Get photon yield items, photon.yield_ = get_tab1_record(file_obj) @@ -730,8 +824,9 @@ def _get_photon_products_endf(ev, rx): elif law == 2: energy = items[0] primary_flag = items[2] - dist.energy = DiscretePhoton(primary_flag, energy, - ev.target['mass']) + dist.energy = DiscretePhoton( + primary_flag, energy, ev.target["mass"] + ) photon.distribution.append(dist) products.append(photon) @@ -739,21 +834,20 @@ def _get_photon_products_endf(ev, rx): elif option == 2: # Transition probability arrays given ppyield = {} - ppyield['type'] = 'transition' - ppyield['transition'] = transition = {} + ppyield["type"] = "transition" + ppyield["transition"] = transition = {} # Determine whether simple (LG=1) or complex (LG=2) transitions lg = items[3] # Get transition data items, values = get_list_record(file_obj) - transition['energy_start'] = items[0] - transition['energies'] = np.array(values[::lg + 1]) - transition['direct_probability'] = np.array(values[1::lg + 1]) + transition["energy_start"] = items[0] + transition["energies"] = np.array(values[:: lg + 1]) + transition["direct_probability"] = np.array(values[1 :: lg + 1]) if lg == 2: # Complex case - transition['conditional_probability'] = np.array( - values[2::lg + 1]) + transition["conditional_probability"] = np.array(values[2 :: lg + 1]) elif (13, rx.mt) in ev.section: file_obj = StringIO(ev.section[13, rx.mt]) @@ -764,14 +858,14 @@ def _get_photon_products_endf(ev, rx): if n_discrete_photon > 1: items, total_xs = get_tab1_record(file_obj) for k in range(n_discrete_photon): - photon = Product('photon') + photon = Product("photon") items, xs = get_tab1_record(file_obj) # Re-interpolate photon production cross section and neutron cross # section to union energy grid - energy = np.union1d(xs.x, rx.xs['0K'].x) + energy = np.union1d(xs.x, rx.xs["0K"].x) photon_prod_xs = xs(energy) - neutron_xs = rx.xs['0K'](energy) + neutron_xs = rx.xs["0K"](energy) idx = np.where(neutron_xs > 0) # Calculate yield as ratio @@ -788,8 +882,7 @@ def _get_photon_products_endf(ev, rx): elif law == 2: energy = items[1] primary_flag = items[2] - dist.energy = DiscretePhoton(primary_flag, energy, - ev.target['mass']) + dist.energy = DiscretePhoton(primary_flag, energy, ev.target["mass"]) photon.distribution.append(dist) products.append(photon) @@ -836,7 +929,7 @@ class Reaction(EqualityMixin): def __init__(self, mt): self._center_of_mass = True self._redundant = False - self._q_value = 0. + self._q_value = 0.0 self._xs = {} self._products = [] self._derived_products = [] @@ -855,7 +948,7 @@ def center_of_mass(self): @center_of_mass.setter def center_of_mass(self, center_of_mass): - cv.check_type('center of mass', center_of_mass, (bool, np.bool_)) + cv.check_type("center of mass", center_of_mass, (bool, np.bool_)) self._center_of_mass = center_of_mass @property @@ -864,7 +957,7 @@ def redundant(self): @redundant.setter def redundant(self, redundant): - cv.check_type('redundant', redundant, (bool, np.bool_)) + cv.check_type("redundant", redundant, (bool, np.bool_)) self._redundant = redundant @property @@ -873,7 +966,7 @@ def q_value(self): @q_value.setter def q_value(self, q_value): - cv.check_type('Q value', q_value, Real) + cv.check_type("Q value", q_value, Real) self._q_value = q_value @property @@ -882,7 +975,7 @@ def products(self): @products.setter def products(self, products): - cv.check_type('reaction products', products, Iterable, Product) + cv.check_type("reaction products", products, Iterable, Product) self._products = products @property @@ -891,8 +984,7 @@ def derived_products(self): @derived_products.setter def derived_products(self, derived_products): - cv.check_type('reaction derived products', derived_products, - Iterable, Product) + cv.check_type("reaction derived products", derived_products, Iterable, Product) self._derived_products = derived_products @property @@ -901,10 +993,10 @@ def xs(self): @xs.setter def xs(self, xs): - cv.check_type('reaction cross section dictionary', xs, MutableMapping) + cv.check_type("reaction cross section dictionary", xs, MutableMapping) for key, value in xs.items(): - cv.check_type('reaction cross section temperature', key, str) - cv.check_type('reaction cross section', value, Callable) + cv.check_type("reaction cross section temperature", key, str) + cv.check_type("reaction cross section", value, Callable) self._xs = xs def to_hdf5(self, group): @@ -917,22 +1009,22 @@ def to_hdf5(self, group): """ - group.attrs['mt'] = self.mt + group.attrs["mt"] = self.mt if self.mt in REACTION_NAME: - group.attrs['label'] = np.bytes_(REACTION_NAME[self.mt]) + group.attrs["label"] = np.bytes_(REACTION_NAME[self.mt]) else: - group.attrs['label'] = np.bytes_(self.mt) - group.attrs['Q_value'] = self.q_value - group.attrs['center_of_mass'] = 1 if self.center_of_mass else 0 - group.attrs['redundant'] = 1 if self.redundant else 0 + group.attrs["label"] = np.bytes_(self.mt) + group.attrs["Q_value"] = self.q_value + group.attrs["center_of_mass"] = 1 if self.center_of_mass else 0 + group.attrs["redundant"] = 1 if self.redundant else 0 for T in self.xs: Tgroup = group.create_group(T) if self.xs[T] is not None: - dset = Tgroup.create_dataset('xs', data=self.xs[T].y) - threshold_idx = getattr(self.xs[T], '_threshold_idx', 0) - dset.attrs['threshold_idx'] = threshold_idx + dset = Tgroup.create_dataset("xs", data=self.xs[T].y) + threshold_idx = getattr(self.xs[T], "_threshold_idx", 0) + dset.attrs["threshold_idx"] = threshold_idx for i, p in enumerate(self.products): - pgroup = group.create_group(f'product_{i}') + pgroup = group.create_group(f"product_{i}") p.to_hdf5(pgroup) @classmethod @@ -954,24 +1046,25 @@ def from_hdf5(cls, group, energy): """ - mt = group.attrs['mt'] + mt = group.attrs["mt"] rx = cls(mt) - rx.q_value = group.attrs['Q_value'] - rx.center_of_mass = bool(group.attrs['center_of_mass']) - rx.redundant = bool(group.attrs.get('redundant', False)) + rx.q_value = group.attrs["Q_value"] + rx.center_of_mass = bool(group.attrs["center_of_mass"]) + rx.redundant = bool(group.attrs.get("redundant", False)) # Read cross section at each temperature for T, Tgroup in group.items(): - if T.endswith('K'): - if 'xs' in Tgroup: + if T.endswith("K"): + if "xs" in Tgroup: # Make sure temperature has associated energy grid if T not in energy: raise ValueError( - 'Could not create reaction cross section for MT={} ' - 'at T={} because no corresponding energy grid ' - 'exists.'.format(mt, T)) - xs = Tgroup['xs'][()] - threshold_idx = Tgroup['xs'].attrs['threshold_idx'] + "Could not create reaction cross section for MT={} " + "at T={} because no corresponding energy grid " + "exists.".format(mt, T) + ) + xs = Tgroup["xs"][()] + threshold_idx = Tgroup["xs"].attrs["threshold_idx"] tabulated_xs = Tabulated1D(energy[T][threshold_idx:], xs) tabulated_xs._threshold_idx = threshold_idx rx.xs[T] = tabulated_xs @@ -979,12 +1072,12 @@ def from_hdf5(cls, group, energy): # Determine number of products n_product = 0 for name in group: - if name.startswith('product_'): + if name.startswith("product_"): n_product += 1 # Read reaction products for i in range(n_product): - pgroup = group[f'product_{i}'] + pgroup = group[f"product_{i}"] rx.products.append(Product.from_hdf5(pgroup)) return rx @@ -993,18 +1086,18 @@ def from_hdf5(cls, group, energy): def from_ace(cls, ace, i_reaction): # Get nuclide energy grid n_grid = ace.nxs[3] - grid = ace.xss[ace.jxs[1]:ace.jxs[1] + n_grid]*EV_PER_MEV + grid = ace.xss[ace.jxs[1] : ace.jxs[1] + n_grid] * EV_PER_MEV # Convert data temperature to a "300.0K" number for indexing # temperature data - strT = str(int(round(ace.temperature*EV_PER_MEV / K_BOLTZMANN))) + "K" + strT = str(int(round(ace.temperature * EV_PER_MEV / K_BOLTZMANN))) + "K" if i_reaction > 0: mt = int(ace.xss[ace.jxs[3] + i_reaction - 1]) rx = cls(mt) # Get Q-value of reaction - rx.q_value = ace.xss[ace.jxs[4] + i_reaction - 1]*EV_PER_MEV + rx.q_value = ace.xss[ace.jxs[4] + i_reaction - 1] * EV_PER_MEV # ================================================================== # CROSS SECTION @@ -1017,10 +1110,10 @@ def from_ace(cls, ace, i_reaction): # Determine number of energies in reaction n_energy = int(ace.xss[ace.jxs[7] + loc]) - energy = grid[threshold_idx:threshold_idx + n_energy] + energy = grid[threshold_idx : threshold_idx + n_energy] # Read reaction cross section - xs = ace.xss[ace.jxs[7] + loc + 1:ace.jxs[7] + loc + 1 + n_energy] + xs = ace.xss[ace.jxs[7] + loc + 1 : ace.jxs[7] + loc + 1 + n_energy] # For damage energy production, convert to eV if mt == 444: @@ -1028,8 +1121,10 @@ def from_ace(cls, ace, i_reaction): # Fix negatives -- known issue for Y89 in JEFF 3.2 if np.any(xs < 0.0): - warn("Negative cross sections found for MT={} in {}. Setting " - "to zero.".format(rx.mt, ace.name)) + warn( + "Negative cross sections found for MT={} in {}. Setting " + "to zero.".format(rx.mt, ace.name) + ) xs[xs < 0.0] = 0.0 tabulated_xs = Tabulated1D(energy, xs) @@ -1041,7 +1136,7 @@ def from_ace(cls, ace, i_reaction): # Determine multiplicity ty = int(ace.xss[ace.jxs[5] + i_reaction - 1]) - rx.center_of_mass = (ty < 0) + rx.center_of_mass = ty < 0 if i_reaction < ace.nxs[5] + 1: if ty != 19: if abs(ty) > 100: @@ -1052,7 +1147,7 @@ def from_ace(cls, ace, i_reaction): # 0-order polynomial i.e. a constant yield_ = Polynomial((abs(ty),)) - neutron = Product('neutron') + neutron = Product("neutron") neutron.yield_ = yield_ rx.products.append(neutron) else: @@ -1060,7 +1155,7 @@ def from_ace(cls, ace, i_reaction): rx.products, rx.derived_products = _get_fission_products_ace(ace) for p in rx.products: - if p.emission_mode in ('prompt', 'total'): + if p.emission_mode in ("prompt", "total"): neutron = p break else: @@ -1070,12 +1165,14 @@ def from_ace(cls, ace, i_reaction): lnw = int(ace.xss[ace.jxs[10] + i_reaction - 1]) while lnw > 0: # Applicability of this distribution - neutron.applicability.append(Tabulated1D.from_ace( - ace, ace.jxs[11] + lnw + 2)) + neutron.applicability.append( + Tabulated1D.from_ace(ace, ace.jxs[11] + lnw + 2) + ) # Read energy distribution data - neutron.distribution.append(AngleEnergy.from_ace( - ace, ace.jxs[11], lnw, rx)) + neutron.distribution.append( + AngleEnergy.from_ace(ace, ace.jxs[11], lnw, rx) + ) lnw = int(ace.xss[ace.jxs[11] + lnw - 1]) @@ -1085,12 +1182,14 @@ def from_ace(cls, ace, i_reaction): rx = cls(mt) # Get elastic cross section values - elastic_xs = ace.xss[ace.jxs[1] + 3*n_grid:ace.jxs[1] + 4*n_grid] + elastic_xs = ace.xss[ace.jxs[1] + 3 * n_grid : ace.jxs[1] + 4 * n_grid] # Fix negatives -- known issue for Ti46,49,50 in JEFF 3.2 if np.any(elastic_xs < 0.0): - warn("Negative elastic scattering cross section found for {}. " - "Setting to zero.".format(ace.name)) + warn( + "Negative elastic scattering cross section found for {}. " + "Setting to zero.".format(ace.name) + ) elastic_xs[elastic_xs < 0.0] = 0.0 tabulated_xs = Tabulated1D(grid, elastic_xs) @@ -1098,7 +1197,7 @@ def from_ace(cls, ace, i_reaction): rx.xs[strT] = tabulated_xs # No energy distribution for elastic scattering - neutron = Product('neutron') + neutron = Product("neutron") neutron.distribution.append(UncorrelatedAngleEnergy()) rx.products.append(neutron) @@ -1115,7 +1214,7 @@ def from_ace(cls, ace, i_reaction): elif loc == 0: # Angular distribution is isotropic energy = [0.0, grid[-1]] - mu = Uniform(-1., 1.) + mu = Uniform(-1.0, 1.0) angle_dist = AngleDistribution(energy, [mu, mu]) else: angle_dist = AngleDistribution.from_ace(ace, ace.jxs[9], loc) @@ -1156,7 +1255,7 @@ def from_endf(cls, ev, mt): if (3, mt) in ev.section: file_obj = StringIO(ev.section[3, mt]) get_head_record(file_obj) - params, rx.xs['0K'] = get_tab1_record(file_obj) + params, rx.xs["0K"] = get_tab1_record(file_obj) rx.q_value = params[1] # Get fission product yields (nu) as well as delayed neutron energy @@ -1167,7 +1266,7 @@ def from_endf(cls, ev, mt): if (6, mt) in ev.section: # Product angle-energy distribution for product in _get_products(ev, mt): - if mt in FISSION_MTS and product.particle == 'neutron': + if mt in FISSION_MTS and product.particle == "neutron": rx.products[0].applicability = product.applicability rx.products[0].distribution = product.distribution else: @@ -1175,7 +1274,7 @@ def from_endf(cls, ev, mt): elif (4, mt) in ev.section or (5, mt) in ev.section: # Uncorrelated angle-energy distribution - neutron = Product('neutron') + neutron = Product("neutron") # Note that the energy distribution for MT=455 is read in # _get_fission_products_endf rather than here @@ -1201,9 +1300,9 @@ def from_endf(cls, ev, mt): # necessary parameters to create a LevelInelastic object dist = UncorrelatedAngleEnergy() - A = ev.target['mass'] - threshold = (A + 1.)/A*abs(rx.q_value) - mass_ratio = (A/(A + 1.))**2 + A = ev.target["mass"] + threshold = (A + 1.0) / A * abs(rx.q_value) + mass_ratio = (A / (A + 1.0)) ** 2 dist.energy = LevelInelastic(threshold, mass_ratio) neutron.distribution.append(dist) diff --git a/openmc/data/resonance.py b/openmc/data/resonance.py index 31e230df588..fd415762465 100644 --- a/openmc/data/resonance.py +++ b/openmc/data/resonance.py @@ -8,9 +8,16 @@ import openmc.checkvalue as cv from .data import NEUTRON_MASS from .endf import get_head_record, get_cont_record, get_tab1_record, get_list_record + try: - from .reconstruct import wave_number, penetration_shift, reconstruct_mlbw, \ - reconstruct_slbw, reconstruct_rm + from .reconstruct import ( + wave_number, + penetration_shift, + reconstruct_mlbw, + reconstruct_slbw, + reconstruct_rm, + ) + _reconstruct = True except ImportError: _reconstruct = False @@ -48,16 +55,14 @@ def ranges(self): @ranges.setter def ranges(self, ranges): - cv.check_type('resonance ranges', ranges, MutableSequence) - self._ranges = cv.CheckedList(ResonanceRange, 'resonance ranges', - ranges) + cv.check_type("resonance ranges", ranges, MutableSequence) + self._ranges = cv.CheckedList(ResonanceRange, "resonance ranges", ranges) @property def resolved(self): - resolved_ranges = [r for r in self.ranges - if not isinstance(r, Unresolved)] + resolved_ranges = [r for r in self.ranges if not isinstance(r, Unresolved)] if len(resolved_ranges) > 1: - raise ValueError('More than one resolved range present') + raise ValueError("More than one resolved range present") elif len(resolved_ranges) == 0: return None else: @@ -95,7 +100,7 @@ def from_endf(cls, ev): ranges = [] for _ in range(n_isotope): items = get_cont_record(file_obj) - fission_widths = (items[3] == 1) # fission widths are given? + fission_widths = items[3] == 1 # fission widths are given? n_ranges = items[4] # number of resonance energy ranges for j in range(n_ranges): @@ -151,6 +156,7 @@ class ResonanceRange: Intrinsic spin, :math:`I`, of the target nuclide """ + def __init__(self, target_spin, energy_min, energy_max, channel, scattering): self.target_spin = target_spin self.energy_min = energy_min @@ -204,7 +210,9 @@ def from_endf(cls, ev, file_obj, items): ap = Polynomial((items[1],)) # Calculate channel radius from ENDF-102 equation D.14 - a = Polynomial((0.123 * (NEUTRON_MASS*ev.target['mass'])**(1./3.) + 0.08,)) + a = Polynomial( + (0.123 * (NEUTRON_MASS * ev.target["mass"]) ** (1.0 / 3.0) + 0.08,) + ) return cls(target_spin, energy_min, energy_max, {0: a}, {0: ap}) @@ -295,8 +303,7 @@ class MultiLevelBreitWigner(ResonanceRange): """ def __init__(self, target_spin, energy_min, energy_max, channel, scattering): - super().__init__(target_spin, energy_min, energy_max, channel, - scattering) + super().__init__(target_spin, energy_min, energy_max, channel, scattering) self.parameters = None self.q_value = {} self.atomic_weight_ratio = None @@ -354,7 +361,7 @@ def from_endf(cls, ev, file_obj, items): competitive = items[3] # Calculate channel radius from ENDF-102 equation D.14 - a = Polynomial((0.123 * (NEUTRON_MASS*awri)**(1./3.) + 0.08,)) + a = Polynomial((0.123 * (NEUTRON_MASS * awri) ** (1.0 / 3.0) + 0.08,)) # Construct scattering and channel radius if nro == 0: @@ -384,16 +391,26 @@ def from_endf(cls, ev, file_obj, items): gx = np.zeros_like(gt) for i, E in enumerate(energy): - records.append([energy[i], l_value, spin[i], gt[i], gn[i], - gg[i], gf[i], gx[i]]) - - columns = ['energy', 'L', 'J', 'totalWidth', 'neutronWidth', - 'captureWidth', 'fissionWidth', 'competitiveWidth'] + records.append( + [energy[i], l_value, spin[i], gt[i], gn[i], gg[i], gf[i], gx[i]] + ) + + columns = [ + "energy", + "L", + "J", + "totalWidth", + "neutronWidth", + "captureWidth", + "fissionWidth", + "competitiveWidth", + ] parameters = pd.DataFrame.from_records(records, columns=columns) # Create instance of class - mlbw = cls(target_spin, energy_min, energy_max, - channel_radius, scattering_radius) + mlbw = cls( + target_spin, energy_min, energy_max, channel_radius, scattering_radius + ) mlbw.q_value = q_value mlbw.atomic_weight_ratio = awri mlbw.parameters = parameters @@ -422,21 +439,21 @@ def _prepare_resonances(self): # Determine penetration and shift corresponding to resonance energy k = wave_number(A, E) - rho = k*self.channel_radius[l](E) + rho = k * self.channel_radius[l](E) p[i], s[i] = penetration_shift(l, rho) # Determine penetration at modified energy for competitive reaction if gx > 0: - Ex = E + self.q_value[l]*(A + 1)/A - rho = k*self.channel_radius[l](Ex) + Ex = E + self.q_value[l] * (A + 1) / A + rho = k * self.channel_radius[l](Ex) px[i], sx[i] = penetration_shift(l, rho) else: px[i] = sx[i] = 0.0 - df['p'] = p - df['s'] = s - df['px'] = px - df['sx'] = sx + df["p"] = p + df["s"] = s + df["px"] = px + df["sx"] = sx self._l_values = np.array(l_values) self._competitive = np.array(competitive) @@ -495,8 +512,7 @@ class SingleLevelBreitWigner(MultiLevelBreitWigner): """ def __init__(self, target_spin, energy_min, energy_max, channel, scattering): - super().__init__(target_spin, energy_min, energy_max, channel, - scattering) + super().__init__(target_spin, energy_min, energy_max, channel, scattering) # Set resonance reconstruction function if _reconstruct: @@ -554,8 +570,7 @@ class ReichMoore(ResonanceRange): """ def __init__(self, target_spin, energy_min, energy_max, channel, scattering): - super().__init__(target_spin, energy_min, energy_max, channel, - scattering) + super().__init__(target_spin, energy_min, energy_max, channel, scattering) self.parameters = None self.angle_distribution = False self.num_l_convergence = 0 @@ -597,7 +612,7 @@ def from_endf(cls, ev, file_obj, items): items = get_cont_record(file_obj) target_spin = items[0] ap = Polynomial((items[1],)) - angle_distribution = (items[3] == 1) # Flag for angular distribution + angle_distribution = items[3] == 1 # Flag for angular distribution NLS = items[4] # Number of l-values num_l_convergence = items[5] # Number of l-values for convergence @@ -612,7 +627,7 @@ def from_endf(cls, ev, file_obj, items): awri = items[0] # Calculate channel radius from ENDF-102 equation D.14 - a = Polynomial((0.123 * (NEUTRON_MASS*awri)**(1./3.) + 0.08,)) + a = Polynomial((0.123 * (NEUTRON_MASS * awri) ** (1.0 / 3.0) + 0.08,)) # Construct scattering and channel radius if nro == 0: @@ -639,17 +654,24 @@ def from_endf(cls, ev, file_obj, items): gfb = values[5::6] for i, E in enumerate(energy): - records.append([energy[i], l_value, spin[i], gn[i], gg[i], - gfa[i], gfb[i]]) + records.append( + [energy[i], l_value, spin[i], gn[i], gg[i], gfa[i], gfb[i]] + ) # Create pandas DataFrame with resonance data - columns = ['energy', 'L', 'J', 'neutronWidth', 'captureWidth', - 'fissionWidthA', 'fissionWidthB'] + columns = [ + "energy", + "L", + "J", + "neutronWidth", + "captureWidth", + "fissionWidthA", + "fissionWidthB", + ] parameters = pd.DataFrame.from_records(records, columns=columns) # Create instance of ReichMoore - rm = cls(target_spin, energy_min, energy_max, - channel_radius, scattering_radius) + rm = cls(target_spin, energy_min, energy_max, channel_radius, scattering_radius) rm.parameters = parameters rm.angle_distribution = angle_distribution rm.num_l_convergence = num_l_convergence @@ -676,16 +698,15 @@ def _prepare_resonances(self): # Determine penetration and shift corresponding to resonance energy k = wave_number(A, E) - rho = k*self.channel_radius[l](E) + rho = k * self.channel_radius[l](E) p[i], s[i] = penetration_shift(l, rho) - df['p'] = p - df['s'] = s + df["p"] = p + df["s"] = s self._l_values = np.array(l_values) - for (l, J) in lj_values: - self._parameter_matrix[l, J] = df[(df.L == l) & - (abs(df.J) == J)].values + for l, J in lj_values: + self._parameter_matrix[l, J] = df[(df.L == l) & (abs(df.J) == J)].values self._prepared = True @@ -758,7 +779,7 @@ def from_endf(cls, ev, file_obj, items): energy_min, energy_max = items[0:2] items = get_cont_record(file_obj) - reduced_width = (items[2] == 1) # reduced width amplitude? + reduced_width = items[2] == 1 # reduced width amplitude? formalism = items[3] # Specify which formulae are used n_spin_groups = items[4] # Number of Jpi values (NJS) @@ -766,33 +787,38 @@ def from_endf(cls, ev, file_obj, items): spin_groups = [] items, values = get_list_record(file_obj) - n_pairs = items[5]//2 # Number of particle pairs (NPP) + n_pairs = items[5] // 2 # Number of particle pairs (NPP) for i in range(n_pairs): - first = {'mass': values[12*i], - 'z': int(values[12*i + 2]), - 'spin': values[12*i + 4], - 'parity': values[12*i + 10]} - second = {'mass': values[12*i + 1], - 'z': int(values[12*i + 3]), - 'spin': values[12*i + 5], - 'parity': values[12*i + 11]} - - q_value = values[12*i + 6] - penetrability = values[12*i + 7] - shift = values[12*i + 8] - mt = int(values[12*i + 9]) - - particle_pairs.append(ParticlePair( - first, second, q_value, penetrability, shift, mt)) + first = { + "mass": values[12 * i], + "z": int(values[12 * i + 2]), + "spin": values[12 * i + 4], + "parity": values[12 * i + 10], + } + second = { + "mass": values[12 * i + 1], + "z": int(values[12 * i + 3]), + "spin": values[12 * i + 5], + "parity": values[12 * i + 11], + } + + q_value = values[12 * i + 6] + penetrability = values[12 * i + 7] + shift = values[12 * i + 8] + mt = int(values[12 * i + 9]) + + particle_pairs.append( + ParticlePair(first, second, q_value, penetrability, shift, mt) + ) # loop over spin groups for i in range(n_spin_groups): items, values = get_list_record(file_obj) J = items[0] if J == 0.0: - parity = '+' if items[1] == 1.0 else '-' + parity = "+" if items[1] == 1.0 else "-" else: - parity = '+' if J > 0. else '-' + parity = "+" if J > 0.0 else "-" J = abs(J) kbk = items[2] kps = items[3] @@ -800,37 +826,37 @@ def from_endf(cls, ev, file_obj, items): channels = [] for j in range(n_channels): channel = {} - channel['particle_pair'] = particle_pairs[ - int(values[6*j]) - 1] - channel['l'] = values[6*j + 1] - channel['spin'] = values[6*j + 2] - channel['boundary'] = values[6*j + 3] - channel['effective_radius'] = values[6*j + 4] - channel['true_radius'] = values[6*j + 5] + channel["particle_pair"] = particle_pairs[int(values[6 * j]) - 1] + channel["l"] = values[6 * j + 1] + channel["spin"] = values[6 * j + 2] + channel["boundary"] = values[6 * j + 3] + channel["effective_radius"] = values[6 * j + 4] + channel["true_radius"] = values[6 * j + 5] channels.append(channel) # Read resonance energies and widths items, values = get_list_record(file_obj) n_resonances = items[3] records = [] - m = n_channels//6 + 1 + m = n_channels // 6 + 1 for j in range(n_resonances): - energy = values[6*m*j] - records.append([energy] + [values[6*m*j + k + 1] - for k in range(n_channels)]) + energy = values[6 * m * j] + records.append( + [energy] + [values[6 * m * j + k + 1] for k in range(n_channels)] + ) # Determine column names - columns = ['energy'] + columns = ["energy"] for channel in channels: - mt = channel['particle_pair'].mt + mt = channel["particle_pair"].mt if mt == 2: - columns.append('neutronWidth') + columns.append("neutronWidth") elif mt == 18: - columns.append('fissionWidth') + columns.append("fissionWidth") elif mt == 102: - columns.append('captureWidth') + columns.append("captureWidth") else: - columns.append(f'width (MT={mt})') + columns.append(f"width (MT={mt})") # Create Pandas dataframe with resonance parameters parameters = pd.DataFrame.from_records(records, columns=columns) @@ -863,8 +889,7 @@ def from_endf(cls, ev, file_obj, items): class ParticlePair: - def __init__(self, first, second, q_value, penetrability, - shift, mt): + def __init__(self, first, second, q_value, penetrability, shift, mt): self.first = first self.second = second self.q_value = q_value @@ -896,7 +921,7 @@ def __init__(self, spin, parity, channels, parameters): self.parameters = parameters def __repr__(self): - return f'' + return f"" class Unresolved(ResonanceRange): @@ -940,8 +965,7 @@ class Unresolved(ResonanceRange): """ def __init__(self, target_spin, energy_min, energy_max, channel, scattering): - super().__init__(target_spin, energy_min, energy_max, channel, - scattering) + super().__init__(target_spin, energy_min, energy_max, channel, scattering) self.energies = None self.parameters = None self.add_to_background = False @@ -981,13 +1005,13 @@ def from_endf(cls, file_obj, items, fission_widths): target_spin = items[0] if nro == 0: ap = Polynomial((items[1],)) - add_to_background = (items[2] == 0) + add_to_background = items[2] == 0 if not fission_widths and formalism == 1: # Case A -- fission widths not given, all parameters are # energy-independent NLS = items[4] - columns = ['L', 'J', 'd', 'amun', 'gn0', 'gg'] + columns = ["L", "J", "d", "amun", "gn0", "gg"] records = [] for ls in range(NLS): items, values = get_list_record(file_obj) @@ -995,7 +1019,7 @@ def from_endf(cls, file_obj, items, fission_widths): l = items[2] NJS = items[5] for j in range(NJS): - d, j, amun, gn0, gg = values[6*j:6*j + 5] + d, j, amun, gn0, gg = values[6 * j : 6 * j + 5] records.append([l, j, d, amun, gn0, gg]) parameters = pd.DataFrame.from_records(records, columns=columns) energies = None @@ -1007,10 +1031,10 @@ def from_endf(cls, file_obj, items, fission_widths): target_spin = items[0] if nro == 0: ap = Polynomial((items[1],)) - add_to_background = (items[2] == 0) + add_to_background = items[2] == 0 NE, NLS = items[4:6] records = [] - columns = ['L', 'J', 'E', 'd', 'amun', 'amuf', 'gn0', 'gg', 'gf'] + columns = ["L", "J", "E", "d", "amun", "amuf", "gn0", "gg", "gf"] for ls in range(NLS): items = get_cont_record(file_obj) awri = items[0] @@ -1032,8 +1056,19 @@ def from_endf(cls, file_obj, items, fission_widths): elif formalism == 2: # Case C -- all parameters are energy-dependent NLS = items[4] - columns = ['L', 'J', 'E', 'd', 'amux', 'amun', 'amuf', 'gx', 'gn0', - 'gg', 'gf'] + columns = [ + "L", + "J", + "E", + "d", + "amux", + "amun", + "amuf", + "gx", + "gn0", + "gg", + "gf", + ] records = [] for ls in range(NLS): items = get_cont_record(file_obj) @@ -1049,19 +1084,18 @@ def from_endf(cls, file_obj, items, fission_widths): amuf = values[5] energies = [] for k in range(1, ne + 1): - E = values[6*k] - d = values[6*k + 1] - gx = values[6*k + 2] - gn0 = values[6*k + 3] - gg = values[6*k + 4] - gf = values[6*k + 5] + E = values[6 * k] + d = values[6 * k + 1] + gx = values[6 * k + 2] + gn0 = values[6 * k + 3] + gg = values[6 * k + 4] + gf = values[6 * k + 5] energies.append(E) - records.append([l, j, E, d, amux, amun, amuf, gx, gn0, - gg, gf]) + records.append([l, j, E, d, amux, amun, amuf, gx, gn0, gg, gf]) parameters = pd.DataFrame.from_records(records, columns=columns) # Calculate channel radius from ENDF-102 equation D.14 - a = Polynomial((0.123 * (NEUTRON_MASS*awri)**(1./3.) + 0.08,)) + a = Polynomial((0.123 * (NEUTRON_MASS * awri) ** (1.0 / 3.0) + 0.08,)) # Determine scattering and channel radius if nro == 0: @@ -1079,8 +1113,9 @@ def from_endf(cls, file_obj, items, fission_widths): elif naps == 2: channel_radius = ap - urr = cls(target_spin, energy_min, energy_max, channel_radius, - scattering_radius) + urr = cls( + target_spin, energy_min, energy_max, channel_radius, scattering_radius + ) urr.parameters = parameters urr.add_to_background = add_to_background urr.atomic_weight_ratio = awri @@ -1089,11 +1124,12 @@ def from_endf(cls, file_obj, items, fission_widths): return urr -_FORMALISMS = {0: ResonanceRange, - 1: SingleLevelBreitWigner, - 2: MultiLevelBreitWigner, - 3: ReichMoore, - 7: RMatrixLimited} +_FORMALISMS = { + 0: ResonanceRange, + 1: SingleLevelBreitWigner, + 2: MultiLevelBreitWigner, + 3: ReichMoore, + 7: RMatrixLimited, +} -_RESOLVED = (SingleLevelBreitWigner, MultiLevelBreitWigner, - ReichMoore, RMatrixLimited) +_RESOLVED = (SingleLevelBreitWigner, MultiLevelBreitWigner, ReichMoore, RMatrixLimited) diff --git a/openmc/data/resonance_covariance.py b/openmc/data/resonance_covariance.py index 7096570449c..4d97a2f6725 100644 --- a/openmc/data/resonance_covariance.py +++ b/openmc/data/resonance_covariance.py @@ -30,14 +30,14 @@ def _add_file2_contributions(file32params, file2params): """ # Use l-values and competitiveWidth from File 2 data # Re-sort File 2 by energy to match File 32 - file2params = file2params.sort_values(by=['energy']) + file2params = file2params.sort_values(by=["energy"]) file2params.reset_index(drop=True, inplace=True) # Sort File 32 parameters by energy as well (maintaining index) - file32params.sort_values(by=['energy'], inplace=True) + file32params.sort_values(by=["energy"], inplace=True) # Add in values (.values converts to array first to ignore index) - file32params['L'] = file2params['L'].values - if 'competitiveWidth' in file2params.columns: - file32params['competitiveWidth'] = file2params['competitiveWidth'].values + file32params["L"] = file2params["L"].values + if "competitiveWidth" in file2params.columns: + file32params["competitiveWidth"] = file2params["competitiveWidth"].values # Resort to File 32 order (by L then by E) for use with covariance file32params.sort_index(inplace=True) return file32params @@ -64,9 +64,10 @@ def ranges(self): @ranges.setter def ranges(self, ranges): - cv.check_type('resonance ranges', ranges, MutableSequence) - self._ranges = cv.CheckedList(ResonanceCovarianceRange, - 'resonance range', ranges) + cv.check_type("resonance ranges", ranges, MutableSequence) + self._ranges = cv.CheckedList( + ResonanceCovarianceRange, "resonance range", ranges + ) @classmethod def from_endf(cls, ev, resonances): @@ -107,20 +108,25 @@ def from_endf(cls, ev, resonances): # Throw error for unsupported formalisms if formalism in [0, 7]: - error = 'LRF='+str(formalism)+' covariance not supported '\ - 'for this formalism' + error = ( + "LRF=" + str(formalism) + " covariance not supported " + "for this formalism" + ) raise NotImplementedError(error) if unresolved_flag in (0, 1): # Resolved resonance region resonance = resonances.ranges[j] - erange = _FORMALISMS[formalism].from_endf(ev, file_obj, - items, resonance) + erange = _FORMALISMS[formalism].from_endf( + ev, file_obj, items, resonance + ) ranges.append(erange) elif unresolved_flag == 2: - warn = 'Unresolved resonance not supported. Covariance '\ - 'values for the unresolved region not imported.' + warn = ( + "Unresolved resonance not supported. Covariance " + "values for the unresolved region not imported." + ) warnings.warn(warn) return cls(ranges) @@ -155,6 +161,7 @@ class ResonanceCovarianceRange: formalism : str String descriptor of formalism """ + def __init__(self, energy_min, energy_max): self.energy_min = energy_min self.energy_max = energy_max @@ -192,15 +199,16 @@ def subset(self, parameter_str, bounds): res_cov_range.parameters = parameters[mask] indices = res_cov_range.parameters.index.values # Build subset of covariance - sub_cov_dim = len(indices)*mpar + sub_cov_dim = len(indices) * mpar cov_subset_vals = [] for index1 in indices: for i in range(mpar): for index2 in indices: for j in range(mpar): - if index2*mpar+j >= index1*mpar+i: - cov_subset_vals.append(cov[index1*mpar+i, - index2*mpar+j]) + if index2 * mpar + j >= index1 * mpar + i: + cov_subset_vals.append( + cov[index1 * mpar + i, index2 * mpar + j] + ) cov_subset = np.zeros([sub_cov_dim, sub_cov_dim]) tri_indices = np.triu_indices(sub_cov_dim) @@ -225,9 +233,11 @@ def sample(self, n_samples): List of samples size `n_samples` """ - warn_str = 'Sampling routine does not guarantee positive values for '\ - 'parameters. This can lead to undefined behavior in the '\ - 'reconstruction routine.' + warn_str = ( + "Sampling routine does not guarantee positive values for " + "parameters. This can lead to undefined behavior in the " + "reconstruction routine." + ) warnings.warn(warn_str) parameters = self.parameters cov = self.covariance @@ -240,61 +250,103 @@ def sample(self, n_samples): # Handling MLBW/SLBW sampling rng = np.random.default_rng() - if formalism == 'mlbw' or formalism == 'slbw': - params = ['energy', 'neutronWidth', 'captureWidth', 'fissionWidth', - 'competitiveWidth'] + if formalism == "mlbw" or formalism == "slbw": + params = [ + "energy", + "neutronWidth", + "captureWidth", + "fissionWidth", + "competitiveWidth", + ] param_list = params[:mpar] mean_array = parameters[param_list].values mean = mean_array.flatten() par_samples = rng.multivariate_normal(mean, cov, size=n_samples) - spin = parameters['J'].values - l_value = parameters['L'].values + spin = parameters["J"].values + l_value = parameters["L"].values for sample in par_samples: energy = sample[0::mpar] gn = sample[1::mpar] gg = sample[2::mpar] - gf = sample[3::mpar] if mpar > 3 else parameters['fissionWidth'].values - gx = sample[4::mpar] if mpar > 4 else parameters['competitiveWidth'].values + gf = sample[3::mpar] if mpar > 3 else parameters["fissionWidth"].values + gx = ( + sample[4::mpar] + if mpar > 4 + else parameters["competitiveWidth"].values + ) gt = gn + gg + gf + gx records = [] for j, E in enumerate(energy): - records.append([energy[j], l_value[j], spin[j], gt[j], - gn[j], gg[j], gf[j], gx[j]]) - columns = ['energy', 'L', 'J', 'totalWidth', 'neutronWidth', - 'captureWidth', 'fissionWidth', 'competitiveWidth'] - sample_params = pd.DataFrame.from_records(records, - columns=columns) + records.append( + [ + energy[j], + l_value[j], + spin[j], + gt[j], + gn[j], + gg[j], + gf[j], + gx[j], + ] + ) + columns = [ + "energy", + "L", + "J", + "totalWidth", + "neutronWidth", + "captureWidth", + "fissionWidth", + "competitiveWidth", + ] + sample_params = pd.DataFrame.from_records(records, columns=columns) # Copy ResonanceRange object res_range = copy.copy(self.file2res) res_range.parameters = sample_params samples.append(res_range) # Handling RM sampling - elif formalism == 'rm': - params = ['energy', 'neutronWidth', 'captureWidth', - 'fissionWidthA', 'fissionWidthB'] + elif formalism == "rm": + params = [ + "energy", + "neutronWidth", + "captureWidth", + "fissionWidthA", + "fissionWidthB", + ] param_list = params[:mpar] mean_array = parameters[param_list].values mean = mean_array.flatten() par_samples = rng.multivariate_normal(mean, cov, size=n_samples) - spin = parameters['J'].values - l_value = parameters['L'].values + spin = parameters["J"].values + l_value = parameters["L"].values for sample in par_samples: energy = sample[0::mpar] gn = sample[1::mpar] gg = sample[2::mpar] - gfa = sample[3::mpar] if mpar > 3 else parameters['fissionWidthA'].values - gfb = sample[4::mpar] if mpar > 3 else parameters['fissionWidthB'].values + gfa = ( + sample[3::mpar] if mpar > 3 else parameters["fissionWidthA"].values + ) + gfb = ( + sample[4::mpar] if mpar > 3 else parameters["fissionWidthB"].values + ) records = [] for j, E in enumerate(energy): - records.append([energy[j], l_value[j], spin[j], gn[j], - gg[j], gfa[j], gfb[j]]) - columns = ['energy', 'L', 'J', 'neutronWidth', - 'captureWidth', 'fissionWidthA', 'fissionWidthB'] - sample_params = pd.DataFrame.from_records(records, - columns=columns) + records.append( + [energy[j], l_value[j], spin[j], gn[j], gg[j], gfa[j], gfb[j]] + ) + columns = [ + "energy", + "L", + "J", + "neutronWidth", + "captureWidth", + "fissionWidthA", + "fissionWidthB", + ] + sample_params = pd.DataFrame.from_records(records, columns=columns) # Copy ResonanceRange object res_range = copy.copy(self.file2res) res_range.parameters = sample_params @@ -333,15 +385,16 @@ class MultiLevelBreitWignerCovariance(ResonanceCovarianceRange): """ - def __init__(self, energy_min, energy_max, parameters, covariance, mpar, - lcomp, file2res): + def __init__( + self, energy_min, energy_max, parameters, covariance, mpar, lcomp, file2res + ): super().__init__(energy_min, energy_max) self.parameters = parameters self.covariance = covariance self.mpar = mpar self.lcomp = lcomp self.file2res = copy.copy(file2res) - self.formalism = 'mlbw' + self.formalism = "mlbw" @classmethod def from_endf(cls, ev, file_obj, items, resonance): @@ -390,7 +443,7 @@ def from_endf(cls, ev, file_obj, items, resonance): items, values = endf.get_list_record(file_obj) mpar = items[2] num_res = items[5] - num_par_vals = num_res*6 + num_par_vals = num_res * 6 res_values = values[:num_par_vals] cov_values = values[num_par_vals:] @@ -402,11 +455,10 @@ def from_endf(cls, ev, file_obj, items, resonance): gf = res_values[5::6] for i, E in enumerate(energy): - records.append([energy[i], spin[i], gt[i], gn[i], - gg[i], gf[i]]) + records.append([energy[i], spin[i], gt[i], gn[i], gg[i], gf[i]]) # Build the upper-triangular covariance matrix - cov_dim = mpar*num_res + cov_dim = mpar * num_res cov = np.zeros([cov_dim, cov_dim]) indices = np.triu_indices(cov_dim) cov[indices] = cov_values @@ -424,7 +476,7 @@ def from_endf(cls, ev, file_obj, items, resonance): gf = values[5::12] par_unc = [] for i in range(num_res): - res_unc = values[i*12+6 : i*12+12] + res_unc = values[i * 12 + 6 : i * 12 + 12] # Delete 0 values (not provided, no fission width) # DAJ/DGT always zero, DGF sometimes nonzero [1, 2, 5] res_unc_nonzero = [] @@ -437,8 +489,7 @@ def from_endf(cls, ev, file_obj, items, resonance): records = [] for i, E in enumerate(energy): - records.append([energy[i], spin[i], gt[i], gn[i], - gg[i], gf[i]]) + records.append([energy[i], spin[i], gt[i], gn[i], gg[i], gf[i]]) corr = endf.get_intg_record(file_obj) cov = np.diag(par_unc).dot(corr).dot(np.diag(par_unc)) @@ -452,7 +503,7 @@ def from_endf(cls, ev, file_obj, items, resonance): items, values = endf.get_list_record(file_obj) num_res = items[5] for j in range(num_res): - one_res = values[18*j:18*(j+1)] + one_res = values[18 * j : 18 * (j + 1)] res_values = one_res[:6] cov_values = one_res[6:] records.append(list(res_values)) @@ -460,33 +511,38 @@ def from_endf(cls, ev, file_obj, items, resonance): # Populate the coviariance matrix for this resonance # There are no covariances between resonances in lcomp=0 cov[cov_index, cov_index] = cov_values[0] - cov[cov_index+1, cov_index+1 : cov_index+2] = cov_values[1:2] - cov[cov_index+1, cov_index+3] = cov_values[4] - cov[cov_index+2, cov_index+2] = cov_values[3] - cov[cov_index+2, cov_index+3] = cov_values[5] - cov[cov_index+3, cov_index+3] = cov_values[6] + cov[cov_index + 1, cov_index + 1 : cov_index + 2] = cov_values[1:2] + cov[cov_index + 1, cov_index + 3] = cov_values[4] + cov[cov_index + 2, cov_index + 2] = cov_values[3] + cov[cov_index + 2, cov_index + 3] = cov_values[5] + cov[cov_index + 3, cov_index + 3] = cov_values[6] cov_index += 4 - if j < num_res-1: # Pad matrix for additional values - cov = np.pad(cov, ((0, 4), (0, 4)), 'constant', - constant_values=0) + if j < num_res - 1: # Pad matrix for additional values + cov = np.pad( + cov, ((0, 4), (0, 4)), "constant", constant_values=0 + ) # Create pandas DataFrame with resonance data, currently # redundant with data.IncidentNeutron.resonance - columns = ['energy', 'J', 'totalWidth', 'neutronWidth', - 'captureWidth', 'fissionWidth'] + columns = [ + "energy", + "J", + "totalWidth", + "neutronWidth", + "captureWidth", + "fissionWidth", + ] parameters = pd.DataFrame.from_records(records, columns=columns) # Determine mpar (number of parameters for each resonance in # covariance matrix) nparams, params = parameters.shape covsize = cov.shape[0] - mpar = int(covsize/nparams) + mpar = int(covsize / nparams) # Add parameters from File 2 - parameters = _add_file2_contributions(parameters, - resonance.parameters) + parameters = _add_file2_contributions(parameters, resonance.parameters) # Create instance of class - mlbw = cls(energy_min, energy_max, parameters, cov, mpar, lcomp, - resonance) + mlbw = cls(energy_min, energy_max, parameters, cov, mpar, lcomp, resonance) return mlbw @@ -522,11 +578,13 @@ class SingleLevelBreitWignerCovariance(MultiLevelBreitWignerCovariance): Corresponding resonance range with File 2 data. """ - def __init__(self, energy_min, energy_max, parameters, covariance, mpar, - lcomp, file2res): - super().__init__(energy_min, energy_max, parameters, covariance, mpar, - lcomp, file2res) - self.formalism = 'slbw' + def __init__( + self, energy_min, energy_max, parameters, covariance, mpar, lcomp, file2res + ): + super().__init__( + energy_min, energy_max, parameters, covariance, mpar, lcomp, file2res + ) + self.formalism = "slbw" class ReichMooreCovariance(ResonanceCovarianceRange): @@ -562,15 +620,16 @@ class ReichMooreCovariance(ResonanceCovarianceRange): String descriptor of formalism """ - def __init__(self, energy_min, energy_max, parameters, covariance, mpar, - lcomp, file2res): + def __init__( + self, energy_min, energy_max, parameters, covariance, mpar, lcomp, file2res + ): super().__init__(energy_min, energy_max) self.parameters = parameters self.covariance = covariance self.mpar = mpar self.lcomp = lcomp self.file2res = copy.copy(file2res) - self.formalism = 'rm' + self.formalism = "rm" @classmethod def from_endf(cls, ev, file_obj, items, resonance): @@ -619,7 +678,7 @@ def from_endf(cls, ev, file_obj, items, resonance): items, values = endf.get_list_record(file_obj) mpar = items[2] num_res = items[5] - num_par_vals = num_res*6 + num_par_vals = num_res * 6 res_values = values[:num_par_vals] cov_values = values[num_par_vals:] @@ -631,11 +690,10 @@ def from_endf(cls, ev, file_obj, items, resonance): gfb = res_values[5::6] for i, E in enumerate(energy): - records.append([energy[i], spin[i], gn[i], gg[i], - gfa[i], gfb[i]]) + records.append([energy[i], spin[i], gn[i], gg[i], gfa[i], gfb[i]]) # Build the upper-triangular covariance matrix - cov_dim = mpar*num_res + cov_dim = mpar * num_res cov = np.zeros([cov_dim, cov_dim]) indices = np.triu_indices(cov_dim) cov[indices] = cov_values @@ -653,36 +711,39 @@ def from_endf(cls, ev, file_obj, items, resonance): gfb = values[5::12] par_unc = [] for i in range(num_res): - res_unc = values[i*12+6 : i*12+12] + res_unc = values[i * 12 + 6 : i * 12 + 12] # Delete 0 values (not provided in evaluation) res_unc = [x for x in res_unc if x != 0.0] par_unc.extend(res_unc) records = [] for i, E in enumerate(energy): - records.append([energy[i], spin[i], gn[i], gg[i], - gfa[i], gfb[i]]) + records.append([energy[i], spin[i], gn[i], gg[i], gfa[i], gfb[i]]) corr = endf.get_intg_record(file_obj) cov = np.diag(par_unc).dot(corr).dot(np.diag(par_unc)) # Create pandas DataFrame with resonacne data - columns = ['energy', 'J', 'neutronWidth', 'captureWidth', - 'fissionWidthA', 'fissionWidthB'] + columns = [ + "energy", + "J", + "neutronWidth", + "captureWidth", + "fissionWidthA", + "fissionWidthB", + ] parameters = pd.DataFrame.from_records(records, columns=columns) # Determine mpar (number of parameters for each resonance in # covariance matrix) nparams, params = parameters.shape covsize = cov.shape[0] - mpar = int(covsize/nparams) + mpar = int(covsize / nparams) # Add parameters from File 2 - parameters = _add_file2_contributions(parameters, - resonance.parameters) + parameters = _add_file2_contributions(parameters, resonance.parameters) # Create instance of ReichMooreCovariance - rmc = cls(energy_min, energy_max, parameters, cov, mpar, lcomp, - resonance) + rmc = cls(energy_min, energy_max, parameters, cov, mpar, lcomp, resonance) return rmc @@ -690,6 +751,6 @@ def from_endf(cls, ev, file_obj, items, resonance): 0: ResonanceCovarianceRange, 1: SingleLevelBreitWignerCovariance, 2: MultiLevelBreitWignerCovariance, - 3: ReichMooreCovariance + 3: ReichMooreCovariance, # 7: RMatrixLimitedCovariance } diff --git a/openmc/data/thermal.py b/openmc/data/thermal.py index 6df171d3a4f..ec084910abd 100644 --- a/openmc/data/thermal.py +++ b/openmc/data/thermal.py @@ -21,69 +21,73 @@ from .angle_energy import AngleEnergy from .function import Tabulated1D, Function1D, Sum from .njoy import make_ace_thermal -from .thermal_angle_energy import (CoherentElasticAE, IncoherentElasticAE, - IncoherentElasticAEDiscrete, - IncoherentInelasticAEDiscrete, - IncoherentInelasticAE, MixedElasticAE) +from .thermal_angle_energy import ( + CoherentElasticAE, + IncoherentElasticAE, + IncoherentElasticAEDiscrete, + IncoherentInelasticAEDiscrete, + IncoherentInelasticAE, + MixedElasticAE, +) _THERMAL_NAMES = { - 'c_Al27': ('al', 'al27', 'al-27', '13-al- 27'), - 'c_Al_in_Al2O3': ('asap00', 'asap', 'al(al2o3)'), - 'c_Be': ('be', 'be-metal', 'be-met', 'be00', 'be-metal', 'be metal', '4-be'), - 'c_BeO': ('beo',), - 'c_Be_in_BeO': ('bebeo', 'be-beo', 'be-o', 'be/o', 'bbeo00', 'be(beo)'), - 'c_Be_in_Be2C': ('bebe2c',), - 'c_Be_in_FLiBe': ('beflib', 'be(flibe)'), - 'c_C6H6': ('benz', 'c6h6', 'benzine'), - 'c_C_in_SiC': ('csic', 'c-sic', 'c(3c-sic)'), - 'c_Ca_in_CaH2': ('cah', 'cah00', 'cacah2', 'ca(cah2)'), - 'c_D_in_D2O': ('dd2o', 'd-d2o', 'hwtr', 'hw', 'dhw00', 'd(d2o)'), - 'c_D_in_D2O_ice': ('dice',), - 'c_F_in_FLiBe': ('fflibe', 'f(flibe)'), - 'c_Fe56': ('fe', 'fe56', 'fe-56', '26-fe- 56'), - 'c_Graphite': ('graph', 'grph', 'gr', 'gr00', 'graphite'), - 'c_Graphite_10p': ('grph10', '10p graphit'), - 'c_Graphite_30p': ('grph30', '30p graphit'), - 'c_H_in_C5O2H8': ('lucite', 'c5o2h8', 'h-luci', 'h(lucite)'), - 'c_H_in_CaH2': ('hcah2', 'hca00', 'h(cah2)'), - 'c_H_in_CH2': ('hch2', 'poly', 'pol', 'h-poly', 'pol00', 'h(ch2)'), - 'c_H_in_CH4_liquid': ('lch4', 'lmeth', 'l-ch4'), - 'c_H_in_CH4_solid': ('sch4', 'smeth', 's-ch4'), - 'c_H_in_CH4_solid_phase_II': ('sch4p2',), - 'c_H_in_H2O': ('hh2o', 'h-h2o', 'lwtr', 'lw', 'lw00', 'h(h2o)'), - 'c_H_in_H2O_solid': ('hice', 'h-ice', 'ice00', 'h(ice-ih)', 'h(ice)'), - 'c_H_in_HF': ('hhf', 'h(hf)'), - 'c_H_in_Mesitylene': ('mesi00', 'mesi', 'mesi-phii'), - 'c_H_in_ParaffinicOil': ('hparaf', 'h(paraffin'), - 'c_H_in_Toluene': ('tol00', 'tol', 'tolue-phii'), - 'c_H_in_UH3': ('huh3', 'h(uh3)'), - 'c_H_in_YH2': ('hyh2', 'h-yh2', 'h(yh2)'), - 'c_H_in_ZrH': ('hzrh', 'h-zrh', 'h-zr', 'h/zr', 'hzr', 'hzr00', 'h(zrh)'), - 'c_H_in_ZrH2': ('hzrh2', 'h(zrh2)'), - 'c_H_in_ZrHx': ('hzrhx', 'h(zrhx)'), - 'c_Li_in_FLiBe': ('liflib', 'li(flibe)'), - 'c_Mg24': ('mg', 'mg24', 'mg00', '24-mg'), - 'c_N_in_UN': ('n-un', 'n(un)', 'n(un) l'), - 'c_O_in_Al2O3': ('osap00', 'osap', 'o(al2o3)'), - 'c_O_in_BeO': ('obeo', 'o-beo', 'o-be', 'o/be', 'obeo00', 'o(beo)'), - 'c_O_in_D2O': ('od2o', 'o-d2o', 'ohw00', 'o(d2o)'), - 'c_O_in_H2O_solid': ('oice', 'o-ice', 'o(ice-ih)'), - 'c_O_in_UO2': ('ouo2', 'o-uo2', 'o2-u', 'o2/u', 'ouo200', 'o(uo2)'), - 'c_ortho_D': ('orthod', 'orthoD', 'dortho', 'od200', 'ortod', 'ortho-d'), - 'c_ortho_H': ('orthoh', 'orthoH', 'hortho', 'oh200', 'ortoh', 'ortho-h'), - 'c_para_D': ('parad', 'paraD', 'dpara', 'pd200', 'para-d'), - 'c_para_H': ('parah', 'paraH', 'hpara', 'ph200', 'para-h'), - 'c_Si28': ('si00', 'sili', 'si'), - 'c_Si_in_SiC': ('sisic', 'si-sic', 'si(3c-sic)'), - 'c_SiO2_alpha': ('sio2', 'sio2a', 'sio2alpha'), - 'c_SiO2_beta': ('sio2b', 'sio2beta'), - 'c_U_in_UN': ('u-un', 'u(un)', 'u(un) l'), - 'c_U_in_UO2': ('uuo2', 'u-uo2', 'u-o2', 'u/o2', 'uuo200', 'u(uo2)'), - 'c_Y_in_YH2': ('yyh2', 'y-yh2', 'y(yh2)'), - 'c_Zr_in_ZrH': ('zrzrh', 'zr-zrh', 'zr-h', 'zr/h', 'zr(zrh)'), - 'c_Zr_in_ZrH2': ('zrzrh2', 'zr(zrh2)'), - 'c_Zr_in_ZrHx': ('zrzrhx', 'zr(zrhx)'), + "c_Al27": ("al", "al27", "al-27", "13-al- 27"), + "c_Al_in_Al2O3": ("asap00", "asap", "al(al2o3)"), + "c_Be": ("be", "be-metal", "be-met", "be00", "be-metal", "be metal", "4-be"), + "c_BeO": ("beo",), + "c_Be_in_BeO": ("bebeo", "be-beo", "be-o", "be/o", "bbeo00", "be(beo)"), + "c_Be_in_Be2C": ("bebe2c",), + "c_Be_in_FLiBe": ("beflib", "be(flibe)"), + "c_C6H6": ("benz", "c6h6", "benzine"), + "c_C_in_SiC": ("csic", "c-sic", "c(3c-sic)"), + "c_Ca_in_CaH2": ("cah", "cah00", "cacah2", "ca(cah2)"), + "c_D_in_D2O": ("dd2o", "d-d2o", "hwtr", "hw", "dhw00", "d(d2o)"), + "c_D_in_D2O_ice": ("dice",), + "c_F_in_FLiBe": ("fflibe", "f(flibe)"), + "c_Fe56": ("fe", "fe56", "fe-56", "26-fe- 56"), + "c_Graphite": ("graph", "grph", "gr", "gr00", "graphite"), + "c_Graphite_10p": ("grph10", "10p graphit"), + "c_Graphite_30p": ("grph30", "30p graphit"), + "c_H_in_C5O2H8": ("lucite", "c5o2h8", "h-luci", "h(lucite)"), + "c_H_in_CaH2": ("hcah2", "hca00", "h(cah2)"), + "c_H_in_CH2": ("hch2", "poly", "pol", "h-poly", "pol00", "h(ch2)"), + "c_H_in_CH4_liquid": ("lch4", "lmeth", "l-ch4"), + "c_H_in_CH4_solid": ("sch4", "smeth", "s-ch4"), + "c_H_in_CH4_solid_phase_II": ("sch4p2",), + "c_H_in_H2O": ("hh2o", "h-h2o", "lwtr", "lw", "lw00", "h(h2o)"), + "c_H_in_H2O_solid": ("hice", "h-ice", "ice00", "h(ice-ih)", "h(ice)"), + "c_H_in_HF": ("hhf", "h(hf)"), + "c_H_in_Mesitylene": ("mesi00", "mesi", "mesi-phii"), + "c_H_in_ParaffinicOil": ("hparaf", "h(paraffin"), + "c_H_in_Toluene": ("tol00", "tol", "tolue-phii"), + "c_H_in_UH3": ("huh3", "h(uh3)"), + "c_H_in_YH2": ("hyh2", "h-yh2", "h(yh2)"), + "c_H_in_ZrH": ("hzrh", "h-zrh", "h-zr", "h/zr", "hzr", "hzr00", "h(zrh)"), + "c_H_in_ZrH2": ("hzrh2", "h(zrh2)"), + "c_H_in_ZrHx": ("hzrhx", "h(zrhx)"), + "c_Li_in_FLiBe": ("liflib", "li(flibe)"), + "c_Mg24": ("mg", "mg24", "mg00", "24-mg"), + "c_N_in_UN": ("n-un", "n(un)", "n(un) l"), + "c_O_in_Al2O3": ("osap00", "osap", "o(al2o3)"), + "c_O_in_BeO": ("obeo", "o-beo", "o-be", "o/be", "obeo00", "o(beo)"), + "c_O_in_D2O": ("od2o", "o-d2o", "ohw00", "o(d2o)"), + "c_O_in_H2O_solid": ("oice", "o-ice", "o(ice-ih)"), + "c_O_in_UO2": ("ouo2", "o-uo2", "o2-u", "o2/u", "ouo200", "o(uo2)"), + "c_ortho_D": ("orthod", "orthoD", "dortho", "od200", "ortod", "ortho-d"), + "c_ortho_H": ("orthoh", "orthoH", "hortho", "oh200", "ortoh", "ortho-h"), + "c_para_D": ("parad", "paraD", "dpara", "pd200", "para-d"), + "c_para_H": ("parah", "paraH", "hpara", "ph200", "para-h"), + "c_Si28": ("si00", "sili", "si"), + "c_Si_in_SiC": ("sisic", "si-sic", "si(3c-sic)"), + "c_SiO2_alpha": ("sio2", "sio2a", "sio2alpha"), + "c_SiO2_beta": ("sio2b", "sio2beta"), + "c_U_in_UN": ("u-un", "u(un)", "u(un) l"), + "c_U_in_UO2": ("uuo2", "u-uo2", "u-o2", "u/o2", "uuo200", "u(uo2)"), + "c_Y_in_YH2": ("yyh2", "y-yh2", "y(yh2)"), + "c_Zr_in_ZrH": ("zrzrh", "zr-zrh", "zr-h", "zr/h", "zr(zrh)"), + "c_Zr_in_ZrH2": ("zrzrh2", "zr(zrh2)"), + "c_Zr_in_ZrHx": ("zrzrhx", "zr(zrhx)"), } @@ -120,8 +124,7 @@ def get_thermal_name(name): # First, construct a list of all the values/keys in the names # dictionary - all_names = itertools.chain(_THERMAL_NAMES.keys(), - *_THERMAL_NAMES.values()) + all_names = itertools.chain(_THERMAL_NAMES.keys(), *_THERMAL_NAMES.values()) matches = get_close_matches(name, all_names, cutoff=0.5) if matches: @@ -133,14 +136,18 @@ def get_thermal_name(name): match = key break - warn('Thermal scattering material "{}" is not recognized. ' - 'Assigning a name of {}.'.format(name, match)) + warn( + 'Thermal scattering material "{}" is not recognized. ' + "Assigning a name of {}.".format(name, match) + ) return match else: # OK, we give up. Just use the ACE name. - warn('Thermal scattering material "{0}" is not recognized. ' - 'Assigning a name of c_{0}.'.format(name)) - return 'c_' + name + warn( + 'Thermal scattering material "{0}" is not recognized. ' + "Assigning a name of c_{0}.".format(name) + ) + return "c_" + name class CoherentElastic(Function1D): @@ -195,7 +202,7 @@ def bragg_edges(self): @bragg_edges.setter def bragg_edges(self, bragg_edges): - cv.check_type('Bragg edges', bragg_edges, Iterable, Real) + cv.check_type("Bragg edges", bragg_edges, Iterable, Real) self._bragg_edges = np.asarray(bragg_edges) @property @@ -204,8 +211,7 @@ def factors(self): @factors.setter def factors(self, factors): - cv.check_type('structure factor cumulative sums', factors, - Iterable, Real) + cv.check_type("structure factor cumulative sums", factors, Iterable, Real) self._factors = np.asarray(factors) def to_hdf5(self, group, name): @@ -219,9 +225,10 @@ def to_hdf5(self, group, name): Name of the dataset to create """ - dataset = group.create_dataset(name, data=np.vstack( - [self.bragg_edges, self.factors])) - dataset.attrs['type'] = np.bytes_(type(self).__name__) + dataset = group.create_dataset( + name, data=np.vstack([self.bragg_edges, self.factors]) + ) + dataset.attrs["type"] = np.bytes_(type(self).__name__) @classmethod def from_hdf5(cls, dataset): @@ -273,13 +280,14 @@ class IncoherentElastic(Function1D): Debye-Waller integral in [eV\ :math:`^{-1}`] """ + def __init__(self, bound_xs, debye_waller): self.bound_xs = bound_xs self.debye_waller = debye_waller def __call__(self, E): W = self.debye_waller - return self.bound_xs / 2.0 * (1 - np.exp(-4*E*W)) / (2*E*W) + return self.bound_xs / 2.0 * (1 - np.exp(-4 * E * W)) / (2 * E * W) def to_hdf5(self, group, name): """Write incoherent elastic scattering to an HDF5 group @@ -294,7 +302,7 @@ def to_hdf5(self, group, name): """ data = np.array([self.bound_xs, self.debye_waller]) dataset = group.create_dataset(name, data=data) - dataset.attrs['type'] = np.bytes_(type(self).__name__) + dataset.attrs["type"] = np.bytes_(type(self).__name__) @classmethod def from_hdf5(cls, dataset): @@ -336,6 +344,7 @@ class ThermalScatteringReaction(EqualityMixin): Secondary angle-energy distribution at each temperature """ + def __init__(self, xs, distribution): self.xs = xs self.distribution = distribution @@ -354,8 +363,8 @@ def to_hdf5(self, group, name): for T, xs in self.xs.items(): Tgroup = group.require_group(T) rx_group = Tgroup.create_group(name) - xs.to_hdf5(rx_group, 'xs') - dgroup = rx_group.create_group('distribution') + xs.to_hdf5(rx_group, "xs") + dgroup = rx_group.create_group("distribution") self.distribution[T].to_hdf5(dgroup) @classmethod @@ -381,11 +390,11 @@ def from_hdf5(cls, group, name, temperatures): distribution = {} for T in temperatures: rx_group = group[T][name] - xs[T] = Function1D.from_hdf5(rx_group['xs']) + xs[T] = Function1D.from_hdf5(rx_group["xs"]) if isinstance(xs[T], CoherentElastic): distribution[T] = CoherentElasticAE(xs[T]) else: - distribution[T] = AngleEnergy.from_hdf5(rx_group['distribution']) + distribution[T] = AngleEnergy.from_hdf5(rx_group["distribution"]) return cls(xs, distribution) @@ -438,7 +447,7 @@ def __init__(self, name, atomic_weight_ratio, energy_max, kTs): self.nuclides = [] def __repr__(self): - if hasattr(self, 'name'): + if hasattr(self, "name"): return f"" else: return "" @@ -447,7 +456,7 @@ def __repr__(self): def temperatures(self): return [_temperature_str(kT / K_BOLTZMANN) for kT in self.kTs] - def export_to_hdf5(self, path, mode='a', libver='earliest'): + def export_to_hdf5(self, path, mode="a", libver="earliest"): """Export table to an HDF5 file. Parameters @@ -464,22 +473,22 @@ def export_to_hdf5(self, path, mode='a', libver='earliest'): """ # Open file and write version with h5py.File(str(path), mode, libver=libver) as f: - f.attrs['filetype'] = np.bytes_('data_thermal') - f.attrs['version'] = np.array(HDF5_VERSION) + f.attrs["filetype"] = np.bytes_("data_thermal") + f.attrs["version"] = np.array(HDF5_VERSION) # Write basic data g = f.create_group(self.name) - g.attrs['atomic_weight_ratio'] = self.atomic_weight_ratio - g.attrs['energy_max'] = self.energy_max - g.attrs['nuclides'] = np.array(self.nuclides, dtype='S') - ktg = g.create_group('kTs') + g.attrs["atomic_weight_ratio"] = self.atomic_weight_ratio + g.attrs["energy_max"] = self.energy_max + g.attrs["nuclides"] = np.array(self.nuclides, dtype="S") + ktg = g.create_group("kTs") for i, temperature in enumerate(self.temperatures): ktg.create_dataset(temperature, data=self.kTs[i]) # Write elastic/inelastic reaction data if self.elastic is not None: - self.elastic.to_hdf5(g, 'elastic') - self.inelastic.to_hdf5(g, 'inelastic') + self.elastic.to_hdf5(g, "elastic") + self.inelastic.to_hdf5(g, "inelastic") def add_temperature_from_ace(self, ace_or_filename, name=None): """Add data to the ThermalScattering object from an ACE file at a @@ -506,12 +515,12 @@ def add_temperature_from_ace(self, ace_or_filename, name=None): # Check if temprature already exists strT = data.temperatures[0] if strT in self.temperatures: - warn(f'S(a,b) data at T={strT} already exists.') + warn(f"S(a,b) data at T={strT} already exists.") return # Check that name matches if data.name != self.name: - raise ValueError('Data provided for an incorrect material.') + raise ValueError("Data provided for an incorrect material.") # Add temperature self.kTs += data.kTs @@ -546,42 +555,45 @@ def from_hdf5(cls, group_or_filename): if isinstance(group_or_filename, h5py.Group): group = group_or_filename else: - h5file = h5py.File(str(group_or_filename), 'r') + h5file = h5py.File(str(group_or_filename), "r") # Make sure version matches - if 'version' in h5file.attrs: - major, minor = h5file.attrs['version'] + if "version" in h5file.attrs: + major, minor = h5file.attrs["version"] if major != HDF5_VERSION_MAJOR: raise IOError( - 'HDF5 data format uses version {}.{} whereas your ' - 'installation of the OpenMC Python API expects version ' - '{}.x.'.format(major, minor, HDF5_VERSION_MAJOR)) + "HDF5 data format uses version {}.{} whereas your " + "installation of the OpenMC Python API expects version " + "{}.x.".format(major, minor, HDF5_VERSION_MAJOR) + ) else: raise IOError( - 'HDF5 data does not indicate a version. Your installation of ' - 'the OpenMC Python API expects version {}.x data.' - .format(HDF5_VERSION_MAJOR)) + "HDF5 data does not indicate a version. Your installation of " + "the OpenMC Python API expects version {}.x data.".format( + HDF5_VERSION_MAJOR + ) + ) group = list(h5file.values())[0] name = group.name[1:] - atomic_weight_ratio = group.attrs['atomic_weight_ratio'] - energy_max = group.attrs['energy_max'] - kTg = group['kTs'] + atomic_weight_ratio = group.attrs["atomic_weight_ratio"] + energy_max = group.attrs["energy_max"] + kTg = group["kTs"] kTs = [dataset[()] for dataset in kTg.values()] table = cls(name, atomic_weight_ratio, energy_max, kTs) - table.nuclides = [nuc.decode() for nuc in group.attrs['nuclides']] + table.nuclides = [nuc.decode() for nuc in group.attrs["nuclides"]] # Read thermal elastic scattering - if 'elastic' in group[table.temperatures[0]]: + if "elastic" in group[table.temperatures[0]]: table.elastic = ThermalScatteringReaction.from_hdf5( - group, 'elastic', table.temperatures + group, "elastic", table.temperatures ) # Read thermal inelastic scattering table.inelastic = ThermalScatteringReaction.from_hdf5( - group, 'inelastic', table.temperatures + group, "inelastic", table.temperatures ) return table @@ -612,64 +624,68 @@ def from_ace(cls, ace_or_filename, name=None): ace = get_table(ace_or_filename) # Get new name that is GND-consistent - ace_name, xs = ace.name.split('.') - if not xs.endswith('t'): + ace_name, xs = ace.name.split(".") + if not xs.endswith("t"): raise TypeError(f"{ace} is not a thermal scattering ACE table.") if name is None: name = get_thermal_name(ace_name) # Assign temperature to the running list - kTs = [ace.temperature*EV_PER_MEV] + kTs = [ace.temperature * EV_PER_MEV] # Incoherent inelastic scattering cross section idx = ace.jxs[1] n_energy = int(ace.xss[idx]) - energy = ace.xss[idx+1 : idx+1+n_energy]*EV_PER_MEV - xs = ace.xss[idx+1+n_energy : idx+1+2*n_energy] + energy = ace.xss[idx + 1 : idx + 1 + n_energy] * EV_PER_MEV + xs = ace.xss[idx + 1 + n_energy : idx + 1 + 2 * n_energy] inelastic_xs = Tabulated1D(energy, xs) energy_max = energy[-1] # Incoherent inelastic angle-energy distribution - continuous = (ace.nxs[7] == 2) + continuous = ace.nxs[7] == 2 n_energy_out = ace.nxs[4] if not continuous: n_mu = ace.nxs[3] idx = ace.jxs[3] - energy_out = ace.xss[idx:idx + n_energy * n_energy_out * - (n_mu + 2): n_mu + 2]*EV_PER_MEV + energy_out = ( + ace.xss[idx : idx + n_energy * n_energy_out * (n_mu + 2) : n_mu + 2] + * EV_PER_MEV + ) energy_out.shape = (n_energy, n_energy_out) - mu_out = ace.xss[idx:idx + n_energy * n_energy_out * (n_mu + 2)] - mu_out.shape = (n_energy, n_energy_out, n_mu+2) + mu_out = ace.xss[idx : idx + n_energy * n_energy_out * (n_mu + 2)] + mu_out.shape = (n_energy, n_energy_out, n_mu + 2) mu_out = mu_out[:, :, 1:] - skewed = (ace.nxs[7] == 1) + skewed = ace.nxs[7] == 1 distribution = IncoherentInelasticAEDiscrete(energy_out, mu_out, skewed) else: n_mu = ace.nxs[3] - 1 idx = ace.jxs[3] - locc = ace.xss[idx:idx + n_energy].astype(int) - n_energy_out = \ - ace.xss[idx + n_energy:idx + 2 * n_energy].astype(int) + locc = ace.xss[idx : idx + n_energy].astype(int) + n_energy_out = ace.xss[idx + n_energy : idx + 2 * n_energy].astype(int) energy_out = [] mu_out = [] for i in range(n_energy): idx = locc[i] # Outgoing energy distribution for incoming energy i - e = ace.xss[idx + 1:idx + 1 + n_energy_out[i]*(n_mu + 3): - n_mu + 3]*EV_PER_MEV - p = ace.xss[idx + 2:idx + 2 + n_energy_out[i]*(n_mu + 3): - n_mu + 3]/EV_PER_MEV - c = ace.xss[idx + 3:idx + 3 + n_energy_out[i]*(n_mu + 3): - n_mu + 3] - eout_i = Tabular(e, p, 'linear-linear', ignore_negative=True) + e = ( + ace.xss[idx + 1 : idx + 1 + n_energy_out[i] * (n_mu + 3) : n_mu + 3] + * EV_PER_MEV + ) + p = ( + ace.xss[idx + 2 : idx + 2 + n_energy_out[i] * (n_mu + 3) : n_mu + 3] + / EV_PER_MEV + ) + c = ace.xss[idx + 3 : idx + 3 + n_energy_out[i] * (n_mu + 3) : n_mu + 3] + eout_i = Tabular(e, p, "linear-linear", ignore_negative=True) eout_i.c = c # Outgoing angle distribution for each # (incoming, outgoing) energy pair mu_i = [] for j in range(n_energy_out[i]): - mu = ace.xss[idx + 4:idx + 4 + n_mu] + mu = ace.xss[idx + 4 : idx + 4 + n_mu] # The equiprobable angles produced by NJOY are not always # sorted. This is problematic when the smearing algorithm # is applied when sampling the angles. We sort the angles @@ -680,12 +696,14 @@ def from_ace(cls, ace_or_filename, name=None): # Older versions of NJOY had a bug, and the discrete # scattering angles could sometimes be less than -1 or # greater than 1. We check for this here, and warn users. - if mu[0] < -1. or mu[-1] > 1.: - warn('S(a,b) scattering angle for incident energy index ' - f'{i} and exit energy index {j} outside of the ' - 'interval [-1, 1].') - - p_mu = 1. / n_mu * np.ones(n_mu) + if mu[0] < -1.0 or mu[-1] > 1.0: + warn( + "S(a,b) scattering angle for incident energy index " + f"{i} and exit energy index {j} outside of the " + "interval [-1, 1]." + ) + + p_mu = 1.0 / n_mu * np.ones(n_mu) mu_ij = Discrete(mu, p_mu) mu_ij.c = np.cumsum(p_mu) mu_i.append(mu_ij) @@ -697,16 +715,16 @@ def from_ace(cls, ace_or_filename, name=None): # the outgoing energy. From Eq. 7.6 of the ENDF manual, we can # add an outgoing energy 0 eV that has a PDF of 0 (and of # course, a CDF of 0 as well). - if eout_i.c[0] > 0.: - eout_i._x = np.insert(eout_i.x, 0, 0.) - eout_i._p = np.insert(eout_i.p, 0, 0.) - eout_i.c = np.insert(eout_i.c, 0, 0.) + if eout_i.c[0] > 0.0: + eout_i._x = np.insert(eout_i.x, 0, 0.0) + eout_i._p = np.insert(eout_i.p, 0, 0.0) + eout_i.c = np.insert(eout_i.c, 0, 0.0) # For this added outgoing energy (of 0 eV) we add a set of # isotropic discrete angles. - dmu = 2. / n_mu - mu = np.linspace(-1. + 0.5*dmu, 1. - 0.5*dmu, n_mu) - p_mu = 1. / n_mu * np.ones(n_mu) + dmu = 2.0 / n_mu + mu = np.linspace(-1.0 + 0.5 * dmu, 1.0 - 0.5 * dmu, n_mu) + p_mu = 1.0 / n_mu * np.ones(n_mu) mu_0 = Discrete(mu, p_mu) mu_0.c = np.cumsum(p_mu) mu_i.insert(0, mu_0) @@ -722,7 +740,8 @@ def from_ace(cls, ace_or_filename, name=None): interpolation = [2] energy = inelastic_xs.x distribution = IncoherentInelasticAE( - breakpoints, interpolation, energy, energy_out, mu_out) + breakpoints, interpolation, energy, energy_out, mu_out + ) table = cls(name, ace.atomic_weight_ratio, energy_max, kTs) T = table.temperatures[0] @@ -736,9 +755,9 @@ def from_ace(cls, ace_or_filename, name=None): if ace.nxs[5] in (4, 5): # Coherent elastic n_energy = int(ace.xss[idx]) - energy = ace.xss[idx + 1: idx + 1 + n_energy]*EV_PER_MEV - P = ace.xss[idx + 1 + n_energy: idx + 1 + 2 * n_energy] - coherent_xs = CoherentElastic(energy, P*EV_PER_MEV) + energy = ace.xss[idx + 1 : idx + 1 + n_energy] * EV_PER_MEV + P = ace.xss[idx + 1 + n_energy : idx + 1 + 2 * n_energy] + coherent_xs = CoherentElastic(energy, P * EV_PER_MEV) coherent_dist = CoherentElasticAE(coherent_xs) # Coherent elastic shouldn't have angular distributions listed @@ -748,13 +767,13 @@ def from_ace(cls, ace_or_filename, name=None): if ace.nxs[5] in (3, 5): # Incoherent elastic scattering -- first determine if both # incoherent and coherent are present (mixed) - mixed = (ace.nxs[5] == 5) + mixed = ace.nxs[5] == 5 # Get cross section values idx = ace.jxs[7] if mixed else ace.jxs[4] n_energy = int(ace.xss[idx]) - energy = ace.xss[idx + 1: idx + 1 + n_energy]*EV_PER_MEV - values = ace.xss[idx + 1 + n_energy: idx + 1 + 2 * n_energy] + energy = ace.xss[idx + 1 : idx + 1 + n_energy] * EV_PER_MEV + values = ace.xss[idx + 1 + n_energy : idx + 1 + 2 * n_energy] incoherent_xs = Tabulated1D(energy, values) @@ -762,7 +781,7 @@ def from_ace(cls, ace_or_filename, name=None): n_mu = (ace.nxs[8] if mixed else ace.nxs[6]) + 1 assert n_mu > 0 idx = ace.jxs[9] if mixed else ace.jxs[6] - mu_out = ace.xss[idx:idx + n_energy * n_mu] + mu_out = ace.xss[idx : idx + n_energy * n_mu] mu_out.shape = (n_energy, n_mu) incoherent_dist = IncoherentElasticAEDiscrete(mu_out) @@ -790,11 +809,11 @@ def from_ace(cls, ace_or_filename, name=None): if zaid > 0: Z, A = divmod(zaid, 1000) element = ATOMIC_SYMBOL[Z] - if element in ['H', 'Fe']: + if element in ["H", "Fe"]: table.nuclides.append(element + str(A)) else: - if element + '0' not in table.nuclides: - table.nuclides.append(element + '0') + if element + "0" not in table.nuclides: + table.nuclides.append(element + "0") for isotope, _ in isotopes(element): if isotope not in table.nuclides: table.nuclides.append(isotope) @@ -802,9 +821,16 @@ def from_ace(cls, ace_or_filename, name=None): return table @classmethod - def from_njoy(cls, filename, filename_thermal, temperatures=None, - evaluation=None, evaluation_thermal=None, - use_endf_data=True, **kwargs): + def from_njoy( + cls, + filename, + filename_thermal, + temperatures=None, + evaluation=None, + evaluation_thermal=None, + use_endf_data=True, + **kwargs, + ): """Generate thermal scattering data by running NJOY. Parameters @@ -838,15 +864,15 @@ def from_njoy(cls, filename, filename_thermal, temperatures=None, """ with tempfile.TemporaryDirectory() as tmpdir: # Run NJOY to create an ACE library - kwargs.setdefault('output_dir', tmpdir) - kwargs.setdefault('ace', os.path.join(kwargs['output_dir'], 'ace')) - kwargs['evaluation'] = evaluation - kwargs['evaluation_thermal'] = evaluation_thermal + kwargs.setdefault("output_dir", tmpdir) + kwargs.setdefault("ace", os.path.join(kwargs["output_dir"], "ace")) + kwargs["evaluation"] = evaluation + kwargs["evaluation_thermal"] = evaluation_thermal make_ace_thermal(filename, filename_thermal, temperatures, **kwargs) # Create instance from ACE tables within library - lib = Library(kwargs['ace']) - name = kwargs.get('table_name') + lib = Library(kwargs["ace"]) + name = kwargs.get("table_name") data = cls.from_ace(lib.tables[0], name=name) for table in lib.tables[1:]: data.add_temperature_from_ace(table, name=name) @@ -942,45 +968,45 @@ def get_incoherent_elastic(file_obj): elastic = ThermalScatteringReaction(xs, distribution) # Read incoherent inelastic data - assert (7, 4) in ev.section, 'No MF=7, MT=4 found in thermal scattering' + assert (7, 4) in ev.section, "No MF=7, MT=4 found in thermal scattering" file_obj = StringIO(ev.section[7, 4]) params = endf.get_head_record(file_obj) - data = {'symmetric': params[4] == 0} + data = {"symmetric": params[4] == 0} # Get information about principal atom params, B = endf.get_list_record(file_obj) - data['log'] = bool(params[2]) - data['free_atom_xs'] = B[0] - data['epsilon'] = B[1] - data['A0'] = awr = B[2] - data['e_max'] = energy_max = B[3] - data['M0'] = B[5] + data["log"] = bool(params[2]) + data["free_atom_xs"] = B[0] + data["epsilon"] = B[1] + data["A0"] = awr = B[2] + data["e_max"] = energy_max = B[3] + data["M0"] = B[5] # Get information about non-principal atoms n_non_principal = params[5] - data['non_principal'] = [] - NonPrincipal = namedtuple('NonPrincipal', ['func', 'xs', 'A', 'M']) + data["non_principal"] = [] + NonPrincipal = namedtuple("NonPrincipal", ["func", "xs", "A", "M"]) for i in range(1, n_non_principal + 1): - func = {0.0: 'SCT', 1.0: 'free gas', 2.0: 'diffusive'}[B[6*i]] - xs = B[6*i + 1] - A = B[6*i + 2] - M = B[6*i + 5] - data['non_principal'].append(NonPrincipal(func, xs, A, M)) + func = {0.0: "SCT", 1.0: "free gas", 2.0: "diffusive"}[B[6 * i]] + xs = B[6 * i + 1] + A = B[6 * i + 2] + M = B[6 * i + 5] + data["non_principal"].append(NonPrincipal(func, xs, A, M)) # Get S(alpha,beta,T) kTs = [] - if data['free_atom_xs'] > 0.0: + if data["free_atom_xs"] > 0.0: params, _ = endf.get_tab2_record(file_obj) n_beta = params[5] - sab = {'beta': np.empty(n_beta)} + sab = {"beta": np.empty(n_beta)} for i in range(n_beta): params, S = endf.get_tab1_record(file_obj) T0, beta, lt = params[:3] if i == 0: - sab['alpha'] = alpha = S.x + sab["alpha"] = alpha = S.x sab[T0] = np.empty((alpha.size, n_beta)) kTs.append(K_BOLTZMANN * T0) - sab['beta'][i] = beta + sab["beta"][i] = beta sab[T0][:, i] = S.y for _ in range(lt): params, S = endf.get_list_record(file_obj) @@ -989,17 +1015,17 @@ def get_incoherent_elastic(file_obj): sab[T] = np.empty((alpha.size, n_beta)) kTs.append(K_BOLTZMANN * T) sab[T][:, i] = S - data['sab'] = sab + data["sab"] = sab # Get effective temperature for each atom _, Teff = endf.get_tab1_record(file_obj) - data['effective_temperature'] = [Teff] - for atom in data['non_principal']: - if atom.func == 'SCT': + data["effective_temperature"] = [Teff] + for atom in data["non_principal"]: + if atom.func == "SCT": _, Teff = endf.get_tab1_record(file_obj) - data['effective_temperature'].append(Teff) + data["effective_temperature"].append(Teff) - name = ev.target['zsymam'].strip() + name = ev.target["zsymam"].strip() instance = cls(name, awr, energy_max, kTs) if elastic is not None: instance.elastic = elastic diff --git a/openmc/data/thermal_angle_energy.py b/openmc/data/thermal_angle_energy.py index 4789ebcc6f2..e97a428ec28 100644 --- a/openmc/data/thermal_angle_energy.py +++ b/openmc/data/thermal_angle_energy.py @@ -31,6 +31,7 @@ class CoherentElasticAE(AngleEnergy): Coherent elastic scattering cross section """ + def __init__(self, coherent_xs): self.coherent_xs = coherent_xs @@ -43,8 +44,8 @@ def to_hdf5(self, group): HDF5 group to write to """ - group.attrs['type'] = np.bytes_('coherent_elastic') - self.coherent_xs.to_hdf5(group, 'coherent_xs') + group.attrs["type"] = np.bytes_("coherent_elastic") + self.coherent_xs.to_hdf5(group, "coherent_xs") @classmethod def from_hdf5(cls, group): @@ -63,7 +64,7 @@ def from_hdf5(cls, group): Coherent elastic distribution """ - coherent_xs = openmc.data.CoherentElastic.from_hdf5(group['coherent_xs']) + coherent_xs = openmc.data.CoherentElastic.from_hdf5(group["coherent_xs"]) return cls(coherent_xs) @@ -92,6 +93,7 @@ class IncoherentElasticAE(AngleEnergy): Debye-Waller integral in [eV\ :math:`^{-1}`] """ + def __init__(self, debye_waller): self.debye_waller = debye_waller @@ -104,8 +106,8 @@ def to_hdf5(self, group): HDF5 group to write to """ - group.attrs['type'] = np.bytes_('incoherent_elastic') - group.create_dataset('debye_waller', data=self.debye_waller) + group.attrs["type"] = np.bytes_("incoherent_elastic") + group.create_dataset("debye_waller", data=self.debye_waller) @classmethod def from_hdf5(cls, group): @@ -122,7 +124,7 @@ def from_hdf5(cls, group): Incoherent elastic distribution """ - return cls(group['debye_waller'][()]) + return cls(group["debye_waller"][()]) class IncoherentElasticAEDiscrete(AngleEnergy): @@ -134,6 +136,7 @@ class IncoherentElasticAEDiscrete(AngleEnergy): Equi-probable discrete angles at each incoming energy """ + def __init__(self, mu_out): self.mu_out = mu_out @@ -146,8 +149,8 @@ def to_hdf5(self, group): HDF5 group to write to """ - group.attrs['type'] = np.bytes_('incoherent_elastic_discrete') - group.create_dataset('mu_out', data=self.mu_out) + group.attrs["type"] = np.bytes_("incoherent_elastic_discrete") + group.create_dataset("mu_out", data=self.mu_out) @classmethod def from_hdf5(cls, group): @@ -164,7 +167,7 @@ def from_hdf5(cls, group): Discrete incoherent elastic distribution """ - return cls(group['mu_out'][()]) + return cls(group["mu_out"][()]) class IncoherentInelasticAEDiscrete(AngleEnergy): @@ -189,6 +192,7 @@ class IncoherentInelasticAEDiscrete(AngleEnergy): Whether discrete angles are equi-probable or have a skewed distribution """ + def __init__(self, energy_out, mu_out, skewed=False): self.energy_out = energy_out self.mu_out = mu_out @@ -203,10 +207,10 @@ def to_hdf5(self, group): HDF5 group to write to """ - group.attrs['type'] = np.bytes_('incoherent_inelastic_discrete') - group.create_dataset('energy_out', data=self.energy_out) - group.create_dataset('mu_out', data=self.mu_out) - group.create_dataset('skewed', data=self.skewed) + group.attrs["type"] = np.bytes_("incoherent_inelastic_discrete") + group.create_dataset("energy_out", data=self.energy_out) + group.create_dataset("mu_out", data=self.mu_out) + group.create_dataset("skewed", data=self.skewed) @classmethod def from_hdf5(cls, group): @@ -223,14 +227,14 @@ def from_hdf5(cls, group): Discrete incoherent inelastic distribution """ - energy_out = group['energy_out'][()] - mu_out = group['mu_out'][()] - skewed = bool(group['skewed'][()]) + energy_out = group["energy_out"][()] + mu_out = group["mu_out"][()] + skewed = bool(group["skewed"][()]) return cls(energy_out, mu_out, skewed) class IncoherentInelasticAE(CorrelatedAngleEnergy): - _name = 'incoherent_inelastic' + _name = "incoherent_inelastic" class MixedElasticAE(AngleEnergy): @@ -253,6 +257,7 @@ class MixedElasticAE(AngleEnergy): Secondary distribution for incoherent elastic scattering """ + def __init__(self, coherent, incoherent): self.coherent = coherent self.incoherent = incoherent @@ -266,10 +271,10 @@ def to_hdf5(self, group): HDF5 group to write to """ - group.attrs['type'] = np.bytes_('mixed_elastic') - coherent_group = group.create_group('coherent') + group.attrs["type"] = np.bytes_("mixed_elastic") + coherent_group = group.create_group("coherent") self.coherent.to_hdf5(coherent_group) - incoherent_group = group.create_group('incoherent') + incoherent_group = group.create_group("incoherent") self.incoherent.to_hdf5(incoherent_group) @classmethod @@ -287,6 +292,6 @@ def from_hdf5(cls, group): Mixed thermal elastic distribution """ - coherent = AngleEnergy.from_hdf5(group['coherent']) - incoherent = AngleEnergy.from_hdf5(group['incoherent']) + coherent = AngleEnergy.from_hdf5(group["coherent"]) + incoherent = AngleEnergy.from_hdf5(group["incoherent"]) return cls(coherent, incoherent) diff --git a/openmc/data/uncorrelated.py b/openmc/data/uncorrelated.py index 141007b70a9..a0d79951f1c 100644 --- a/openmc/data/uncorrelated.py +++ b/openmc/data/uncorrelated.py @@ -40,8 +40,7 @@ def angle(self): @angle.setter def angle(self, angle): - cv.check_type('uncorrelated angle distribution', angle, - AngleDistribution) + cv.check_type("uncorrelated angle distribution", angle, AngleDistribution) self._angle = angle @property @@ -50,8 +49,7 @@ def energy(self): @energy.setter def energy(self, energy): - cv.check_type('uncorrelated energy distribution', energy, - EnergyDistribution) + cv.check_type("uncorrelated energy distribution", energy, EnergyDistribution) self._energy = energy def to_hdf5(self, group): @@ -63,13 +61,13 @@ def to_hdf5(self, group): HDF5 group to write to """ - group.attrs['type'] = np.bytes_('uncorrelated') + group.attrs["type"] = np.bytes_("uncorrelated") if self.angle is not None: - angle_group = group.create_group('angle') + angle_group = group.create_group("angle") self.angle.to_hdf5(angle_group) if self.energy is not None: - energy_group = group.create_group('energy') + energy_group = group.create_group("energy") self.energy.to_hdf5(energy_group) @classmethod @@ -88,8 +86,8 @@ def from_hdf5(cls, group): """ dist = cls() - if 'angle' in group: - dist.angle = AngleDistribution.from_hdf5(group['angle']) - if 'energy' in group: - dist.energy = EnergyDistribution.from_hdf5(group['energy']) + if "angle" in group: + dist.angle = AngleDistribution.from_hdf5(group["angle"]) + if "energy" in group: + dist.energy = EnergyDistribution.from_hdf5(group["energy"]) return dist diff --git a/openmc/data/urr.py b/openmc/data/urr.py index f129c98f811..d9587fb8c79 100644 --- a/openmc/data/urr.py +++ b/openmc/data/urr.py @@ -66,8 +66,15 @@ class ProbabilityTables(EqualityMixin): sections (True). """ - def __init__(self, energy, table, interpolation, inelastic_flag=-1, - absorption_flag=-1, multiply_smooth=False): + def __init__( + self, + energy, + table, + interpolation, + inelastic_flag=-1, + absorption_flag=-1, + multiply_smooth=False, + ): self.energy = energy self.table = table self.interpolation = interpolation @@ -81,7 +88,7 @@ def absorption_flag(self): @absorption_flag.setter def absorption_flag(self, absorption_flag): - cv.check_type('absorption flag', absorption_flag, Integral) + cv.check_type("absorption flag", absorption_flag, Integral) self._absorption_flag = absorption_flag @property @@ -90,7 +97,7 @@ def energy(self): @energy.setter def energy(self, energy): - cv.check_type('probability table energies', energy, Iterable, Real) + cv.check_type("probability table energies", energy, Iterable, Real) self._energy = energy @property @@ -99,7 +106,7 @@ def inelastic_flag(self): @inelastic_flag.setter def inelastic_flag(self, inelastic_flag): - cv.check_type('inelastic flag', inelastic_flag, Integral) + cv.check_type("inelastic flag", inelastic_flag, Integral) self._inelastic_flag = inelastic_flag @property @@ -108,7 +115,7 @@ def interpolation(self): @interpolation.setter def interpolation(self, interpolation): - cv.check_value('interpolation', interpolation, [2, 5]) + cv.check_value("interpolation", interpolation, [2, 5]) self._interpolation = interpolation @property @@ -117,7 +124,7 @@ def multiply_smooth(self): @multiply_smooth.setter def multiply_smooth(self, multiply_smooth): - cv.check_type('multiply by smooth', multiply_smooth, bool) + cv.check_type("multiply by smooth", multiply_smooth, bool) self._multiply_smooth = multiply_smooth @property @@ -126,7 +133,7 @@ def table(self): @table.setter def table(self, table): - cv.check_type('probability tables', table, np.ndarray) + cv.check_type("probability tables", table, np.ndarray) self._table = table def to_hdf5(self, group): @@ -138,13 +145,13 @@ def to_hdf5(self, group): HDF5 group to write to """ - group.attrs['interpolation'] = self.interpolation - group.attrs['inelastic'] = self.inelastic_flag - group.attrs['absorption'] = self.absorption_flag - group.attrs['multiply_smooth'] = int(self.multiply_smooth) + group.attrs["interpolation"] = self.interpolation + group.attrs["inelastic"] = self.inelastic_flag + group.attrs["absorption"] = self.absorption_flag + group.attrs["multiply_smooth"] = int(self.multiply_smooth) - group.create_dataset('energy', data=self.energy) - group.create_dataset('table', data=self.table) + group.create_dataset("energy", data=self.energy) + group.create_dataset("table", data=self.table) @classmethod def from_hdf5(cls, group): @@ -161,16 +168,22 @@ def from_hdf5(cls, group): Probability tables """ - interpolation = group.attrs['interpolation'] - inelastic_flag = group.attrs['inelastic'] - absorption_flag = group.attrs['absorption'] - multiply_smooth = bool(group.attrs['multiply_smooth']) - - energy = group['energy'][()] - table = group['table'][()] - - return cls(energy, table, interpolation, inelastic_flag, - absorption_flag, multiply_smooth) + interpolation = group.attrs["interpolation"] + inelastic_flag = group.attrs["inelastic"] + absorption_flag = group.attrs["absorption"] + multiply_smooth = bool(group.attrs["multiply_smooth"]) + + energy = group["energy"][()] + table = group["table"][()] + + return cls( + energy, + table, + interpolation, + inelastic_flag, + absorption_flag, + multiply_smooth, + ) @classmethod def from_ace(cls, ace): @@ -192,24 +205,30 @@ def from_ace(cls, ace): if idx == 0: return None - N = int(ace.xss[idx]) # Number of incident energies - M = int(ace.xss[idx+1]) # Length of probability table - interpolation = int(ace.xss[idx+2]) - inelastic_flag = int(ace.xss[idx+3]) - absorption_flag = int(ace.xss[idx+4]) - multiply_smooth = (int(ace.xss[idx+5]) == 1) + N = int(ace.xss[idx]) # Number of incident energies + M = int(ace.xss[idx + 1]) # Length of probability table + interpolation = int(ace.xss[idx + 2]) + inelastic_flag = int(ace.xss[idx + 3]) + absorption_flag = int(ace.xss[idx + 4]) + multiply_smooth = int(ace.xss[idx + 5]) == 1 idx += 6 # Get energies at which tables exist - energy = ace.xss[idx : idx+N]*EV_PER_MEV + energy = ace.xss[idx : idx + N] * EV_PER_MEV idx += N # Get probability tables - table = ace.xss[idx : idx+N*6*M].copy() + table = ace.xss[idx : idx + N * 6 * M].copy() table.shape = (N, 6, M) # Convert units on heating numbers - table[:,5,:] *= EV_PER_MEV - - return cls(energy, table, interpolation, inelastic_flag, - absorption_flag, multiply_smooth) + table[:, 5, :] *= EV_PER_MEV + + return cls( + energy, + table, + interpolation, + inelastic_flag, + absorption_flag, + multiply_smooth, + ) diff --git a/openmc/deplete/_matrix_funcs.py b/openmc/deplete/_matrix_funcs.py index c7f7df76fbd..911c0875fec 100644 --- a/openmc/deplete/_matrix_funcs.py +++ b/openmc/deplete/_matrix_funcs.py @@ -2,13 +2,15 @@ def celi_f1(chain, rates, fission_yields=None): - return (5 / 12 * chain.form_matrix(rates[0], fission_yields) - + 1 / 12 * chain.form_matrix(rates[1], fission_yields)) + return 5 / 12 * chain.form_matrix( + rates[0], fission_yields + ) + 1 / 12 * chain.form_matrix(rates[1], fission_yields) def celi_f2(chain, rates, fission_yields=None): - return (1 / 12 * chain.form_matrix(rates[0], fission_yields) - + 5 / 12 * chain.form_matrix(rates[1], fission_yields)) + return 1 / 12 * chain.form_matrix( + rates[0], fission_yields + ) + 5 / 12 * chain.form_matrix(rates[1], fission_yields) def cf4_f1(chain, rates, fission_yields=None): @@ -16,22 +18,27 @@ def cf4_f1(chain, rates, fission_yields=None): def cf4_f2(chain, rates, fission_yields=None): - return (-1 / 2 * chain.form_matrix(rates[0], fission_yields) - + chain.form_matrix(rates[1], fission_yields)) + return -1 / 2 * chain.form_matrix(rates[0], fission_yields) + chain.form_matrix( + rates[1], fission_yields + ) def cf4_f3(chain, rates, fission_yields=None): - return (1 / 4 * chain.form_matrix(rates[0], fission_yields) - + 1 / 6 * chain.form_matrix(rates[1], fission_yields) - + 1 / 6 * chain.form_matrix(rates[2], fission_yields) - - 1 / 12 * chain.form_matrix(rates[3], fission_yields)) + return ( + 1 / 4 * chain.form_matrix(rates[0], fission_yields) + + 1 / 6 * chain.form_matrix(rates[1], fission_yields) + + 1 / 6 * chain.form_matrix(rates[2], fission_yields) + - 1 / 12 * chain.form_matrix(rates[3], fission_yields) + ) def cf4_f4(chain, rates, fission_yields=None): - return (-1 / 12 * chain.form_matrix(rates[0], fission_yields) - + 1 / 6 * chain.form_matrix(rates[1], fission_yields) - + 1 / 6 * chain.form_matrix(rates[2], fission_yields) - + 1 / 4 * chain.form_matrix(rates[3], fission_yields)) + return ( + -1 / 12 * chain.form_matrix(rates[0], fission_yields) + + 1 / 6 * chain.form_matrix(rates[1], fission_yields) + + 1 / 6 * chain.form_matrix(rates[2], fission_yields) + + 1 / 4 * chain.form_matrix(rates[3], fission_yields) + ) def rk4_f1(chain, rates, fission_yields=None): @@ -39,10 +46,12 @@ def rk4_f1(chain, rates, fission_yields=None): def rk4_f4(chain, rates, fission_yields=None): - return (1 / 6 * chain.form_matrix(rates[0], fission_yields) - + 1 / 3 * chain.form_matrix(rates[1], fission_yields) - + 1 / 3 * chain.form_matrix(rates[2], fission_yields) - + 1 / 6 * chain.form_matrix(rates[3], fission_yields)) + return ( + 1 / 6 * chain.form_matrix(rates[0], fission_yields) + + 1 / 3 * chain.form_matrix(rates[1], fission_yields) + + 1 / 3 * chain.form_matrix(rates[2], fission_yields) + + 1 / 6 * chain.form_matrix(rates[3], fission_yields) + ) def leqi_f1(chain, inputs, fission_yields): @@ -64,9 +73,11 @@ def leqi_f3(chain, inputs, fission_yields=None): f2 = chain.form_matrix(inputs[1], fission_yields) f3 = chain.form_matrix(inputs[2], fission_yields) dt_l, dt = inputs[3], inputs[4] - return (-dt ** 2 / (12 * dt_l * (dt + dt_l)) * f1 - + (dt ** 2 + 6 * dt * dt_l + 5 * dt_l ** 2) - / (12 * dt_l * (dt + dt_l)) * f2 + dt_l / (12 * (dt + dt_l)) * f3) + return ( + -(dt**2) / (12 * dt_l * (dt + dt_l)) * f1 + + (dt**2 + 6 * dt * dt_l + 5 * dt_l**2) / (12 * dt_l * (dt + dt_l)) * f2 + + dt_l / (12 * (dt + dt_l)) * f3 + ) def leqi_f4(chain, inputs, fission_yields=None): @@ -74,7 +85,8 @@ def leqi_f4(chain, inputs, fission_yields=None): f2 = chain.form_matrix(inputs[1], fission_yields) f3 = chain.form_matrix(inputs[2], fission_yields) dt_l, dt = inputs[3], inputs[4] - return (-dt ** 2 / (12 * dt_l * (dt + dt_l)) * f1 - + (dt ** 2 + 2 * dt * dt_l + dt_l ** 2) - / (12 * dt_l * (dt + dt_l)) * f2 - + (4 * dt + 5 * dt_l) / (12 * (dt + dt_l)) * f3) + return ( + -(dt**2) / (12 * dt_l * (dt + dt_l)) * f1 + + (dt**2 + 2 * dt * dt_l + dt_l**2) / (12 * dt_l * (dt + dt_l)) * f2 + + (4 * dt + 5 * dt_l) / (12 * (dt + dt_l)) * f3 + ) diff --git a/openmc/deplete/abc.py b/openmc/deplete/abc.py index 784023f26ff..7e1401d0f10 100644 --- a/openmc/deplete/abc.py +++ b/openmc/deplete/abc.py @@ -31,17 +31,24 @@ __all__ = [ - "OperatorResult", "TransportOperator", - "ReactionRateHelper", "NormalizationHelper", "FissionYieldHelper", - "Integrator", "SIIntegrator", "DepSystemSolver", "add_params"] + "OperatorResult", + "TransportOperator", + "ReactionRateHelper", + "NormalizationHelper", + "FissionYieldHelper", + "Integrator", + "SIIntegrator", + "DepSystemSolver", + "add_params", +] _SECONDS_PER_MINUTE = 60 -_SECONDS_PER_HOUR = 60*60 -_SECONDS_PER_DAY = 24*60*60 -_SECONDS_PER_JULIAN_YEAR = 365.25*24*60*60 +_SECONDS_PER_HOUR = 60 * 60 +_SECONDS_PER_DAY = 24 * 60 * 60 +_SECONDS_PER_JULIAN_YEAR = 365.25 * 24 * 60 * 60 -OperatorResult = namedtuple('OperatorResult', ['k', 'rates']) +OperatorResult = namedtuple("OperatorResult", ["k", "rates"]) OperatorResult.__doc__ = """\ Result of applying transport operator @@ -92,8 +99,9 @@ class TransportOperator(ABC): The depletion chain information necessary to form matrices and tallies. """ + def __init__(self, chain_file, fission_q=None, prev_results=None): - self.output_dir = '.' + self.output_dir = "." # Read depletion chain self.chain = Chain.from_xml(chain_file, fission_q) @@ -217,10 +225,7 @@ def nuclides(self, nuclides): @abstractmethod def get_material_rates( - self, - mat_id: int, - nuc_index: Sequence[str], - react_index: Sequence[str] + self, mat_id: int, nuc_index: Sequence[str], react_index: Sequence[str] ): """Return 2D array of [nuclide, reaction] reaction rates @@ -366,8 +371,7 @@ def __init__(self, chain_nuclides): if nuc.yield_data is None: continue if len(nuc.yield_data) == 1: - self._constant_yields[nuc.name] = ( - nuc.yield_data[nuc.yield_energies[0]]) + self._constant_yields[nuc.name] = nuc.yield_data[nuc.yield_energies[0]] elif len(nuc.yield_data) > 1: self._chain_nuclides[nuc.name] = nuc self._chain_set = set(self._chain_nuclides) | set(self._constant_yields) @@ -469,8 +473,7 @@ def add_params(cls): @add_params class Integrator(ABC): - r"""Abstract class for solving the time-integration for depletion - """ + r"""Abstract class for solving the time-integration for depletion""" _params = r""" Parameters @@ -549,15 +552,15 @@ class Integrator(ABC): """ def __init__( - self, - operator: TransportOperator, - timesteps: Sequence[float], - power: Optional[Union[float, Sequence[float]]] = None, - power_density: Optional[Union[float, Sequence[float]]] = None, - source_rates: Optional[Sequence[float]] = None, - timestep_units: str = 's', - solver: str = "cram48" - ): + self, + operator: TransportOperator, + timesteps: Sequence[float], + power: Optional[Union[float, Sequence[float]]] = None, + power_density: Optional[Union[float, Sequence[float]]] = None, + source_rates: Optional[Sequence[float]] = None, + timestep_units: str = "s", + solver: str = "cram48", + ): # Check number of stages previously used if operator.prev_res is not None: res = operator.prev_res[-1] @@ -566,8 +569,9 @@ def __init__( "{} incompatible with previous restart calculation. " "Previous scheme used {} intermediate solutions, while " "this uses {}".format( - self.__class__.__name__, res.data.shape[0], - self._num_stages)) + self.__class__.__name__, res.data.shape[0], self._num_stages + ) + ) self.operator = operator self.chain = operator.chain @@ -578,7 +582,7 @@ def __init__( if not isinstance(power_density, Iterable): source_rates = power_density * operator.heavy_metal else: - source_rates = [p*operator.heavy_metal for p in power_density] + source_rates = [p * operator.heavy_metal for p in power_density] elif source_rates is None: raise ValueError("Either power, power_density, or source_rates must be set") @@ -589,7 +593,9 @@ def __init__( if len(source_rates) != len(timesteps): raise ValueError( "Number of time steps ({}) != number of powers ({})".format( - len(timesteps), len(source_rates))) + len(timesteps), len(source_rates) + ) + ) # Get list of times / units if isinstance(timesteps[0], Iterable): @@ -602,30 +608,32 @@ def __init__( seconds = [] for timestep, unit, rate in zip(times, units, source_rates): # Make sure values passed make sense - check_type('timestep', timestep, Real) - check_greater_than('timestep', timestep, 0.0, False) - check_type('timestep units', unit, str) - check_type('source rate', rate, Real) - check_greater_than('source rate', rate, 0.0, True) + check_type("timestep", timestep, Real) + check_greater_than("timestep", timestep, 0.0, False) + check_type("timestep units", unit, str) + check_type("source rate", rate, Real) + check_greater_than("source rate", rate, 0.0, True) - if unit in ('s', 'sec'): + if unit in ("s", "sec"): seconds.append(timestep) - elif unit in ('min', 'minute'): - seconds.append(timestep*_SECONDS_PER_MINUTE) - elif unit in ('h', 'hr', 'hour'): - seconds.append(timestep*_SECONDS_PER_HOUR) - elif unit in ('d', 'day'): - seconds.append(timestep*_SECONDS_PER_DAY) - elif unit in ('a', 'year'): - seconds.append(timestep*_SECONDS_PER_JULIAN_YEAR) - elif unit.lower() == 'mwd/kg': - watt_days_per_kg = 1e6*timestep - kilograms = 1e-3*operator.heavy_metal + elif unit in ("min", "minute"): + seconds.append(timestep * _SECONDS_PER_MINUTE) + elif unit in ("h", "hr", "hour"): + seconds.append(timestep * _SECONDS_PER_HOUR) + elif unit in ("d", "day"): + seconds.append(timestep * _SECONDS_PER_DAY) + elif unit in ("a", "year"): + seconds.append(timestep * _SECONDS_PER_JULIAN_YEAR) + elif unit.lower() == "mwd/kg": + watt_days_per_kg = 1e6 * timestep + kilograms = 1e-3 * operator.heavy_metal if rate == 0.0: - raise ValueError("Cannot specify a timestep in [MWd/kg] when" - " the power is zero.") + raise ValueError( + "Cannot specify a timestep in [MWd/kg] when" + " the power is zero." + ) days = watt_days_per_kg * kilograms / rate - seconds.append(days*_SECONDS_PER_DAY) + seconds.append(days * _SECONDS_PER_DAY) else: raise ValueError(f"Invalid timestep unit '{unit}'") @@ -638,13 +646,16 @@ def __init__( # Delay importing of cram module, which requires this file if solver == "cram48": from .cram import CRAM48 + self._solver = CRAM48 elif solver == "cram16": from .cram import CRAM16 + self._solver = CRAM16 else: raise ValueError( - f"Solver {solver} not understood. Expected 'cram48' or 'cram16'") + f"Solver {solver} not understood. Expected 'cram48' or 'cram16'" + ) else: self.solver = solver @@ -655,8 +666,7 @@ def solver(self): @solver.setter def solver(self, func): if not isinstance(func, Callable): - raise TypeError( - f"Solver must be callable, not {type(func)}") + raise TypeError(f"Solver must be callable, not {type(func)}") try: sig = signature(func) except ValueError: @@ -668,21 +678,24 @@ def solver(self, func): # Inspect arguments if len(sig.parameters) != 3: - raise ValueError("Function {} does not support three arguments: " - "{!s}".format(func, sig)) + raise ValueError( + "Function {} does not support three arguments: " + "{!s}".format(func, sig) + ) for ix, param in enumerate(sig.parameters.values()): if param.kind in {param.KEYWORD_ONLY, param.VAR_KEYWORD}: raise ValueError( - f"Keyword arguments like {ix} at position {param} are not allowed") + f"Keyword arguments like {ix} at position {param} are not allowed" + ) self._solver = func def _timed_deplete(self, n, rates, dt, matrix_func=None): start = time.time() results = deplete( - self._solver, self.chain, n, rates, dt, matrix_func, - self.transfer_rates) + self._solver, self.chain, n, rates, dt, matrix_func, self.transfer_rates + ) return time.time() - start, results @abstractmethod @@ -692,7 +705,7 @@ def __call__( rates: ReactionRates, dt: float, source_rate: float, - i: int + i: int, ): """Perform the integration across one time step @@ -739,8 +752,7 @@ def __len__(self): return len(self.timesteps) def _get_bos_data_from_operator(self, step_index, source_rate, bos_conc): - """Get beginning of step concentrations, reaction rates from Operator - """ + """Get beginning of step concentrations, reaction rates from Operator""" x = deepcopy(bos_conc) res = self.operator(x, source_rate) self.operator.write_bos_data(step_index + self._i_res) @@ -762,15 +774,14 @@ def _get_bos_data_from_restart(self, source_rate, bos_conc): def _get_start_data(self): if self.operator.prev_res is None: return 0.0, 0 - return (self.operator.prev_res[-1].time[-1], - len(self.operator.prev_res) - 1) + return (self.operator.prev_res[-1].time[-1], len(self.operator.prev_res) - 1) def integrate( - self, - final_step: bool = True, - output: bool = True, - path: PathLike = 'depletion_results.h5' - ): + self, + final_step: bool = True, + output: bool = True, + path: PathLike = "depletion_results.h5", + ): """Perform the entire depletion process across all steps Parameters @@ -813,8 +824,16 @@ def integrate( # Remove actual EOS concentration for next step n = n_list.pop() - StepResult.save(self.operator, n_list, res_list, [t, t + dt], - source_rate, self._i_res + i, proc_time, path) + StepResult.save( + self.operator, + n_list, + res_list, + [t, t + dt], + source_rate, + self._i_res + i, + proc_time, + path, + ) t += dt @@ -825,20 +844,28 @@ def integrate( if output and final_step and comm.rank == 0: print(f"[openmc.deplete] t={t} (final operator evaluation)") res_list = [self.operator(n, source_rate if final_step else 0.0)] - StepResult.save(self.operator, [n], res_list, [t, t], - source_rate, self._i_res + len(self), proc_time, path) + StepResult.save( + self.operator, + [n], + res_list, + [t, t], + source_rate, + self._i_res + len(self), + proc_time, + path, + ) self.operator.write_bos_data(len(self) + self._i_res) self.operator.finalize() def add_transfer_rate( - self, - material: Union[str, int, Material], - components: Sequence[str], - transfer_rate: float, - transfer_rate_units: str = '1/s', - destination_material: Optional[Union[str, int, Material]] = None - ): + self, + material: Union[str, int, Material], + components: Sequence[str], + transfer_rate: float, + transfer_rate_units: str = "1/s", + destination_material: Optional[Union[str, int, Material]] = None, + ): """Add transfer rates to depletable material. Parameters @@ -862,8 +889,14 @@ def add_transfer_rate( if self.transfer_rates is None: self.transfer_rates = TransferRates(self.operator, self.operator.model) - self.transfer_rates.set_transfer_rate(material, components, transfer_rate, - transfer_rate_units, destination_material) + self.transfer_rates.set_transfer_rate( + material, + components, + transfer_rate, + transfer_rate_units, + destination_material, + ) + @add_params class SIIntegrator(Integrator): @@ -952,21 +985,27 @@ class SIIntegrator(Integrator): """ def __init__( - self, - operator: TransportOperator, - timesteps: Sequence[float], - power: Optional[Union[float, Sequence[float]]] = None, - power_density: Optional[Union[float, Sequence[float]]] = None, - source_rates: Optional[Sequence[float]] = None, - timestep_units: str = 's', - n_steps: int = 10, - solver: str = "cram48" - ): + self, + operator: TransportOperator, + timesteps: Sequence[float], + power: Optional[Union[float, Sequence[float]]] = None, + power_density: Optional[Union[float, Sequence[float]]] = None, + source_rates: Optional[Sequence[float]] = None, + timestep_units: str = "s", + n_steps: int = 10, + solver: str = "cram48", + ): check_type("n_steps", n_steps, Integral) check_greater_than("n_steps", n_steps, 0) super().__init__( - operator, timesteps, power, power_density, source_rates, - timestep_units=timestep_units, solver=solver) + operator, + timesteps, + power, + power_density, + source_rates, + timestep_units=timestep_units, + solver=solver, + ) self.n_steps = n_steps def _get_bos_data_from_operator(self, step_index, step_power, n_bos): @@ -974,17 +1013,12 @@ def _get_bos_data_from_operator(self, step_index, step_power, n_bos): if step_index == 0 and hasattr(self.operator, "settings"): reset_particles = True self.operator.settings.particles *= self.n_steps - inherited = super()._get_bos_data_from_operator( - step_index, step_power, n_bos) + inherited = super()._get_bos_data_from_operator(step_index, step_power, n_bos) if reset_particles: self.operator.settings.particles //= self.n_steps return inherited - def integrate( - self, - output: bool = True, - path: PathLike = "depletion_results.h5" - ): + def integrate(self, output: bool = True, path: PathLike = "depletion_results.h5"): """Perform the entire depletion process across all steps Parameters @@ -1023,14 +1057,30 @@ def integrate( # Remove actual EOS concentration for next step n = n_list.pop() - StepResult.save(self.operator, n_list, res_list, [t, t + dt], - p, self._i_res + i, proc_time, path) + StepResult.save( + self.operator, + n_list, + res_list, + [t, t + dt], + p, + self._i_res + i, + proc_time, + path, + ) t += dt # No final simulation for SIE, use last iteration results - StepResult.save(self.operator, [n], [res_list[-1]], [t, t], - p, self._i_res + len(self), proc_time, path) + StepResult.save( + self.operator, + [n], + [res_list[-1]], + [t, t], + p, + self._i_res + len(self), + proc_time, + path, + ) self.operator.write_bos_data(self._i_res + len(self)) self.operator.finalize() diff --git a/openmc/deplete/atom_number.py b/openmc/deplete/atom_number.py index b24c048cc92..542c78dc761 100644 --- a/openmc/deplete/atom_number.py +++ b/openmc/deplete/atom_number.py @@ -2,6 +2,7 @@ An ndarray to store atom densities with string, integer, or slice indexing. """ + import numpy as np from openmc import Material @@ -44,6 +45,7 @@ class AtomNumber: Number of nuclides. """ + def __init__(self, local_mats, nuclides, volume, n_nuc_burn): self.index_mat = {mat: i for i, mat in enumerate(local_mats)} self.index_nuc = {nuc: i for i, nuc in enumerate(nuclides)} @@ -121,8 +123,7 @@ def n_nuc(self): @property def burnable_nuclides(self): - return [nuc for nuc, ind in self.index_nuc.items() - if ind < self.n_nuc_burn] + return [nuc for nuc, ind in self.index_nuc.items() if ind < self.n_nuc_burn] def get_mat_volume(self, mat): """Return material volume @@ -163,7 +164,7 @@ def get_atom_density(self, mat, nuc): return self[mat, nuc] / self.volume[mat] - def get_atom_densities(self, mat, units='atom/b-cm'): + def get_atom_densities(self, mat, units="atom/b-cm"): """Return atom densities for a given material Parameters @@ -182,10 +183,9 @@ def get_atom_densities(self, mat, units='atom/b-cm'): """ mat = self._get_mat_index(mat) - normalization = (1.0e-24 if units == 'atom/b-cm' else 1.0) / self.volume[mat] + normalization = (1.0e-24 if units == "atom/b-cm" else 1.0) / self.volume[mat] return { - name: normalization * self[mat, nuc] - for name, nuc in self.index_nuc.items() + name: normalization * self[mat, nuc] for name, nuc in self.index_nuc.items() } def set_atom_density(self, mat, nuc, val): @@ -222,7 +222,7 @@ def get_mat_slice(self, mat): """ mat = self._get_mat_index(mat) - return self[mat, :self.n_nuc_burn] + return self[mat, : self.n_nuc_burn] def set_mat_slice(self, mat, val): """Sets atom quantity indexed by mats for all burned nuclides @@ -236,7 +236,7 @@ def set_mat_slice(self, mat, val): """ mat = self._get_mat_index(mat) - self[mat, :self.n_nuc_burn] = val + self[mat, : self.n_nuc_burn] = val def set_density(self, total_density): """Sets density. diff --git a/openmc/deplete/chain.py b/openmc/deplete/chain.py index 2e81250dcfb..6f0162e7a93 100644 --- a/openmc/deplete/chain.py +++ b/openmc/deplete/chain.py @@ -23,93 +23,93 @@ # tuple of (possible MT values, secondaries) -ReactionInfo = namedtuple('ReactionInfo', ('mts', 'secondaries')) +ReactionInfo = namedtuple("ReactionInfo", ("mts", "secondaries")) REACTIONS = { - '(n,2nd)': ReactionInfo({11}, ('H2',)), - '(n,2n)': ReactionInfo(set(chain([16], range(875, 892))), ()), - '(n,3n)': ReactionInfo({17}, ()), - '(n,na)': ReactionInfo({22}, ('He4',)), - '(n,n3a)': ReactionInfo({23}, ('He4', 'He4', 'He4')), - '(n,2na)': ReactionInfo({24}, ('He4',)), - '(n,3na)': ReactionInfo({25}, ('He4',)), - '(n,np)': ReactionInfo({28}, ('H1',)), - '(n,n2a)': ReactionInfo({29}, ('He4', 'He4')), - '(n,2n2a)': ReactionInfo({30}, ('He4', 'He4')), - '(n,nd)': ReactionInfo({32}, ('H2',)), - '(n,nt)': ReactionInfo({33}, ('H3',)), - '(n,n3He)': ReactionInfo({34}, ('He3',)), - '(n,nd2a)': ReactionInfo({35}, ('H2', 'He4', 'He4')), - '(n,nt2a)': ReactionInfo({36}, ('H3', 'He4', 'He4')), - '(n,4n)': ReactionInfo({37}, ()), - '(n,2np)': ReactionInfo({41}, ('H1',)), - '(n,3np)': ReactionInfo({42}, ('H1',)), - '(n,n2p)': ReactionInfo({44}, ('H1', 'H1')), - '(n,npa)': ReactionInfo({45}, ('H1', 'He4')), - '(n,gamma)': ReactionInfo({102}, ()), - '(n,p)': ReactionInfo(set(chain([103], range(600, 650))), ('H1',)), - '(n,d)': ReactionInfo(set(chain([104], range(650, 700))), ('H2',)), - '(n,t)': ReactionInfo(set(chain([105], range(700, 750))), ('H3',)), - '(n,3He)': ReactionInfo(set(chain([106], range(750, 800))), ('He3',)), - '(n,a)': ReactionInfo(set(chain([107], range(800, 850))), ('He4',)), - '(n,2a)': ReactionInfo({108}, ('He4', 'He4')), - '(n,3a)': ReactionInfo({109}, ('He4', 'He4', 'He4')), - '(n,2p)': ReactionInfo({111}, ('H1', 'H1')), - '(n,pa)': ReactionInfo({112}, ('H1', 'He4')), - '(n,t2a)': ReactionInfo({113}, ('H3', 'He4', 'He4')), - '(n,d2a)': ReactionInfo({114}, ('H2', 'He4', 'He4')), - '(n,pd)': ReactionInfo({115}, ('H1', 'H2')), - '(n,pt)': ReactionInfo({116}, ('H1', 'H3')), - '(n,da)': ReactionInfo({117}, ('H2', 'He4')), - '(n,5n)': ReactionInfo({152}, ()), - '(n,6n)': ReactionInfo({153}, ()), - '(n,2nt)': ReactionInfo({154}, ('H3',)), - '(n,ta)': ReactionInfo({155}, ('H3', 'He4')), - '(n,4np)': ReactionInfo({156}, ('H1',)), - '(n,3nd)': ReactionInfo({157}, ('H2',)), - '(n,nda)': ReactionInfo({158}, ('H2', 'He4')), - '(n,2npa)': ReactionInfo({159}, ('H1', 'He4')), - '(n,7n)': ReactionInfo({160}, ()), - '(n,8n)': ReactionInfo({161}, ()), - '(n,5np)': ReactionInfo({162}, ('H1',)), - '(n,6np)': ReactionInfo({163}, ('H1',)), - '(n,7np)': ReactionInfo({164}, ('H1',)), - '(n,4na)': ReactionInfo({165}, ('He4',)), - '(n,5na)': ReactionInfo({166}, ('He4',)), - '(n,6na)': ReactionInfo({167}, ('He4',)), - '(n,7na)': ReactionInfo({168}, ('He4',)), - '(n,4nd)': ReactionInfo({169}, ('H2',)), - '(n,5nd)': ReactionInfo({170}, ('H2',)), - '(n,6nd)': ReactionInfo({171}, ('H2',)), - '(n,3nt)': ReactionInfo({172}, ('H3',)), - '(n,4nt)': ReactionInfo({173}, ('H3',)), - '(n,5nt)': ReactionInfo({174}, ('H3',)), - '(n,6nt)': ReactionInfo({175}, ('H3',)), - '(n,2n3He)': ReactionInfo({176}, ('He3',)), - '(n,3n3He)': ReactionInfo({177}, ('He3',)), - '(n,4n3He)': ReactionInfo({178}, ('He3',)), - '(n,3n2p)': ReactionInfo({179}, ('H1', 'H1')), - '(n,3n2a)': ReactionInfo({180}, ('He4', 'He4')), - '(n,3npa)': ReactionInfo({181}, ('H1', 'He4')), - '(n,dt)': ReactionInfo({182}, ('H2', 'H3')), - '(n,npd)': ReactionInfo({183}, ('H1', 'H2')), - '(n,npt)': ReactionInfo({184}, ('H1', 'H3')), - '(n,ndt)': ReactionInfo({185}, ('H2', 'H3')), - '(n,np3He)': ReactionInfo({186}, ('H1', 'He3')), - '(n,nd3He)': ReactionInfo({187}, ('H2', 'He3')), - '(n,nt3He)': ReactionInfo({188}, ('H3', 'He3')), - '(n,nta)': ReactionInfo({189}, ('H3', 'He4')), - '(n,2n2p)': ReactionInfo({190}, ('H1', 'H1')), - '(n,p3He)': ReactionInfo({191}, ('H1', 'He3')), - '(n,d3He)': ReactionInfo({192}, ('H2', 'He3')), - '(n,3Hea)': ReactionInfo({193}, ('He3', 'He4')), - '(n,4n2p)': ReactionInfo({194}, ('H1', 'H1')), - '(n,4n2a)': ReactionInfo({195}, ('He4', 'He4')), - '(n,4npa)': ReactionInfo({196}, ('H1', 'He4')), - '(n,3p)': ReactionInfo({197}, ('H1', 'H1', 'H1')), - '(n,n3p)': ReactionInfo({198}, ('H1', 'H1', 'H1')), - '(n,3n2pa)': ReactionInfo({199}, ('H1', 'H1', 'He4')), - '(n,5n2p)': ReactionInfo({200}, ('H1', 'H1')), + "(n,2nd)": ReactionInfo({11}, ("H2",)), + "(n,2n)": ReactionInfo(set(chain([16], range(875, 892))), ()), + "(n,3n)": ReactionInfo({17}, ()), + "(n,na)": ReactionInfo({22}, ("He4",)), + "(n,n3a)": ReactionInfo({23}, ("He4", "He4", "He4")), + "(n,2na)": ReactionInfo({24}, ("He4",)), + "(n,3na)": ReactionInfo({25}, ("He4",)), + "(n,np)": ReactionInfo({28}, ("H1",)), + "(n,n2a)": ReactionInfo({29}, ("He4", "He4")), + "(n,2n2a)": ReactionInfo({30}, ("He4", "He4")), + "(n,nd)": ReactionInfo({32}, ("H2",)), + "(n,nt)": ReactionInfo({33}, ("H3",)), + "(n,n3He)": ReactionInfo({34}, ("He3",)), + "(n,nd2a)": ReactionInfo({35}, ("H2", "He4", "He4")), + "(n,nt2a)": ReactionInfo({36}, ("H3", "He4", "He4")), + "(n,4n)": ReactionInfo({37}, ()), + "(n,2np)": ReactionInfo({41}, ("H1",)), + "(n,3np)": ReactionInfo({42}, ("H1",)), + "(n,n2p)": ReactionInfo({44}, ("H1", "H1")), + "(n,npa)": ReactionInfo({45}, ("H1", "He4")), + "(n,gamma)": ReactionInfo({102}, ()), + "(n,p)": ReactionInfo(set(chain([103], range(600, 650))), ("H1",)), + "(n,d)": ReactionInfo(set(chain([104], range(650, 700))), ("H2",)), + "(n,t)": ReactionInfo(set(chain([105], range(700, 750))), ("H3",)), + "(n,3He)": ReactionInfo(set(chain([106], range(750, 800))), ("He3",)), + "(n,a)": ReactionInfo(set(chain([107], range(800, 850))), ("He4",)), + "(n,2a)": ReactionInfo({108}, ("He4", "He4")), + "(n,3a)": ReactionInfo({109}, ("He4", "He4", "He4")), + "(n,2p)": ReactionInfo({111}, ("H1", "H1")), + "(n,pa)": ReactionInfo({112}, ("H1", "He4")), + "(n,t2a)": ReactionInfo({113}, ("H3", "He4", "He4")), + "(n,d2a)": ReactionInfo({114}, ("H2", "He4", "He4")), + "(n,pd)": ReactionInfo({115}, ("H1", "H2")), + "(n,pt)": ReactionInfo({116}, ("H1", "H3")), + "(n,da)": ReactionInfo({117}, ("H2", "He4")), + "(n,5n)": ReactionInfo({152}, ()), + "(n,6n)": ReactionInfo({153}, ()), + "(n,2nt)": ReactionInfo({154}, ("H3",)), + "(n,ta)": ReactionInfo({155}, ("H3", "He4")), + "(n,4np)": ReactionInfo({156}, ("H1",)), + "(n,3nd)": ReactionInfo({157}, ("H2",)), + "(n,nda)": ReactionInfo({158}, ("H2", "He4")), + "(n,2npa)": ReactionInfo({159}, ("H1", "He4")), + "(n,7n)": ReactionInfo({160}, ()), + "(n,8n)": ReactionInfo({161}, ()), + "(n,5np)": ReactionInfo({162}, ("H1",)), + "(n,6np)": ReactionInfo({163}, ("H1",)), + "(n,7np)": ReactionInfo({164}, ("H1",)), + "(n,4na)": ReactionInfo({165}, ("He4",)), + "(n,5na)": ReactionInfo({166}, ("He4",)), + "(n,6na)": ReactionInfo({167}, ("He4",)), + "(n,7na)": ReactionInfo({168}, ("He4",)), + "(n,4nd)": ReactionInfo({169}, ("H2",)), + "(n,5nd)": ReactionInfo({170}, ("H2",)), + "(n,6nd)": ReactionInfo({171}, ("H2",)), + "(n,3nt)": ReactionInfo({172}, ("H3",)), + "(n,4nt)": ReactionInfo({173}, ("H3",)), + "(n,5nt)": ReactionInfo({174}, ("H3",)), + "(n,6nt)": ReactionInfo({175}, ("H3",)), + "(n,2n3He)": ReactionInfo({176}, ("He3",)), + "(n,3n3He)": ReactionInfo({177}, ("He3",)), + "(n,4n3He)": ReactionInfo({178}, ("He3",)), + "(n,3n2p)": ReactionInfo({179}, ("H1", "H1")), + "(n,3n2a)": ReactionInfo({180}, ("He4", "He4")), + "(n,3npa)": ReactionInfo({181}, ("H1", "He4")), + "(n,dt)": ReactionInfo({182}, ("H2", "H3")), + "(n,npd)": ReactionInfo({183}, ("H1", "H2")), + "(n,npt)": ReactionInfo({184}, ("H1", "H3")), + "(n,ndt)": ReactionInfo({185}, ("H2", "H3")), + "(n,np3He)": ReactionInfo({186}, ("H1", "He3")), + "(n,nd3He)": ReactionInfo({187}, ("H2", "He3")), + "(n,nt3He)": ReactionInfo({188}, ("H3", "He3")), + "(n,nta)": ReactionInfo({189}, ("H3", "He4")), + "(n,2n2p)": ReactionInfo({190}, ("H1", "H1")), + "(n,p3He)": ReactionInfo({191}, ("H1", "He3")), + "(n,d3He)": ReactionInfo({192}, ("H2", "He3")), + "(n,3Hea)": ReactionInfo({193}, ("He3", "He4")), + "(n,4n2p)": ReactionInfo({194}, ("H1", "H1")), + "(n,4n2a)": ReactionInfo({195}, ("He4", "He4")), + "(n,4npa)": ReactionInfo({196}, ("H1", "He4")), + "(n,3p)": ReactionInfo({197}, ("H1", "H1", "H1")), + "(n,n3p)": ReactionInfo({198}, ("H1", "H1", "H1")), + "(n,3n2pa)": ReactionInfo({199}, ("H1", "H1", "He4")), + "(n,5n2p)": ReactionInfo({200}, ("H1", "H1")), } __all__ = ["Chain", "REACTIONS"] @@ -141,15 +141,15 @@ def replace_missing(product, decay_data): # First check if ground state is available if state: - product = f'{symbol}{A}' + product = f"{symbol}{A}" # Find isotope with longest half-life half_life = 0.0 for nuclide, data in decay_data.items(): - m = re.match(r'{}(\d+)(?:_m\d+)?'.format(symbol), nuclide) + m = re.match(r"{}(\d+)(?:_m\d+)?".format(symbol), nuclide) if m: # If we find a stable nuclide, stop search - if data.nuclide['stable']: + if data.nuclide["stable"]: mass_longest_lived = int(m.group(1)) break if data.half_life.nominal_value > half_life: @@ -158,7 +158,7 @@ def replace_missing(product, decay_data): # If mass number of longest-lived isotope is less than that of missing # product, assume it undergoes beta-. Otherwise assume beta+. - beta_minus = (mass_longest_lived < A) + beta_minus = mass_longest_lived < A # Iterate until we find an existing nuclide while product not in decay_data: @@ -172,7 +172,7 @@ def replace_missing(product, decay_data): Z += 1 else: Z -= 1 - product = f'{openmc.data.ATOMIC_SYMBOL[Z]}{A}' + product = f"{openmc.data.ATOMIC_SYMBOL[Z]}{A}" return product @@ -222,7 +222,7 @@ def replace_missing_fpy(actinide, fpy_data, decay_data): return isotone # If all else fails, use U235 yields - return 'U235' + return "U235" class Chain: @@ -290,9 +290,13 @@ def add_nuclide(self, nuclide): self.reactions.append(rx.type) @classmethod - def from_endf(cls, decay_files, fpy_files, neutron_files, - reactions=('(n,2n)', '(n,3n)', '(n,4n)', '(n,gamma)', '(n,p)', '(n,a)'), - progress=True + def from_endf( + cls, + decay_files, + fpy_files, + neutron_files, + reactions=("(n,2n)", "(n,3n)", "(n,4n)", "(n,gamma)", "(n,p)", "(n,a)"), + progress=True, ): """Create a depletion chain from ENDF files. @@ -339,7 +343,7 @@ def from_endf(cls, decay_files, fpy_files, neutron_files, # Create dictionary mapping target to filename if progress: - print('Processing neutron sub-library files...') + print("Processing neutron sub-library files...") reactions = {} for f in neutron_files: evaluation = openmc.data.endf.Evaluation(f) @@ -354,24 +358,24 @@ def from_endf(cls, decay_files, fpy_files, neutron_files, # Determine what decay and FPY nuclides are available if progress: - print('Processing decay sub-library files...') + print("Processing decay sub-library files...") decay_data = {} for f in decay_files: data = openmc.data.Decay(f) # Skip decay data for neutron itself - if data.nuclide['atomic_number'] == 0: + if data.nuclide["atomic_number"] == 0: continue - decay_data[data.nuclide['name']] = data + decay_data[data.nuclide["name"]] = data if progress: - print('Processing fission product yield sub-library files...') + print("Processing fission product yield sub-library files...") fpy_data = {} for f in fpy_files: data = openmc.data.FissionProductYields(f) - fpy_data[data.nuclide['name']] = data + fpy_data[data.nuclide["name"]] = data if progress: - print('Creating depletion_chain...') + print("Creating depletion_chain...") missing_daughter = [] missing_rx_product = [] missing_fpy = [] @@ -383,18 +387,17 @@ def from_endf(cls, decay_files, fpy_files, neutron_files, nuclide = Nuclide(parent) - if not data.nuclide['stable'] and data.half_life.nominal_value != 0.0: + if not data.nuclide["stable"] and data.half_life.nominal_value != 0.0: nuclide.half_life = data.half_life.nominal_value nuclide.decay_energy = data.decay_energy.nominal_value branch_ratios = [] branch_ids = [] for mode in data.modes: - type_ = ','.join(mode.modes) + type_ = ",".join(mode.modes) if mode.daughter in decay_data: target = mode.daughter else: - print('missing {} {} {}'.format( - parent, type_, mode.daughter)) + print("missing {} {} {}".format(parent, type_, mode.daughter)) target = replace_missing(mode.daughter, decay_data) br = mode.branching_ratio.nominal_value branch_ratios.append(br) @@ -422,9 +425,9 @@ def from_endf(cls, decay_files, fpy_files, neutron_files, mts = REACTIONS[name].mts delta_A, delta_Z = openmc.data.DADZ[name] if mts & reactions_available: - A = data.nuclide['mass_number'] + delta_A - Z = data.nuclide['atomic_number'] + delta_Z - daughter = f'{openmc.data.ATOMIC_SYMBOL[Z]}{A}' + A = data.nuclide["mass_number"] + delta_A + Z = data.nuclide["atomic_number"] + delta_Z + daughter = f"{openmc.data.ATOMIC_SYMBOL[Z]}{A}" if daughter not in decay_data: daughter = replace_missing(daughter, decay_data) @@ -443,10 +446,9 @@ def from_endf(cls, decay_files, fpy_files, neutron_files, if any(mt in reactions_available for mt in openmc.data.FISSION_MTS): q_value = reactions[parent][18] - nuclide.add_reaction('fission', None, q_value, 1.0) + nuclide.add_reaction("fission", None, q_value, 1.0) fissionable = True - if fissionable: if parent in fpy_data: fpy = fpy_data[parent] @@ -483,32 +485,32 @@ def from_endf(cls, decay_files, fpy_files, neutron_files, # Replace missing FPY data for nuclide in chain.nuclides: - if hasattr(nuclide, '_fpy'): + if hasattr(nuclide, "_fpy"): nuclide.yield_data = chain[nuclide._fpy].yield_data # Display warnings if missing_daughter: - print('The following decay modes have daughters with no decay data:') + print("The following decay modes have daughters with no decay data:") for mode in missing_daughter: - print(f' {mode}') - print('') + print(f" {mode}") + print("") if missing_rx_product: - print('The following reaction products have no decay data:') + print("The following reaction products have no decay data:") for vals in missing_rx_product: - print('{} {} -> {}'.format(*vals)) - print('') + print("{} {} -> {}".format(*vals)) + print("") if missing_fpy: - print('The following fissionable nuclides have no fission product yields:') + print("The following fissionable nuclides have no fission product yields:") for parent, replacement in missing_fpy: - print(f' {parent}, replaced with {replacement}') - print('') + print(f" {parent}, replaced with {replacement}") + print("") if missing_fp: - print('The following nuclides have fission products with no decay data:') + print("The following nuclides have fission products with no decay data:") for vals in missing_fp: - print(' {}, E={} eV (total yield={})'.format(*vals)) + print(" {}, E={} eV (total yield={})".format(*vals)) return chain @@ -535,7 +537,7 @@ def from_xml(cls, filename, fission_q=None): # Load XML tree root = ET.parse(str(filename)) - for i, nuclide_elem in enumerate(root.findall('nuclide')): + for i, nuclide_elem in enumerate(root.findall("nuclide")): this_q = fission_q.get(nuclide_elem.get("name")) nuc = Nuclide.from_xml(nuclide_elem, root, this_q) @@ -553,12 +555,12 @@ def export_to_xml(self, filename): """ - root_elem = ET.Element('depletion_chain') + root_elem = ET.Element("depletion_chain") for nuclide in self.nuclides: root_elem.append(nuclide.to_xml_element()) tree = ET.ElementTree(root_elem) - tree.write(str(filename), encoding='utf-8', pretty_print=True) + tree.write(str(filename), encoding="utf-8", pretty_print=True) def get_default_fission_yields(self): """Return fission yields at lowest incident neutron energy @@ -632,15 +634,15 @@ def form_matrix(self, rates, fission_yields=None): matrix[k, i] += branch_val # Produce alphas and protons from decay - if 'alpha' in decay_type: - k = self.nuclide_dict.get('He4') + if "alpha" in decay_type: + k = self.nuclide_dict.get("He4") if k is not None: - count = decay_type.count('alpha') + count = decay_type.count("alpha") matrix[k, i] += count * branch_val - elif 'p' in decay_type: - k = self.nuclide_dict.get('H1') + elif "p" in decay_type: + k = self.nuclide_dict.get("H1") if k is not None: - count = decay_type.count('p') + count = decay_type.count("p") matrix[k, i] += count * branch_val if nuc.name in rates.index_nuc: @@ -661,7 +663,7 @@ def form_matrix(self, rates, fission_yields=None): matrix[i, i] -= path_rate # Gain term; allow for total annihilation for debug purposes - if r_type != 'fission': + if r_type != "fission": if target is not None and path_rate != 0.0: k = self.nuclide_dict[target] matrix[k, i] += path_rate * br @@ -723,7 +725,7 @@ def form_rr_term(self, tr_rates, mats): matrix = sp.dok_matrix((n, n)) for i, nuc in enumerate(self.nuclides): - elm = re.split(r'\d+', nuc.name)[0] + elm = re.split(r"\d+", nuc.name)[0] # Build transfer terms matrices if isinstance(mats, str): mat = mats @@ -734,18 +736,24 @@ def form_rr_term(self, tr_rates, mats): matrix[i, i] = sum(tr_rates.get_transfer_rate(mat, nuc.name)) else: matrix[i, i] = 0.0 - #Build transfer terms matrices + # Build transfer terms matrices elif isinstance(mats, tuple): dest_mat, mat = mats if dest_mat in tr_rates.get_destination_material(mat, elm): - dest_mat_idx = tr_rates.get_destination_material(mat, elm).index(dest_mat) + dest_mat_idx = tr_rates.get_destination_material(mat, elm).index( + dest_mat + ) matrix[i, i] = tr_rates.get_transfer_rate(mat, elm)[dest_mat_idx] elif dest_mat in tr_rates.get_destination_material(mat, nuc.name): - dest_mat_idx = tr_rates.get_destination_material(mat, nuc.name).index(dest_mat) - matrix[i, i] = tr_rates.get_transfer_rate(mat, nuc.name)[dest_mat_idx] + dest_mat_idx = tr_rates.get_destination_material( + mat, nuc.name + ).index(dest_mat) + matrix[i, i] = tr_rates.get_transfer_rate(mat, nuc.name)[ + dest_mat_idx + ] else: matrix[i, i] = 0.0 - #Nothing else is allowed + # Nothing else is allowed # Return CSC instead of DOK return matrix.tocsc() @@ -783,8 +791,9 @@ def get_branch_ratios(self, reaction="(n,gamma)"): capt[nuclide.name] = nuc_capt return capt - def set_branch_ratios(self, branch_ratios, reaction="(n,gamma)", - strict=True, tolerance=1e-5): + def set_branch_ratios( + self, branch_ratios, reaction="(n,gamma)", strict=True, tolerance=1e-5 + ): """Set the branching ratios for a given reactions Parameters @@ -880,20 +889,23 @@ def set_branch_ratios(self, branch_ratios, reaction="(n,gamma)", if len(indexes) == 0: if strict: raise AttributeError( - f"Nuclide {parent} does not have {reaction} reactions") + f"Nuclide {parent} does not have {reaction} reactions" + ) missing_reaction.add(parent) continue this_sum = sum(sub.values()) # sum of branching ratios can be lower than 1 if no ground # target is given, but never greater - if (this_sum >= 1 + tolerance or (grounds[parent] in sub - and this_sum <= 1 - tolerance)): + if this_sum >= 1 + tolerance or ( + grounds[parent] in sub and this_sum <= 1 - tolerance + ): if strict: - msg = ("Sum of {} branching ratios for {} " - "({:7.3f}) outside tolerance of 1 +/- " - "{:5.3e}".format( - reaction, parent, this_sum, tolerance)) + msg = ( + "Sum of {} branching ratios for {} " + "({:7.3f}) outside tolerance of 1 +/- " + "{:5.3e}".format(reaction, parent, this_sum, tolerance) + ) raise ValueError(msg) bad_sums[parent] = this_sum else: @@ -902,29 +914,39 @@ def set_branch_ratios(self, branch_ratios, reaction="(n,gamma)", if len(rxn_ix_map) == 0: raise IndexError( - f"No {reaction} reactions found in this {self.__class__.__name__}") + f"No {reaction} reactions found in this {self.__class__.__name__}" + ) if len(missing_parents) > 0: - warn("The following nuclides were not found in {}: {}".format( - self.__class__.__name__, ", ".join(sorted(missing_parents)))) + warn( + "The following nuclides were not found in {}: {}".format( + self.__class__.__name__, ", ".join(sorted(missing_parents)) + ) + ) if len(missing_reaction) > 0: - warn("The following nuclides did not have {} reactions: " - "{}".format(reaction, ", ".join(sorted(missing_reaction)))) + warn( + "The following nuclides did not have {} reactions: " + "{}".format(reaction, ", ".join(sorted(missing_reaction))) + ) if len(missing_products) > 0: - tail = (f"{k} -> {v}" - for k, v in sorted(missing_products.items())) - warn("The following products were not found in the {} and " - "parents were unmodified: \n{}".format( - self.__class__.__name__, ", ".join(tail))) + tail = (f"{k} -> {v}" for k, v in sorted(missing_products.items())) + warn( + "The following products were not found in the {} and " + "parents were unmodified: \n{}".format( + self.__class__.__name__, ", ".join(tail) + ) + ) if len(bad_sums) > 0: - tail = (f"{k}: {s:5.3f}" - for k, s in sorted(bad_sums.items())) - warn("The following parent nuclides were given {} branch ratios " - "with a sum outside tolerance of 1 +/- {:5.3e}:\n{}".format( - reaction, tolerance, "\n".join(tail))) + tail = (f"{k}: {s:5.3f}" for k, s in sorted(bad_sums.items())) + warn( + "The following parent nuclides were given {} branch ratios " + "with a sum outside tolerance of 1 +/- {:5.3e}:\n{}".format( + reaction, tolerance, "\n".join(tail) + ) + ) # Insert new ReactionTuples with updated branch ratios @@ -1088,7 +1110,7 @@ def reduce(self, initial_isotopes, level=None): new_nuclide.half_life = previous.half_life new_nuclide.decay_energy = previous.decay_energy new_nuclide.sources = previous.sources.copy() - if hasattr(previous, '_fpy'): + if hasattr(previous, "_fpy"): new_nuclide._fpy = previous._fpy for mode in previous.decay_modes: @@ -1102,7 +1124,8 @@ def reduce(self, initial_isotopes, level=None): new_nuclide.add_reaction(*rx) elif rx.type == "fission": new_yields = new_nuclide.yield_data = ( - previous.yield_data.restrict_products(name_sort)) + previous.yield_data.restrict_products(name_sort) + ) if new_yields is not None: new_nuclide.add_reaction(*rx) # Maintain total destruction rates but set no target @@ -1124,7 +1147,8 @@ def _follow(self, isotopes, level): if not found.issubset(remaining): raise IndexError( "The following isotopes were not found in the chain: " - "{}".format(", ".join(found - remaining))) + "{}".format(", ".join(found - remaining)) + ) if level == 0: return found @@ -1159,15 +1183,21 @@ def _follow(self, isotopes, level): if product is None: continue # Skip if we've already come across this isotope - elif (product in next_iso or product in found - or product in isotopes): + elif ( + product in next_iso + or product in found + or product in isotopes + ): continue next_iso.add(product) if nuclide.yield_data is not None: for product in nuclide.yield_data.products: - if (product in next_iso - or product in found or product in isotopes): + if ( + product in next_iso + or product in found + or product in isotopes + ): continue next_iso.add(product) diff --git a/openmc/deplete/coupled_operator.py b/openmc/deplete/coupled_operator.py index 17af935f4a2..351776b3909 100644 --- a/openmc/deplete/coupled_operator.py +++ b/openmc/deplete/coupled_operator.py @@ -25,9 +25,15 @@ from .pool import _distribute from .results import Results from .helpers import ( - DirectReactionRateHelper, ChainFissionHelper, ConstantFissionYieldHelper, - FissionYieldCutoffHelper, AveragedFissionYieldHelper, EnergyScoreHelper, - SourceRateHelper, FluxCollapseHelper) + DirectReactionRateHelper, + ChainFissionHelper, + ConstantFissionYieldHelper, + FissionYieldCutoffHelper, + AveragedFissionYieldHelper, + EnergyScoreHelper, + SourceRateHelper, + FluxCollapseHelper, +) __all__ = ["CoupledOperator", "Operator", "OperatorResult"] @@ -74,9 +80,9 @@ def _get_nuclides_with_data(cross_sections): nuclides = set() data_lib = DataLibrary.from_xml(cross_sections) for library in data_lib.libraries: - if library['type'] != 'neutron': + if library["type"] != "neutron": continue - for name in library['materials']: + for name in library["materials"]: if name not in nuclides: nuclides.add(name) @@ -203,35 +209,52 @@ class CoupledOperator(OpenMCOperator): Whether to finalize and clear the shared library memory when the depletion operation is complete. Defaults to clearing the library. """ + _fission_helpers = { "average": AveragedFissionYieldHelper, "constant": ConstantFissionYieldHelper, "cutoff": FissionYieldCutoffHelper, } - def __init__(self, model, chain_file=None, prev_results=None, - diff_burnable_mats=False, diff_volume_method="divide equally", - normalization_mode="fission-q", fission_q=None, - fission_yield_mode="constant", fission_yield_opts=None, - reaction_rate_mode="direct", reaction_rate_opts=None, - reduce_chain=False, reduce_chain_level=None): + def __init__( + self, + model, + chain_file=None, + prev_results=None, + diff_burnable_mats=False, + diff_volume_method="divide equally", + normalization_mode="fission-q", + fission_q=None, + fission_yield_mode="constant", + fission_yield_opts=None, + reaction_rate_mode="direct", + reaction_rate_opts=None, + reduce_chain=False, + reduce_chain_level=None, + ): # check for old call to constructor if isinstance(model, openmc.Geometry): - msg = "As of version 0.13.0 openmc.deplete.CoupledOperator " \ - "requires an openmc.Model object rather than the " \ - "openmc.Geometry and openmc.Settings parameters. Please use " \ - "the geometry and settings objects passed here to create a " \ + msg = ( + "As of version 0.13.0 openmc.deplete.CoupledOperator " + "requires an openmc.Model object rather than the " + "openmc.Geometry and openmc.Settings parameters. Please use " + "the geometry and settings objects passed here to create a " " model with which to generate the transport Operator." + ) raise TypeError(msg) # Determine cross sections cross_sections = _find_cross_sections(model) - check_value('fission yield mode', fission_yield_mode, - self._fission_helpers.keys()) - check_value('normalization mode', normalization_mode, - ('energy-deposition', 'fission-q', 'source-rate')) + check_value( + "fission yield mode", fission_yield_mode, self._fission_helpers.keys() + ) + check_value( + "normalization mode", + normalization_mode, + ("energy-deposition", "fission-q", "source-rate"), + ) if normalization_mode != "fission-q": if fission_q is not None: warn("Fission Q dictionary will not be used") @@ -251,11 +274,11 @@ def __init__(self, model, chain_file=None, prev_results=None, if fission_yield_opts is None: fission_yield_opts = {} helper_kwargs = { - 'reaction_rate_mode': reaction_rate_mode, - 'normalization_mode': normalization_mode, - 'fission_yield_mode': fission_yield_mode, - 'reaction_rate_opts': reaction_rate_opts, - 'fission_yield_opts': fission_yield_opts + "reaction_rate_mode": reaction_rate_mode, + "normalization_mode": normalization_mode, + "fission_yield_mode": fission_yield_mode, + "reaction_rate_opts": reaction_rate_opts, + "fission_yield_opts": fission_yield_opts, } # Records how many times the operator has been called @@ -271,7 +294,8 @@ def __init__(self, model, chain_file=None, prev_results=None, fission_q=fission_q, helper_kwargs=helper_kwargs, reduce_chain=reduce_chain, - reduce_chain_level=reduce_chain_level) + reduce_chain_level=reduce_chain_level, + ) def _differentiate_burnable_mats(self): """Assign distribmats for each burnable material""" @@ -322,28 +346,30 @@ def _get_helper_classes(self, helper_kwargs): Keyword arguments for helper classes """ - reaction_rate_mode = helper_kwargs['reaction_rate_mode'] - normalization_mode = helper_kwargs['normalization_mode'] - fission_yield_mode = helper_kwargs['fission_yield_mode'] - reaction_rate_opts = helper_kwargs['reaction_rate_opts'] - fission_yield_opts = helper_kwargs['fission_yield_opts'] + reaction_rate_mode = helper_kwargs["reaction_rate_mode"] + normalization_mode = helper_kwargs["normalization_mode"] + fission_yield_mode = helper_kwargs["fission_yield_mode"] + reaction_rate_opts = helper_kwargs["reaction_rate_opts"] + fission_yield_opts = helper_kwargs["fission_yield_opts"] # Get classes to assist working with tallies if reaction_rate_mode == "direct": self._rate_helper = DirectReactionRateHelper( - self.reaction_rates.n_nuc, self.reaction_rates.n_react) + self.reaction_rates.n_nuc, self.reaction_rates.n_react + ) elif reaction_rate_mode == "flux": # Ensure energy group boundaries were specified - if 'energies' not in reaction_rate_opts: + if "energies" not in reaction_rate_opts: raise ValueError( "Energy group boundaries must be specified in the " "reaction_rate_opts argument when reaction_rate_mode is" - "set to 'flux'.") + "set to 'flux'." + ) self._rate_helper = FluxCollapseHelper( self.reaction_rates.n_nuc, self.reaction_rates.n_react, - **reaction_rate_opts + **reaction_rate_opts, ) else: raise ValueError("Invalid reaction rate mode.") @@ -351,15 +377,16 @@ def _get_helper_classes(self, helper_kwargs): if normalization_mode == "fission-q": self._normalization_helper = ChainFissionHelper() elif normalization_mode == "energy-deposition": - score = "heating" if self.model.settings.photon_transport else "heating-local" + score = ( + "heating" if self.model.settings.photon_transport else "heating-local" + ) self._normalization_helper = EnergyScoreHelper(score) else: self._normalization_helper = SourceRateHelper() # Select and create fission yield helper fission_helper = self._fission_helpers[fission_yield_mode] - self._yield_helper = fission_helper.from_operator( - self, **fission_yield_opts) + self._yield_helper = fission_helper.from_operator(self, **fission_yield_opts) def initial_condition(self): """Performs final setup and returns initial condition. @@ -492,10 +519,11 @@ def _update_materials(self): # negative. CRAM does not guarantee positive # values. if val < -1.0e-21: - print(f'WARNING: nuclide {nuc} in material' - f'{mat} is negative (density = {val}' - - ' atom/b-cm)') + print( + f"WARNING: nuclide {nuc} in material" + f"{mat} is negative (density = {val}" + " atom/b-cm)" + ) number_i[mat, nuc] = 0.0 @@ -516,9 +544,7 @@ def write_bos_data(step): Current depletion step including restarts """ - openmc.lib.statepoint_write( - f"openmc_simulation_n{step}.h5", - write_source=False) + openmc.lib.statepoint_write(f"openmc_simulation_n{step}.h5", write_source=False) def finalize(self): """Finalize a depletion simulation and release resources.""" @@ -563,6 +589,6 @@ def Operator(*args, **kwargs): "The Operator(...) class has been renamed and will " "be removed in a future version of OpenMC. Use " "CoupledOperator(...) instead.", - FutureWarning + FutureWarning, ) return CoupledOperator(*args, **kwargs) diff --git a/openmc/deplete/cram.py b/openmc/deplete/cram.py index 53de83bb68e..fe0a324d9d1 100644 --- a/openmc/deplete/cram.py +++ b/openmc/deplete/cram.py @@ -77,34 +77,40 @@ def __call__(self, A, n0, dt): """ A = dt * sp.csc_matrix(A, dtype=np.float64) y = n0.copy() - ident = sp.eye(A.shape[0], format='csc') + ident = sp.eye(A.shape[0], format="csc") for alpha, theta in zip(self.alpha, self.theta): - y += 2*np.real(alpha*sla.spsolve(A - theta*ident, y)) + y += 2 * np.real(alpha * sla.spsolve(A - theta * ident, y)) return y * self.alpha0 # Coefficients for IPF Cram 16 -c16_alpha = np.array([ - +5.464930576870210e+3 - 3.797983575308356e+4j, - +9.045112476907548e+1 - 1.115537522430261e+3j, - +2.344818070467641e+2 - 4.228020157070496e+2j, - +9.453304067358312e+1 - 2.951294291446048e+2j, - +7.283792954673409e+2 - 1.205646080220011e+5j, - +3.648229059594851e+1 - 1.155509621409682e+2j, - +2.547321630156819e+1 - 2.639500283021502e+1j, - +2.394538338734709e+1 - 5.650522971778156e+0j], - dtype=np.complex128) - -c16_theta = np.array([ - +3.509103608414918 + 8.436198985884374j, - +5.948152268951177 + 3.587457362018322j, - -5.264971343442647 + 16.22022147316793j, - +1.419375897185666 + 10.92536348449672j, - +6.416177699099435 + 1.194122393370139j, - +4.993174737717997 + 5.996881713603942j, - -1.413928462488886 + 13.49772569889275j, - -10.84391707869699 + 19.27744616718165j], - dtype=np.complex128) +c16_alpha = np.array( + [ + +5.464930576870210e3 - 3.797983575308356e4j, + +9.045112476907548e1 - 1.115537522430261e3j, + +2.344818070467641e2 - 4.228020157070496e2j, + +9.453304067358312e1 - 2.951294291446048e2j, + +7.283792954673409e2 - 1.205646080220011e5j, + +3.648229059594851e1 - 1.155509621409682e2j, + +2.547321630156819e1 - 2.639500283021502e1j, + +2.394538338734709e1 - 5.650522971778156e0j, + ], + dtype=np.complex128, +) + +c16_theta = np.array( + [ + +3.509103608414918 + 8.436198985884374j, + +5.948152268951177 + 3.587457362018322j, + -5.264971343442647 + 16.22022147316793j, + +1.419375897185666 + 10.92536348449672j, + +6.416177699099435 + 1.194122393370139j, + +4.993174737717997 + 5.996881713603942j, + -1.413928462488886 + 13.49772569889275j, + -10.84391707869699 + 19.27744616718165j, + ], + dtype=np.complex128, +) c16_alpha0 = 2.124853710495224e-16 Cram16Solver = IPFCramSolver(c16_alpha, c16_theta, c16_alpha0) @@ -114,63 +120,123 @@ def __call__(self, A, n0, dt): # Coefficients for 48th order IPF Cram -theta_r = np.array([ - -4.465731934165702e+1, -5.284616241568964e+0, - -8.867715667624458e+0, +3.493013124279215e+0, - +1.564102508858634e+1, +1.742097597385893e+1, - -2.834466755180654e+1, +1.661569367939544e+1, - +8.011836167974721e+0, -2.056267541998229e+0, - +1.449208170441839e+1, +1.853807176907916e+1, - +9.932562704505182e+0, -2.244223871767187e+1, - +8.590014121680897e-1, -1.286192925744479e+1, - +1.164596909542055e+1, +1.806076684783089e+1, - +5.870672154659249e+0, -3.542938819659747e+1, - +1.901323489060250e+1, +1.885508331552577e+1, - -1.734689708174982e+1, +1.316284237125190e+1]) - -theta_i = np.array([ - +6.233225190695437e+1, +4.057499381311059e+1, - +4.325515754166724e+1, +3.281615453173585e+1, - +1.558061616372237e+1, +1.076629305714420e+1, - +5.492841024648724e+1, +1.316994930024688e+1, - +2.780232111309410e+1, +3.794824788914354e+1, - +1.799988210051809e+1, +5.974332563100539e+0, - +2.532823409972962e+1, +5.179633600312162e+1, - +3.536456194294350e+1, +4.600304902833652e+1, - +2.287153304140217e+1, +8.368200580099821e+0, - +3.029700159040121e+1, +5.834381701800013e+1, - +1.194282058271408e+0, +3.583428564427879e+0, - +4.883941101108207e+1, +2.042951874827759e+1]) +theta_r = np.array( + [ + -4.465731934165702e1, + -5.284616241568964e0, + -8.867715667624458e0, + +3.493013124279215e0, + +1.564102508858634e1, + +1.742097597385893e1, + -2.834466755180654e1, + +1.661569367939544e1, + +8.011836167974721e0, + -2.056267541998229e0, + +1.449208170441839e1, + +1.853807176907916e1, + +9.932562704505182e0, + -2.244223871767187e1, + +8.590014121680897e-1, + -1.286192925744479e1, + +1.164596909542055e1, + +1.806076684783089e1, + +5.870672154659249e0, + -3.542938819659747e1, + +1.901323489060250e1, + +1.885508331552577e1, + -1.734689708174982e1, + +1.316284237125190e1, + ] +) + +theta_i = np.array( + [ + +6.233225190695437e1, + +4.057499381311059e1, + +4.325515754166724e1, + +3.281615453173585e1, + +1.558061616372237e1, + +1.076629305714420e1, + +5.492841024648724e1, + +1.316994930024688e1, + +2.780232111309410e1, + +3.794824788914354e1, + +1.799988210051809e1, + +5.974332563100539e0, + +2.532823409972962e1, + +5.179633600312162e1, + +3.536456194294350e1, + +4.600304902833652e1, + +2.287153304140217e1, + +8.368200580099821e0, + +3.029700159040121e1, + +5.834381701800013e1, + +1.194282058271408e0, + +3.583428564427879e0, + +4.883941101108207e1, + +2.042951874827759e1, + ] +) c48_theta = np.array(theta_r + theta_i * 1j, dtype=np.complex128) -alpha_r = np.array([ - +6.387380733878774e+2, +1.909896179065730e+2, - +4.236195226571914e+2, +4.645770595258726e+2, - +7.765163276752433e+2, +1.907115136768522e+3, - +2.909892685603256e+3, +1.944772206620450e+2, - +1.382799786972332e+5, +5.628442079602433e+3, - +2.151681283794220e+2, +1.324720240514420e+3, - +1.617548476343347e+4, +1.112729040439685e+2, - +1.074624783191125e+2, +8.835727765158191e+1, - +9.354078136054179e+1, +9.418142823531573e+1, - +1.040012390717851e+2, +6.861882624343235e+1, - +8.766654491283722e+1, +1.056007619389650e+2, - +7.738987569039419e+1, +1.041366366475571e+2]) - -alpha_i = np.array([ - -6.743912502859256e+2, -3.973203432721332e+2, - -2.041233768918671e+3, -1.652917287299683e+3, - -1.783617639907328e+4, -5.887068595142284e+4, - -9.953255345514560e+3, -1.427131226068449e+3, - -3.256885197214938e+6, -2.924284515884309e+4, - -1.121774011188224e+3, -6.370088443140973e+4, - -1.008798413156542e+6, -8.837109731680418e+1, - -1.457246116408180e+2, -6.388286188419360e+1, - -2.195424319460237e+2, -6.719055740098035e+2, - -1.693747595553868e+2, -1.177598523430493e+1, - -4.596464999363902e+3, -1.738294585524067e+3, - -4.311715386228984e+1, -2.777743732451969e+2]) +alpha_r = np.array( + [ + +6.387380733878774e2, + +1.909896179065730e2, + +4.236195226571914e2, + +4.645770595258726e2, + +7.765163276752433e2, + +1.907115136768522e3, + +2.909892685603256e3, + +1.944772206620450e2, + +1.382799786972332e5, + +5.628442079602433e3, + +2.151681283794220e2, + +1.324720240514420e3, + +1.617548476343347e4, + +1.112729040439685e2, + +1.074624783191125e2, + +8.835727765158191e1, + +9.354078136054179e1, + +9.418142823531573e1, + +1.040012390717851e2, + +6.861882624343235e1, + +8.766654491283722e1, + +1.056007619389650e2, + +7.738987569039419e1, + +1.041366366475571e2, + ] +) + +alpha_i = np.array( + [ + -6.743912502859256e2, + -3.973203432721332e2, + -2.041233768918671e3, + -1.652917287299683e3, + -1.783617639907328e4, + -5.887068595142284e4, + -9.953255345514560e3, + -1.427131226068449e3, + -3.256885197214938e6, + -2.924284515884309e4, + -1.121774011188224e3, + -6.370088443140973e4, + -1.008798413156542e6, + -8.837109731680418e1, + -1.457246116408180e2, + -6.388286188419360e1, + -2.195424319460237e2, + -6.719055740098035e2, + -1.693747595553868e2, + -1.177598523430493e1, + -4.596464999363902e3, + -1.738294585524067e3, + -4.311715386228984e1, + -2.777743732451969e2, + ] +) c48_alpha = np.array(alpha_r + alpha_i * 1j, dtype=np.complex128) diff --git a/openmc/deplete/helpers.py b/openmc/deplete/helpers.py index f24b89868b8..16280588706 100644 --- a/openmc/deplete/helpers.py +++ b/openmc/deplete/helpers.py @@ -1,6 +1,7 @@ """ Classes for collecting and calculating quantities for reaction rate operators """ + import bisect from abc import abstractmethod from collections import defaultdict @@ -15,16 +16,25 @@ from openmc.checkvalue import check_type, check_greater_than from openmc.data import JOULE_PER_EV, REACTION_MT from openmc.lib import ( - Tally, MaterialFilter, EnergyFilter, EnergyFunctionFilter, load_nuclide) + Tally, + MaterialFilter, + EnergyFilter, + EnergyFunctionFilter, + load_nuclide, +) import openmc.lib -from .abc import ( - ReactionRateHelper, NormalizationHelper, FissionYieldHelper) +from .abc import ReactionRateHelper, NormalizationHelper, FissionYieldHelper __all__ = ( - "DirectReactionRateHelper", "ChainFissionHelper", "EnergyScoreHelper" - "SourceRateHelper", "TalliedFissionYieldHelper", - "ConstantFissionYieldHelper", "FissionYieldCutoffHelper", - "AveragedFissionYieldHelper", "FluxCollapseHelper") + "DirectReactionRateHelper", + "ChainFissionHelper", + "EnergyScoreHelper" "SourceRateHelper", + "TalliedFissionYieldHelper", + "ConstantFissionYieldHelper", + "FissionYieldCutoffHelper", + "AveragedFissionYieldHelper", + "FluxCollapseHelper", +) class TalliedFissionYieldHelper(FissionYieldHelper): @@ -78,7 +88,7 @@ def generate_tallies(self, materials, mat_indexes): # Tally group-wise fission reaction rates self._fission_rate_tally = Tally() self._fission_rate_tally.writable = False - self._fission_rate_tally.scores = ['fission'] + self._fission_rate_tally.scores = ["fission"] self._fission_rate_tally.filters = [MaterialFilter(materials)] def update_tally_nuclides(self, nuclides): @@ -103,8 +113,7 @@ def update_tally_nuclides(self, nuclides): AttributeError If tallies not generated """ - assert self._fission_rate_tally is not None, ( - "Run generate_tallies first") + assert self._fission_rate_tally is not None, "Run generate_tallies first" overlap = set(self._chain_nuclides).intersection(set(nuclides)) nuclides = sorted(overlap) self._tally_nucs = [self._chain_nuclides[n] for n in nuclides] @@ -145,6 +154,7 @@ class DirectReactionRateHelper(ReactionRateHelper): nuclides : list of str All nuclides with desired reaction rates. """ + def __init__(self, n_nuc, n_react): super().__init__(n_nuc, n_react) self._rate_tally = None @@ -181,8 +191,7 @@ def generate_tallies(self, materials, scores): @property def rate_tally_means(self): - """The mean results of the tally of every material's reaction rates for this cycle - """ + """The mean results of the tally of every material's reaction rates for this cycle""" # If the mean cache is empty, fill it once with this transport cycle's results if self._rate_tally_means_cache is None: self._rate_tally_means_cache = self._rate_tally.mean @@ -256,6 +265,7 @@ class FluxCollapseHelper(ReactionRateHelper): All nuclides with desired reaction rates. """ + def __init__(self, n_nucs, n_reacts, energies, reactions=None, nuclides=None): super().__init__(n_nucs, n_reacts) self._energies = asarray(energies) @@ -299,9 +309,9 @@ def generate_tallies(self, materials, scores): self._flux_tally.writable = False self._flux_tally.filters = [ MaterialFilter(materials), - EnergyFilter(self._energies) + EnergyFilter(self._energies), ] - self._flux_tally.scores = ['flux'] + self._flux_tally.scores = ["flux"] self._flux_tally_means_cache = None # Create reaction rate tally @@ -323,8 +333,7 @@ def generate_tallies(self, materials, scores): @property def rate_tally_means(self): - """The mean results of the tally of every material's reaction rates for this cycle - """ + """The mean results of the tally of every material's reaction rates for this cycle""" # If the mean cache is empty, fill it once with this transport cycle's results if self._rate_tally_means_cache is None: self._rate_tally_means_cache = self._rate_tally.mean @@ -390,12 +399,15 @@ def get_material_rates(self, mat_index, nuc_index, react_index): i_nuc_direct = nuclides_direct.index(name) # Get reaction rate from tally - self._results_cache[i_nuc, i_rx] = rx_rates[i_nuc_direct, i_rx_direct] + self._results_cache[i_nuc, i_rx] = rx_rates[ + i_nuc_direct, i_rx_direct + ] else: # Use flux to collapse reaction rate (per N) nuc = openmc.lib.nuclides[name] rate_per_nuc = nuc.collapse_rate( - mt, mat.temperature, self._energies, flux) + mt, mat.temperature, self._energies, flux + ) self._results_cache[i_nuc, i_rx] = rate_per_nuc @@ -423,8 +435,12 @@ def factor(self, source_rate): if energy == 0: if comm.rank == 0: sys.stderr.flush() - print("No energy reported from OpenMC tallies. Do your HDF5 " - "files have heating data?\n", file=sys.stderr, flush=True) + print( + "No energy reported from OpenMC tallies. Do your HDF5 " + "files have heating data?\n", + file=sys.stderr, + flush=True, + ) comm.barrier() comm.Abort(1) @@ -463,8 +479,9 @@ def prepare(self, chain_nucs, rate_index): to a corresponding index in the desired fission Q vector. """ - if (self._fission_q_vector is not None - and self._fission_q_vector.shape == (len(rate_index),)): + if self._fission_q_vector is not None and self._fission_q_vector.shape == ( + len(rate_index), + ): return fission_qs = zeros(len(rate_index)) @@ -550,6 +567,7 @@ def prepare(self, *args, **kwargs): def factor(self, source_rate): return source_rate + # ------------------------------------ # Helper for collapsing fission yields # ------------------------------------ @@ -691,8 +709,14 @@ class FissionYieldCutoffHelper(TalliedFissionYieldHelper): :class:`openmc.deplete.CoupledOperator` """ - def __init__(self, chain_nuclides, n_bmats, cutoff=112.0, - thermal_energy=0.0253, fast_energy=500.0e3): + def __init__( + self, + chain_nuclides, + n_bmats, + cutoff=112.0, + thermal_energy=0.0253, + fast_energy=500.0e3, + ): check_type("cutoff", cutoff, Real) check_type("thermal_energy", thermal_energy, Real) check_type("fast_energy", fast_energy, Real) @@ -724,12 +748,14 @@ def __init__(self, chain_nuclides, n_bmats, cutoff=112.0, cutoff_ix = bisect.bisect_left(energies, cutoff) # find closest energy to requested thermal, fast energies if thermal is None: - min_E = min(energies[:cutoff_ix], - key=lambda e: abs(e - thermal_energy)) + min_E = min( + energies[:cutoff_ix], key=lambda e: abs(e - thermal_energy) + ) thermal = yields[min_E] if fast is None: - min_E = min(energies[cutoff_ix:], - key=lambda e: abs(e - fast_energy)) + min_E = min( + energies[cutoff_ix:], key=lambda e: abs(e - fast_energy) + ) fast = yields[min_E] self._thermal_yields[name] = thermal self._fast_yields[name] = fast @@ -755,8 +781,7 @@ def from_operator(cls, operator, **kwargs): FissionYieldCutoffHelper """ - return cls(operator.chain.nuclides, len(operator.burnable_mats), - **kwargs) + return cls(operator.chain.nuclides, len(operator.burnable_mats), **kwargs) def generate_tallies(self, materials, mat_indexes): """Use C API to produce a fission rate tally in burnable materials @@ -777,8 +802,9 @@ def generate_tallies(self, materials, mat_indexes): """ super().generate_tallies(materials, mat_indexes) energy_filter = EnergyFilter([0.0, self._cutoff, self._upper_energy]) - self._fission_rate_tally.filters = ( - self._fission_rate_tally.filters + [energy_filter]) + self._fission_rate_tally.filters = self._fission_rate_tally.filters + [ + energy_filter + ] def unpack(self): """Obtain fast and thermal fission fractions from tally""" @@ -786,7 +812,8 @@ def unpack(self): self.results = None return fission_rates = self._fission_rate_tally.mean.reshape( - self.n_bmats, 2, len(self._tally_nucs)) + self.n_bmats, 2, len(self._tally_nucs) + ) self.results = fission_rates[self._local_indexes] total_fission = self.results.sum(axis=1) nz_mat, nz_nuc = total_fission.nonzero() @@ -825,8 +852,10 @@ def weighted_yields(self, local_mat_index): rates = self.results[local_mat_index] # iterate over thermal then fast yields, prefer __mul__ to __rmul__ for therm_frac, fast_frac, nuc in zip(rates[0], rates[1], self._tally_nucs): - yields[nuc.name] = (self._thermal_yields[nuc.name] * therm_frac - + self._fast_yields[nuc.name] * fast_frac) + yields[nuc.name] = ( + self._thermal_yields[nuc.name] * therm_frac + + self._fast_yields[nuc.name] * fast_frac + ) return yields @property @@ -911,7 +940,7 @@ def generate_tallies(self, materials, mat_indexes): func_filter.set_data((0, self._upper_energy), (0, self._upper_energy)) weighted_tally = Tally() weighted_tally.writable = False - weighted_tally.scores = ['fission'] + weighted_tally.scores = ["fission"] weighted_tally.filters = filters + [func_filter] self._weighted_tally = weighted_tally @@ -946,10 +975,8 @@ def unpack(self): if not self._tally_nucs or self._local_indexes.size == 0: self.results = None return - fission_results = ( - self._fission_rate_tally.mean[self._local_indexes]) - self.results = ( - self._weighted_tally.mean[self._local_indexes]).copy() + fission_results = self._fission_rate_tally.mean[self._local_indexes] + self.results = (self._weighted_tally.mean[self._local_indexes]).copy() nz_mat, nz_nuc = fission_results.nonzero() self.results[nz_mat, nz_nuc] /= fission_results[nz_mat, nz_nuc] @@ -992,11 +1019,12 @@ def weighted_yields(self, local_mat_index): for ix, ene in enumerate(nuc_energies[:-1]): if nuc_energies[ix + 1] > avg_e: break - lower, upper = nuc_energies[ix:ix + 2] + lower, upper = nuc_energies[ix : ix + 2] fast_frac = (avg_e - lower) / (upper - lower) mat_yields[nuc.name] = ( nuc.yield_data[lower] * (1 - fast_frac) - + nuc.yield_data[upper] * fast_frac) + + nuc.yield_data[upper] * fast_frac + ) mat_yields.update(self.constant_yields) return mat_yields diff --git a/openmc/deplete/independent_operator.py b/openmc/deplete/independent_operator.py index 226682ffa53..41c47593bd2 100644 --- a/openmc/deplete/independent_operator.py +++ b/openmc/deplete/independent_operator.py @@ -114,39 +114,45 @@ class IndependentOperator(OpenMCOperator): """ - def __init__(self, - materials, - fluxes, - micros, - chain_file=None, - keff=None, - normalization_mode='fission-q', - fission_q=None, - prev_results=None, - reduce_chain=False, - reduce_chain_level=None, - fission_yield_opts=None): + def __init__( + self, + materials, + fluxes, + micros, + chain_file=None, + keff=None, + normalization_mode="fission-q", + fission_q=None, + prev_results=None, + reduce_chain=False, + reduce_chain_level=None, + fission_yield_opts=None, + ): # Validate micro-xs parameters - check_type('materials', materials, Iterable, openmc.Material) - check_type('micros', micros, Iterable, MicroXS) + check_type("materials", materials, Iterable, openmc.Material) + check_type("micros", micros, Iterable, MicroXS) materials = openmc.Materials(materials) if not (len(fluxes) == len(micros) == len(materials)): - msg = (f'The length of fluxes ({len(fluxes)}) should be equal to ' - f'the length of micros ({len(micros)}) and the length of ' - f'materials ({len(materials)}).') + msg = ( + f"The length of fluxes ({len(fluxes)}) should be equal to " + f"the length of micros ({len(micros)}) and the length of " + f"materials ({len(materials)})." + ) raise ValueError(msg) if keff is not None: - check_type('keff', keff, tuple, float) + check_type("keff", keff, tuple, float) keff = ufloat(*keff) self._keff = keff if fission_yield_opts is None: fission_yield_opts = {} - helper_kwargs = {'normalization_mode': normalization_mode, - 'fission_yield_opts': fission_yield_opts} + helper_kwargs = { + "normalization_mode": normalization_mode, + "fission_yield_opts": fission_yield_opts, + } # Sort fluxes and micros in same order that materials get sorted index_sort = np.argsort([mat.id for mat in materials]) @@ -162,21 +168,26 @@ def __init__(self, fission_q=fission_q, helper_kwargs=helper_kwargs, reduce_chain=reduce_chain, - reduce_chain_level=reduce_chain_level) + reduce_chain_level=reduce_chain_level, + ) @classmethod - def from_nuclides(cls, volume, nuclides, - flux, - micro_xs, - chain_file=None, - nuc_units='atom/b-cm', - keff=None, - normalization_mode='fission-q', - fission_q=None, - prev_results=None, - reduce_chain=False, - reduce_chain_level=None, - fission_yield_opts=None): + def from_nuclides( + cls, + volume, + nuclides, + flux, + micro_xs, + chain_file=None, + nuc_units="atom/b-cm", + keff=None, + normalization_mode="fission-q", + fission_q=None, + prev_results=None, + reduce_chain=False, + reduce_chain_level=None, + fission_yield_opts=None, + ): """ Alternate constructor from a dictionary of nuclide concentrations @@ -224,32 +235,32 @@ def from_nuclides(cls, volume, nuclides, the defaults for the associated helper. """ - check_type('nuclides', nuclides, dict, str) + check_type("nuclides", nuclides, dict, str) materials = cls._consolidate_nuclides_to_material(nuclides, nuc_units, volume) fluxes = [flux] micros = [micro_xs] - return cls(materials, - fluxes, - micros, - chain_file, - keff=keff, - normalization_mode=normalization_mode, - fission_q=fission_q, - prev_results=prev_results, - reduce_chain=reduce_chain, - reduce_chain_level=reduce_chain_level, - fission_yield_opts=fission_yield_opts) + return cls( + materials, + fluxes, + micros, + chain_file, + keff=keff, + normalization_mode=normalization_mode, + fission_q=fission_q, + prev_results=prev_results, + reduce_chain=reduce_chain, + reduce_chain_level=reduce_chain_level, + fission_yield_opts=fission_yield_opts, + ) @staticmethod def _consolidate_nuclides_to_material(nuclides, nuc_units, volume): - """Puts nuclide list into an openmc.Materials object. - - """ + """Puts nuclide list into an openmc.Materials object.""" mat = openmc.Material() - if nuc_units == 'atom/b-cm': + if nuc_units == "atom/b-cm": for nuc, conc in nuclides.items(): mat.add_nuclide(nuc, conc) - elif nuc_units == 'atom/cm3': + elif nuc_units == "atom/cm3": for nuc, conc in nuclides.items(): mat.add_nuclide(nuc, conc * 1e-24) # convert to at/b-cm else: @@ -362,8 +373,8 @@ def _get_helper_classes(self, helper_kwargs): """ - normalization_mode = helper_kwargs['normalization_mode'] - fission_yield_opts = helper_kwargs['fission_yield_opts'] + normalization_mode = helper_kwargs["normalization_mode"] + fission_yield_opts = helper_kwargs["fission_yield_opts"] self._rate_helper = self._IndependentRateHelper(self) if normalization_mode == "fission-q": @@ -373,8 +384,7 @@ def _get_helper_classes(self, helper_kwargs): # Select and create fission yield helper fission_helper = ConstantFissionYieldHelper - self._yield_helper = fission_helper.from_operator( - self, **fission_yield_opts) + self._yield_helper = fission_helper.from_operator(self, **fission_yield_opts) def initial_condition(self): """Performs final setup and returns initial condition. @@ -448,8 +458,9 @@ def _update_materials(self): # negative. CRAM does not guarantee positive # values. if val < -1.0e-21: - print(f'WARNING: nuclide {nuc} in material' - f'{mat} is negative (density = {val}' - - ' atom/b-cm)') + print( + f"WARNING: nuclide {nuc} in material" + f"{mat} is negative (density = {val}" + " atom/b-cm)" + ) number_i[mat, nuc] = 0.0 diff --git a/openmc/deplete/integrators.py b/openmc/deplete/integrators.py index a877c4900f6..6a166c3d0e0 100644 --- a/openmc/deplete/integrators.py +++ b/openmc/deplete/integrators.py @@ -3,14 +3,30 @@ from .abc import Integrator, SIIntegrator, OperatorResult, add_params from ._matrix_funcs import ( - cf4_f1, cf4_f2, cf4_f3, cf4_f4, celi_f1, celi_f2, - leqi_f1, leqi_f2, leqi_f3, leqi_f4, rk4_f1, rk4_f4 + cf4_f1, + cf4_f2, + cf4_f3, + cf4_f4, + celi_f1, + celi_f2, + leqi_f1, + leqi_f2, + leqi_f3, + leqi_f4, + rk4_f1, + rk4_f4, ) __all__ = [ - "PredictorIntegrator", "CECMIntegrator", "CF4Integrator", - "CELIIntegrator", "EPCRK4Integrator", "LEQIIntegrator", - "SICELIIntegrator", "SILEQIIntegrator"] + "PredictorIntegrator", + "CECMIntegrator", + "CF4Integrator", + "CELIIntegrator", + "EPCRK4Integrator", + "LEQIIntegrator", + "SICELIIntegrator", + "SILEQIIntegrator", +] @add_params @@ -24,6 +40,7 @@ class PredictorIntegrator(Integrator): \mathbf{n}_{i+1} = \exp\left(h\mathbf{A}(\mathbf{n}_i) \right) \mathbf{n}_i """ + _num_stages = 1 def __call__(self, n, rates, dt, source_rate, _i=None): @@ -76,6 +93,7 @@ class CECMIntegrator(Integrator): \mathbf{n}_i. \end{aligned} """ + _num_stages = 2 def __call__(self, n, rates, dt, source_rate, _i=None): @@ -140,6 +158,7 @@ class CF4Integrator(Integrator): \frac{\mathbf{A}_3}{6} + \frac{\mathbf{A}_4}{4} \right ) \mathbf{n}_i. \end{aligned} """ + _num_stages = 4 def __call__(self, n_bos, bos_rates, dt, source_rate, _i=None): @@ -171,31 +190,28 @@ def __call__(self, n_bos, bos_rates, dt, source_rate, _i=None): simulations """ # Step 1: deplete with matrix 1/2*A(y0) - time1, n_eos1 = self._timed_deplete( - n_bos, bos_rates, dt, matrix_func=cf4_f1) + time1, n_eos1 = self._timed_deplete(n_bos, bos_rates, dt, matrix_func=cf4_f1) res1 = self.operator(n_eos1, source_rate) # Step 2: deplete with matrix 1/2*A(y1) - time2, n_eos2 = self._timed_deplete( - n_bos, res1.rates, dt, matrix_func=cf4_f1) + time2, n_eos2 = self._timed_deplete(n_bos, res1.rates, dt, matrix_func=cf4_f1) res2 = self.operator(n_eos2, source_rate) # Step 3: deplete with matrix -1/2*A(y0)+A(y2) list_rates = list(zip(bos_rates, res2.rates)) - time3, n_eos3 = self._timed_deplete( - n_eos1, list_rates, dt, matrix_func=cf4_f2) + time3, n_eos3 = self._timed_deplete(n_eos1, list_rates, dt, matrix_func=cf4_f2) res3 = self.operator(n_eos3, source_rate) # Step 4: deplete with two matrix exponentials list_rates = list(zip(bos_rates, res1.rates, res2.rates, res3.rates)) - time4, n_inter = self._timed_deplete( - n_bos, list_rates, dt, matrix_func=cf4_f3) - time5, n_eos5 = self._timed_deplete( - n_inter, list_rates, dt, matrix_func=cf4_f4) + time4, n_inter = self._timed_deplete(n_bos, list_rates, dt, matrix_func=cf4_f3) + time5, n_eos5 = self._timed_deplete(n_inter, list_rates, dt, matrix_func=cf4_f4) - return (time1 + time2 + time3 + time4 + time5, - [n_eos1, n_eos2, n_eos3, n_eos5], - [res1, res2, res3]) + return ( + time1 + time2 + time3 + time4 + time5, + [n_eos1, n_eos2, n_eos3, n_eos5], + [res1, res2, res3], + ) @add_params @@ -218,6 +234,7 @@ class CELIIntegrator(Integrator): \frac{h}{12} \mathbf{A}(\mathbf{n}_{i+1}^p) \right) \mathbf{n}_i. \end{aligned} """ + _num_stages = 2 def __call__(self, n_bos, rates, dt, source_rate, _i=None): @@ -256,10 +273,12 @@ def __call__(self, n_bos, rates, dt, source_rate, _i=None): list_rates = list(zip(rates, res_ce.rates)) time_le1, n_inter = self._timed_deplete( - n_bos, list_rates, dt, matrix_func=celi_f1) + n_bos, list_rates, dt, matrix_func=celi_f1 + ) time_le2, n_end = self._timed_deplete( - n_inter, list_rates, dt, matrix_func=celi_f2) + n_inter, list_rates, dt, matrix_func=celi_f2 + ) return proc_time + time_le1 + time_le1, [n_ce, n_end], [res_ce] @@ -284,6 +303,7 @@ class EPCRK4Integrator(Integrator): + \frac{\mathbf{A}_3}{3} + \frac{\mathbf{A}_4}{6} \right ) \mathbf{n}_i. \end{aligned} """ + _num_stages = 4 def __call__(self, n, rates, dt, source_rate, _i=None): @@ -366,6 +386,7 @@ class LEQIIntegrator(Integrator): It is initialized using the CE/LI algorithm. """ + _num_stages = 2 def __call__(self, n_bos, bos_rates, dt, source_rate, i): @@ -400,7 +421,8 @@ def __call__(self, n_bos, bos_rates, dt, source_rate, i): if self._i_res < 1: # need at least previous transport solution self._prev_rates = bos_rates return CELIIntegrator.__call__( - self, n_bos, bos_rates, dt, source_rate, i) + self, n_bos, bos_rates, dt, source_rate, i + ) prev_res = self.operator.prev_res[-2] prev_dt = self.timesteps[i] - prev_res.time[0] self._prev_rates = prev_res.rates[0] @@ -410,31 +432,32 @@ def __call__(self, n_bos, bos_rates, dt, source_rate, i): # Remaining LE/QI bos_res = self.operator(n_bos, source_rate) - le_inputs = list(zip( - self._prev_rates, bos_res.rates, repeat(prev_dt), repeat(dt))) + le_inputs = list( + zip(self._prev_rates, bos_res.rates, repeat(prev_dt), repeat(dt)) + ) - time1, n_inter = self._timed_deplete( - n_bos, le_inputs, dt, matrix_func=leqi_f1) - time2, n_eos0 = self._timed_deplete( - n_inter, le_inputs, dt, matrix_func=leqi_f2) + time1, n_inter = self._timed_deplete(n_bos, le_inputs, dt, matrix_func=leqi_f1) + time2, n_eos0 = self._timed_deplete(n_inter, le_inputs, dt, matrix_func=leqi_f2) res_inter = self.operator(n_eos0, source_rate) - qi_inputs = list(zip( - self._prev_rates, bos_res.rates, res_inter.rates, - repeat(prev_dt), repeat(dt))) + qi_inputs = list( + zip( + self._prev_rates, + bos_res.rates, + res_inter.rates, + repeat(prev_dt), + repeat(dt), + ) + ) - time3, n_inter = self._timed_deplete( - n_bos, qi_inputs, dt, matrix_func=leqi_f3) - time4, n_eos1 = self._timed_deplete( - n_inter, qi_inputs, dt, matrix_func=leqi_f4) + time3, n_inter = self._timed_deplete(n_bos, qi_inputs, dt, matrix_func=leqi_f3) + time4, n_eos1 = self._timed_deplete(n_inter, qi_inputs, dt, matrix_func=leqi_f4) # store updated rates self._prev_rates = copy.deepcopy(bos_res.rates) - return ( - time1 + time2 + time3 + time4, [n_eos0, n_eos1], - [bos_res, res_inter]) + return (time1 + time2 + time3 + time4, [n_eos0, n_eos1], [bos_res, res_inter]) @add_params @@ -448,6 +471,7 @@ class SICELIIntegrator(SIIntegrator): Detailed algorithm can be found in section 3.2 in `Colin Josey's thesis `_. """ + _num_stages = 2 def __call__(self, n_bos, bos_rates, dt, source_rate, _i=None): @@ -488,15 +512,17 @@ def __call__(self, n_bos, bos_rates, dt, source_rate, _i=None): if j <= 1: res_bar = copy.deepcopy(inter_res) else: - rates = 1/j * inter_res.rates + (1 - 1 / j) * res_bar.rates - k = 1/j * inter_res.k + (1 - 1 / j) * res_bar.k + rates = 1 / j * inter_res.rates + (1 - 1 / j) * res_bar.rates + k = 1 / j * inter_res.k + (1 - 1 / j) * res_bar.k res_bar = OperatorResult(k, rates) list_rates = list(zip(bos_rates, res_bar.rates)) time1, n_inter = self._timed_deplete( - n_bos, list_rates, dt, matrix_func=celi_f1) + n_bos, list_rates, dt, matrix_func=celi_f1 + ) time2, n_inter = self._timed_deplete( - n_inter, list_rates, dt, matrix_func=celi_f2) + n_inter, list_rates, dt, matrix_func=celi_f2 + ) proc_time += time1 + time2 # end iteration @@ -514,6 +540,7 @@ class SILEQIIntegrator(SIIntegrator): Detailed algorithm can be found in Section 3.2 in `Colin Josey's thesis `_. """ + _num_stages = 2 def __call__(self, n_bos, bos_rates, dt, source_rate, i): @@ -549,7 +576,8 @@ def __call__(self, n_bos, bos_rates, dt, source_rate, i): self._prev_rates = bos_rates # Perform CELI for initial steps return SICELIIntegrator.__call__( - self, n_bos, bos_rates, dt, source_rate, i) + self, n_bos, bos_rates, dt, source_rate, i + ) prev_res = self.operator.prev_res[-2] prev_dt = self.timesteps[i] - prev_res.time[0] self._prev_rates = prev_res.rates[0] @@ -557,12 +585,9 @@ def __call__(self, n_bos, bos_rates, dt, source_rate, i): prev_dt = self.timesteps[i - 1] # Perform remaining LE/QI - inputs = list(zip(self._prev_rates, bos_rates, - repeat(prev_dt), repeat(dt))) - proc_time, n_inter = self._timed_deplete( - n_bos, inputs, dt, matrix_func=leqi_f1) - time1, n_eos = self._timed_deplete( - n_inter, inputs, dt, matrix_func=leqi_f2) + inputs = list(zip(self._prev_rates, bos_rates, repeat(prev_dt), repeat(dt))) + proc_time, n_inter = self._timed_deplete(n_bos, inputs, dt, matrix_func=leqi_f1) + time1, n_eos = self._timed_deplete(n_inter, inputs, dt, matrix_func=leqi_f2) proc_time += time1 n_inter = copy.deepcopy(n_eos) @@ -577,24 +602,31 @@ def __call__(self, n_bos, bos_rates, dt, source_rate, i): k = 1 / j * inter_res.k + (1 - 1 / j) * res_bar.k res_bar = OperatorResult(k, rates) - inputs = list(zip(self._prev_rates, bos_rates, res_bar.rates, - repeat(prev_dt), repeat(dt))) - time1, n_inter = self._timed_deplete( - n_bos, inputs, dt, matrix_func=leqi_f3) + inputs = list( + zip( + self._prev_rates, + bos_rates, + res_bar.rates, + repeat(prev_dt), + repeat(dt), + ) + ) + time1, n_inter = self._timed_deplete(n_bos, inputs, dt, matrix_func=leqi_f3) time2, n_inter = self._timed_deplete( - n_inter, inputs, dt, matrix_func=leqi_f4) + n_inter, inputs, dt, matrix_func=leqi_f4 + ) proc_time += time1 + time2 return proc_time, [n_eos, n_inter], [res_bar] integrator_by_name = { - 'cecm': CECMIntegrator, - 'predictor': PredictorIntegrator, - 'cf4': CF4Integrator, - 'epc_rk4': EPCRK4Integrator, - 'si_celi': SICELIIntegrator, - 'si_leqi': SILEQIIntegrator, - 'celi': CELIIntegrator, - 'leqi': LEQIIntegrator + "cecm": CECMIntegrator, + "predictor": PredictorIntegrator, + "cf4": CF4Integrator, + "epc_rk4": EPCRK4Integrator, + "si_celi": SICELIIntegrator, + "si_leqi": SILEQIIntegrator, + "celi": CELIIntegrator, + "leqi": LEQIIntegrator, } diff --git a/openmc/deplete/microxs.py b/openmc/deplete/microxs.py index bbe9b2a4322..2a95aca1694 100644 --- a/openmc/deplete/microxs.py +++ b/openmc/deplete/microxs.py @@ -24,14 +24,14 @@ from openmc.mpi import comm _valid_rxns = list(REACTIONS) -_valid_rxns.append('fission') -_valid_rxns.append('damage-energy') +_valid_rxns.append("fission") +_valid_rxns.append("damage-energy") def _resolve_chain_file_path(chain_file: str | None): if chain_file is None: - chain_file = openmc.config.get('chain_file') - if 'chain_file' not in openmc.config: + chain_file = openmc.config.get("chain_file") + if "chain_file" not in openmc.config: raise DataError( "No depletion chain specified and could not find depletion " "chain in openmc.config['chain_file']" @@ -40,14 +40,14 @@ def _resolve_chain_file_path(chain_file: str | None): def get_microxs_and_flux( - model: openmc.Model, - domains, - nuclides: Iterable[str] | None = None, - reactions: Iterable[str] | None = None, - energies: Iterable[float] | str | None = None, - chain_file: PathLike | None = None, - run_kwargs=None - ) -> tuple[list[np.ndarray], list[MicroXS]]: + model: openmc.Model, + domains, + nuclides: Iterable[str] | None = None, + reactions: Iterable[str] | None = None, + energies: Iterable[float] | str | None = None, + chain_file: PathLike | None = None, + run_kwargs=None, +) -> tuple[list[np.ndarray], list[MicroXS]]: """Generate a microscopic cross sections and flux from a Model .. versionadded:: 0.14.0 @@ -93,8 +93,9 @@ def get_microxs_and_flux( if not nuclides: cross_sections = _find_cross_sections(model) nuclides_with_data = _get_nuclides_with_data(cross_sections) - nuclides = [nuc.name for nuc in chain.nuclides - if nuc.name in nuclides_with_data] + nuclides = [ + nuc.name for nuc in chain.nuclides if nuc.name in nuclides_with_data + ] # Set up the reaction rate and flux tallies if energies is None: @@ -114,15 +115,15 @@ def get_microxs_and_flux( else: raise ValueError(f"Unsupported domain type: {type(domains[0])}") - rr_tally = openmc.Tally(name='MicroXS RR') + rr_tally = openmc.Tally(name="MicroXS RR") rr_tally.filters = [domain_filter, energy_filter] rr_tally.nuclides = nuclides rr_tally.multiply_density = False rr_tally.scores = reactions - flux_tally = openmc.Tally(name='MicroXS flux') + flux_tally = openmc.Tally(name="MicroXS flux") flux_tally.filters = [domain_filter, energy_filter] - flux_tally.scores = ['flux'] + flux_tally.scores = ["flux"] model.tallies = openmc.Tallies([rr_tally, flux_tally]) if openmc.lib.is_initialized: @@ -140,7 +141,7 @@ def get_microxs_and_flux( run_kwargs = {} else: run_kwargs = dict(run_kwargs) - run_kwargs.setdefault('cwd', temp_dir) + run_kwargs.setdefault("cwd", temp_dir) statepoint_path = model.run(**run_kwargs) if comm.rank == 0: @@ -153,15 +154,19 @@ def get_microxs_and_flux( rr_tally = comm.bcast(rr_tally) flux_tally = comm.bcast(flux_tally) # Get reaction rates and flux values - reaction_rates = rr_tally.get_reshaped_data() # (domains, groups, nuclides, reactions) + reaction_rates = ( + rr_tally.get_reshaped_data() + ) # (domains, groups, nuclides, reactions) flux = flux_tally.get_reshaped_data() # (domains, groups, 1, 1) # Make energy groups last dimension - reaction_rates = np.moveaxis(reaction_rates, 1, -1) # (domains, nuclides, reactions, groups) + reaction_rates = np.moveaxis( + reaction_rates, 1, -1 + ) # (domains, nuclides, reactions, groups) flux = np.moveaxis(flux, 1, -1) # (domains, 1, 1, groups) # Divide RR by flux to get microscopic cross sections - xs = np.empty_like(reaction_rates) # (domains, nuclides, reactions, groups) + xs = np.empty_like(reaction_rates) # (domains, nuclides, reactions, groups) d, _, _, g = np.nonzero(flux) xs[d, ..., g] = reaction_rates[d, ..., g] / flux[d, :, :, g] @@ -196,20 +201,22 @@ class MicroXS: :data:`openmc.deplete.chain.REACTIONS` """ + def __init__(self, data: np.ndarray, nuclides: list[str], reactions: list[str]): # Validate inputs if len(data.shape) != 3: - raise ValueError('Data array must be 3D.') + raise ValueError("Data array must be 3D.") if data.shape[:2] != (len(nuclides), len(reactions)): raise ValueError( - f'Nuclides list of length {len(nuclides)} and ' - f'reactions array of length {len(reactions)} do not ' - f'match dimensions of data array of shape {data.shape}') - check_iterable_type('nuclides', nuclides, str) - check_iterable_type('reactions', reactions, str) - check_type('data', data, np.ndarray, expected_iter_type=float) + f"Nuclides list of length {len(nuclides)} and " + f"reactions array of length {len(reactions)} do not " + f"match dimensions of data array of shape {data.shape}" + ) + check_iterable_type("nuclides", nuclides, str) + check_iterable_type("reactions", reactions, str) + check_type("data", data, np.ndarray, expected_iter_type=float) for reaction in reactions: - check_value('reactions', reaction, _valid_rxns) + check_value("reactions", reaction, _valid_rxns) self.data = data self.nuclides = nuclides @@ -269,11 +276,11 @@ def from_multigroup_flux( # if user inputs energies check they are ascending (low to high) as # some depletion codes use high energy to low energy. if not np.all(np.diff(energies) > 0): - raise ValueError('Energy group boundaries must be in ascending order') + raise ValueError("Energy group boundaries must be in ascending order") # check dimension consistency if len(multigroup_flux) != len(energies) - 1: - raise ValueError('Length of flux array should be len(energies)-1') + raise ValueError("Length of flux array should be len(energies)-1") chain_file_path = _resolve_chain_file_path(chain_file) chain = Chain.from_xml(chain_file_path) @@ -312,7 +319,8 @@ def from_multigroup_flux( model = openmc.Model() model.geometry = openmc.Geometry([surf1_cell]) model.settings = openmc.Settings( - particles=1, batches=1, output={'summary': False}) + particles=1, batches=1, output={"summary": False} + ) with change_directory(tmpdir=True): # Export model within temporary directory @@ -350,14 +358,14 @@ def from_csv(cls, csv_file, **kwargs): MicroXS """ - if 'float_precision' not in kwargs: - kwargs['float_precision'] = 'round_trip' + if "float_precision" not in kwargs: + kwargs["float_precision"] = "round_trip" df = pd.read_csv(csv_file, **kwargs) - df.set_index(['nuclides', 'reactions', 'groups'], inplace=True) - nuclides = list(df.index.unique(level='nuclides')) - reactions = list(df.index.unique(level='reactions')) - groups = list(df.index.unique(level='groups')) + df.set_index(["nuclides", "reactions", "groups"], inplace=True) + nuclides = list(df.index.unique(level="nuclides")) + reactions = list(df.index.unique(level="reactions")) + groups = list(df.index.unique(level="groups")) shape = (len(nuclides), len(reactions), len(groups)) data = df.values.reshape(shape) return cls(data, nuclides, reactions) @@ -382,7 +390,7 @@ def to_csv(self, *args, **kwargs): groups = self.data.shape[2] multi_index = pd.MultiIndex.from_product( [self.nuclides, self.reactions, range(1, groups + 1)], - names=['nuclides', 'reactions', 'groups'] + names=["nuclides", "reactions", "groups"], ) - df = pd.DataFrame({'xs': self.data.flatten()}, index=multi_index) + df = pd.DataFrame({"xs": self.data.flatten()}, index=multi_index) df.to_csv(*args, **kwargs) diff --git a/openmc/deplete/nuclide.py b/openmc/deplete/nuclide.py index 60e3e5317f1..61d7382aca0 100644 --- a/openmc/deplete/nuclide.py +++ b/openmc/deplete/nuclide.py @@ -16,11 +16,15 @@ from openmc.stats import Univariate __all__ = [ - "DecayTuple", "ReactionTuple", "Nuclide", "FissionYield", - "FissionYieldDistribution"] + "DecayTuple", + "ReactionTuple", + "Nuclide", + "FissionYield", + "FissionYieldDistribution", +] -DecayTuple = namedtuple('DecayTuple', 'type target branching_ratio') +DecayTuple = namedtuple("DecayTuple", "type target branching_ratio") DecayTuple.__doc__ = """\ Decay mode information @@ -45,7 +49,7 @@ pass -ReactionTuple = namedtuple('ReactionTuple', 'type target Q branching_ratio') +ReactionTuple = namedtuple("ReactionTuple", "type target Q branching_ratio") ReactionTuple.__doc__ = """\ Transmutation reaction information @@ -177,9 +181,7 @@ def add_decay_mode(self, type, target, branching_ratio): Branching ratio of the decay mode """ - self.decay_modes.append( - DecayTuple(type, target, branching_ratio) - ) + self.decay_modes.append(DecayTuple(type, target, branching_ratio)) def add_reaction(self, type, target, Q, branching_ratio): """Add transmutation reaction to the nuclide @@ -199,9 +201,7 @@ def add_reaction(self, type, target, Q, branching_ratio): Branching ratio of the reaction """ - self.reactions.append( - ReactionTuple(type, target, Q, branching_ratio) - ) + self.reactions.append(ReactionTuple(type, target, Q, branching_ratio)) @classmethod def from_xml(cls, element, root=None, fission_q=None): @@ -225,38 +225,38 @@ def from_xml(cls, element, root=None, fission_q=None): """ nuc = cls() - nuc.name = element.get('name') + nuc.name = element.get("name") # Check for half-life - if 'half_life' in element.attrib: - nuc.half_life = float(element.get('half_life')) - nuc.decay_energy = float(element.get('decay_energy', '0')) + if "half_life" in element.attrib: + nuc.half_life = float(element.get("half_life")) + nuc.decay_energy = float(element.get("decay_energy", "0")) # Check for decay paths - for decay_elem in element.iter('decay'): - d_type = decay_elem.get('type') - target = decay_elem.get('target') + for decay_elem in element.iter("decay"): + d_type = decay_elem.get("type") + target = decay_elem.get("target") if target is not None and target.lower() == "nothing": target = None - branching_ratio = float(decay_elem.get('branching_ratio')) + branching_ratio = float(decay_elem.get("branching_ratio")) nuc.decay_modes.append(DecayTuple(d_type, target, branching_ratio)) # Check for sources - for src_elem in element.iter('source'): - particle = src_elem.get('particle') + for src_elem in element.iter("source"): + particle = src_elem.get("particle") distribution = Univariate.from_xml_element(src_elem) nuc.sources[particle] = distribution # Check for reaction paths - for reaction_elem in element.iter('reaction'): - r_type = reaction_elem.get('type') - Q = float(reaction_elem.get('Q', '0')) - branching_ratio = float(reaction_elem.get('branching_ratio', '1')) + for reaction_elem in element.iter("reaction"): + r_type = reaction_elem.get("type") + Q = float(reaction_elem.get("Q", "0")) + branching_ratio = float(reaction_elem.get("branching_ratio", "1")) # If the type is not fission, get target and Q value, otherwise # just set null values - if r_type != 'fission': - target = reaction_elem.get('target') + if r_type != "fission": + target = reaction_elem.get("target") if target is not None and target.lower() == "nothing": target = None else: @@ -265,13 +265,12 @@ def from_xml(cls, element, root=None, fission_q=None): Q = fission_q # Append reaction - nuc.reactions.append(ReactionTuple( - r_type, target, Q, branching_ratio)) + nuc.reactions.append(ReactionTuple(r_type, target, Q, branching_ratio)) - fpy_elem = element.find('neutron_fission_yields') + fpy_elem = element.find("neutron_fission_yields") if fpy_elem is not None: # Check for use of FPY from other nuclide - parent = fpy_elem.get('parent') + parent = fpy_elem.get("parent") if parent is not None: assert root is not None fpy_elem = root.find( @@ -282,7 +281,8 @@ def from_xml(cls, element, root=None, fission_q=None): "Fission product yields for {0} borrow from {1}, but {1} is" " not present in the chain file or has no yields.".format( nuc.name, parent - )) + ) + ) nuc._fpy = parent nuc.yield_data = FissionYieldDistribution.from_xml_element(fpy_elem) @@ -298,46 +298,46 @@ def to_xml_element(self): XML element to write nuclide data to """ - elem = ET.Element('nuclide') - elem.set('name', self.name) + elem = ET.Element("nuclide") + elem.set("name", self.name) if self.half_life is not None: - elem.set('half_life', str(self.half_life)) - elem.set('decay_modes', str(len(self.decay_modes))) - elem.set('decay_energy', str(self.decay_energy)) + elem.set("half_life", str(self.half_life)) + elem.set("decay_modes", str(len(self.decay_modes))) + elem.set("decay_energy", str(self.decay_energy)) for mode_type, daughter, br in self.decay_modes: - mode_elem = ET.SubElement(elem, 'decay') - mode_elem.set('type', mode_type) + mode_elem = ET.SubElement(elem, "decay") + mode_elem.set("type", mode_type) if daughter: - mode_elem.set('target', daughter) - mode_elem.set('branching_ratio', str(br)) + mode_elem.set("target", daughter) + mode_elem.set("branching_ratio", str(br)) # Write decay sources if self.sources: for particle, source in self.sources.items(): - src_elem = source.to_xml_element('source') - src_elem.set('particle', particle) + src_elem = source.to_xml_element("source") + src_elem.set("particle", particle) elem.append(src_elem) - elem.set('reactions', str(len(self.reactions))) + elem.set("reactions", str(len(self.reactions))) for rx, daughter, Q, br in self.reactions: - rx_elem = ET.SubElement(elem, 'reaction') - rx_elem.set('type', rx) - rx_elem.set('Q', str(Q)) + rx_elem = ET.SubElement(elem, "reaction") + rx_elem.set("type", rx) + rx_elem.set("Q", str(Q)) if daughter is not None: - rx_elem.set('target', daughter) + rx_elem.set("target", daughter) if br != 1.0: - rx_elem.set('branching_ratio', str(br)) + rx_elem.set("branching_ratio", str(br)) if self.yield_data: - fpy_elem = ET.SubElement(elem, 'neutron_fission_yields') + fpy_elem = ET.SubElement(elem, "neutron_fission_yields") - if hasattr(self, '_fpy'): + if hasattr(self, "_fpy"): # Check for link to other nuclide data - fpy_elem.set('parent', self._fpy) + fpy_elem.set("parent", self._fpy) else: - energy_elem = ET.SubElement(fpy_elem, 'energies') - energy_elem.text = ' '.join(str(E) for E in self.yield_energies) + energy_elem = ET.SubElement(fpy_elem, "energies") + energy_elem.text = " ".join(str(E) for E in self.yield_energies) self.yield_data.to_xml_element(fpy_elem) return elem @@ -383,8 +383,10 @@ def validate(self, strict=True, quiet=False, tolerance=1e-4): openmc.deplete.Chain.validate """ - msg_func = ("Nuclide {name} has {prop} that sum to {actual} " - "instead of {expected} +/- {tol:7.4e}").format + msg_func = ( + "Nuclide {name} has {prop} that sum to {actual} " + "instead of {expected} +/- {tol:7.4e}" + ).format valid = True # check decay modes @@ -393,8 +395,12 @@ def validate(self, strict=True, quiet=False, tolerance=1e-4): stat = 1.0 - tolerance <= sum_br <= 1.0 + tolerance if not stat: msg = msg_func( - name=self.name, actual=sum_br, expected=1.0, tol=tolerance, - prop="decay mode branch ratios") + name=self.name, + actual=sum_br, + expected=1.0, + tol=tolerance, + prop="decay mode branch ratios", + ) if strict: raise ValueError(msg) elif quiet: @@ -412,8 +418,12 @@ def validate(self, strict=True, quiet=False, tolerance=1e-4): if stat: continue msg = msg_func( - name=self.name, actual=sum_br, expected=1.0, tol=tolerance, - prop=f"{rxn_type} reaction branch ratios") + name=self.name, + actual=sum_br, + expected=1.0, + tol=tolerance, + prop=f"{rxn_type} reaction branch ratios", + ) if strict: raise ValueError(msg) elif quiet: @@ -428,9 +438,12 @@ def validate(self, strict=True, quiet=False, tolerance=1e-4): if stat: continue msg = msg_func( - name=self.name, actual=sum_yield, - expected=2.0, tol=tolerance, - prop=f"fission yields (E = {energy:7.4e} eV)") + name=self.name, + actual=sum_yield, + expected=2.0, + tol=tolerance, + prop=f"fission yields (E = {energy:7.4e} eV)", + ) if strict: raise ValueError(msg) elif quiet: @@ -504,15 +517,16 @@ def __getitem__(self, energy): if energy not in self.energies: raise KeyError(energy) return FissionYield( - self.products, self.yield_matrix[self.energies.index(energy)]) + self.products, self.yield_matrix[self.energies.index(energy)] + ) def __iter__(self): return iter(self.energies) def __repr__(self): return "<{} with {} products at {} energies>".format( - self.__class__.__name__, self.yield_matrix.shape[1], - len(self.energies)) + self.__class__.__name__, self.yield_matrix.shape[1], len(self.energies) + ) @classmethod def from_xml_element(cls, element): diff --git a/openmc/deplete/openmc_operator.py b/openmc/deplete/openmc_operator.py index 049e0dd37ee..5e7bc47123c 100644 --- a/openmc/deplete/openmc_operator.py +++ b/openmc/deplete/openmc_operator.py @@ -98,21 +98,22 @@ class OpenMCOperator(TransportOperator): """ def __init__( - self, - materials=None, - cross_sections=None, - chain_file=None, - prev_results=None, - diff_burnable_mats=False, - diff_volume_method='divide equally', - fission_q=None, - helper_kwargs=None, - reduce_chain=False, - reduce_chain_level=None): + self, + materials=None, + cross_sections=None, + chain_file=None, + prev_results=None, + diff_burnable_mats=False, + diff_volume_method="divide equally", + fission_q=None, + helper_kwargs=None, + reduce_chain=False, + reduce_chain_level=None, + ): # If chain file was not specified, try to get it from global config if chain_file is None: - chain_file = openmc.config.get('chain_file') + chain_file = openmc.config.get("chain_file") if chain_file is None: raise DataError( "No depletion chain specified and could not find depletion " @@ -124,8 +125,9 @@ def __init__( self.materials = materials self.cross_sections = cross_sections - check_value('diff volume method', diff_volume_method, - {'divide equally', 'match cell'}) + check_value( + "diff volume method", diff_volume_method, {"divide equally", "match cell"} + ) self.diff_volume_method = diff_volume_method # Reduce the chain to only those nuclides present @@ -146,35 +148,39 @@ def __init__( # Determine which nuclides have cross section data # This nuclides variables contains every nuclides # for which there is an entry in the micro_xs parameter - self.nuclides_with_data = self._get_nuclides_with_data( - self.cross_sections) + self.nuclides_with_data = self._get_nuclides_with_data(self.cross_sections) # Select nuclides with data that are also in the chain - self._burnable_nucs = [nuc.name for nuc in self.chain.nuclides - if nuc.name in self.nuclides_with_data] + self._burnable_nucs = [ + nuc.name + for nuc in self.chain.nuclides + if nuc.name in self.nuclides_with_data + ] # Select nuclides without data that are also in the chain - self._decay_nucs = [nuc.name for nuc in self.chain.nuclides - if nuc.name not in self.nuclides_with_data] + self._decay_nucs = [ + nuc.name + for nuc in self.chain.nuclides + if nuc.name not in self.nuclides_with_data + ] self.burnable_mats, volumes, all_nuclides = self._get_burnable_mats() self.local_mats = _distribute(self.burnable_mats) self._mat_index_map = { - lm: self.burnable_mats.index(lm) for lm in self.local_mats} + lm: self.burnable_mats.index(lm) for lm in self.local_mats + } if self.prev_res is not None: self._load_previous_results() # Extract number densities from the geometry / previous depletion run - self._extract_number(self.local_mats, - volumes, - all_nuclides, - self.prev_res) + self._extract_number(self.local_mats, volumes, all_nuclides, self.prev_res) # Create reaction rates array self.reaction_rates = ReactionRates( - self.local_mats, self._burnable_nucs, self.chain.reactions) + self.local_mats, self._burnable_nucs, self.chain.reactions + ) self._get_helper_classes(helper_kwargs) @@ -208,27 +214,32 @@ def _get_burnable_mats(self) -> tuple[list[str], dict[str, float], list[str]]: if nuclide in self.nuclides_with_data or self._decay_nucs: model_nuclides.add(nuclide) else: - msg = (f"Nuclide {nuclide} in material {mat.id} is not " - "present in the depletion chain and has no cross " - "section data.") + msg = ( + f"Nuclide {nuclide} in material {mat.id} is not " + "present in the depletion chain and has no cross " + "section data." + ) warn(msg) if mat.depletable: burnable_mats.add(str(mat.id)) if mat.volume is None: if mat.name is None: - msg = ("Volume not specified for depletable material " - f"with ID={mat.id}.") + msg = ( + "Volume not specified for depletable material " + f"with ID={mat.id}." + ) else: - msg = ("Volume not specified for depletable material " - f"with ID={mat.id} Name={mat.name}.") + msg = ( + "Volume not specified for depletable material " + f"with ID={mat.id} Name={mat.name}." + ) raise RuntimeError(msg) volume[str(mat.id)] = mat.volume self.heavy_metal += mat.fissionable_mass # Make sure there are burnable materials if not burnable_mats: - raise RuntimeError( - "No depletable materials were found in the model.") + raise RuntimeError("No depletable materials were found in the model.") # Sort the sets burnable_mats = sorted(burnable_mats, key=int) @@ -370,11 +381,13 @@ def initial_condition(self, materials): self._rate_helper.generate_tallies(materials, self.chain.reactions) self._normalization_helper.prepare( - self.chain.nuclides, self.reaction_rates.index_nuc) + self.chain.nuclides, self.reaction_rates.index_nuc + ) # Tell fission yield helper what materials this process is # responsible for self._yield_helper.generate_tallies( - materials, tuple(sorted(self._mat_index_map.values()))) + materials, tuple(sorted(self._mat_index_map.values())) + ) # Return number density vector return list(self.number.get_mat_slice(np.s_[:])) @@ -451,8 +464,7 @@ def _get_reaction_nuclides(self): if comm.rank == 0: # Sort nuclides in the same order as self.number - nuc_list = [nuc for nuc in self.number.nuclides - if nuc in nuc_set] + nuc_list = [nuc for nuc in self.number.nuclides if nuc in nuc_set] else: nuc_list = None @@ -520,7 +532,8 @@ def _calculate_reaction_rates(self, source_rate): # Get microscopic reaction rates in [(reactions/src)*b-cm/atom]. 2D # array with shape (nuclides, reactions). tally_rates = self._rate_helper.get_material_rates( - mat_index, nuc_ind, rx_ind) + mat_index, nuc_ind, rx_ind + ) # Compute fission yields for this material fission_yields.append(self._yield_helper.weighted_yields(i)) diff --git a/openmc/deplete/pool.py b/openmc/deplete/pool.py index 27ecaa4dd8b..45fd7bd8a15 100644 --- a/openmc/deplete/pool.py +++ b/openmc/deplete/pool.py @@ -2,6 +2,7 @@ Provided to avoid some circular imports """ + from itertools import repeat, starmap from multiprocessing import Pool @@ -18,6 +19,7 @@ # calculations NUM_PROCESSES = None + def _distribute(items): """Distribute items across MPI communicator @@ -37,11 +39,13 @@ def _distribute(items): for i in range(comm.size): chunk_size = min_size + int(i < extra) if comm.rank == i: - return items[j:j + chunk_size] + return items[j : j + chunk_size] j += chunk_size -def deplete(func, chain, n, rates, dt, matrix_func=None, transfer_rates=None, - *matrix_args): + +def deplete( + func, chain, n, rates, dt, matrix_func=None, transfer_rates=None, *matrix_args +): """Deplete materials using given reaction rates for a specified time Parameters @@ -84,22 +88,23 @@ def deplete(func, chain, n, rates, dt, matrix_func=None, transfer_rates=None, elif len(fission_yields) != len(n): raise ValueError( "Number of material fission yield distributions {} is not " - "equal to the number of compositions {}".format( - len(fission_yields), len(n))) + "equal to the number of compositions {}".format(len(fission_yields), len(n)) + ) if matrix_func is None: matrices = map(chain.form_matrix, rates, fission_yields) else: - matrices = map(matrix_func, repeat(chain), rates, fission_yields, - *matrix_args) + matrices = map(matrix_func, repeat(chain), rates, fission_yields, *matrix_args) if transfer_rates is not None: # Calculate transfer rate terms as diagonal matrices - transfers = map(chain.form_rr_term, repeat(transfer_rates), - transfer_rates.local_mats) + transfers = map( + chain.form_rr_term, repeat(transfer_rates), transfer_rates.local_mats + ) # Subtract transfer rate terms from Bateman matrices - matrices = [matrix - transfer for (matrix, transfer) in zip(matrices, - transfers)] + matrices = [ + matrix - transfer for (matrix, transfer) in zip(matrices, transfers) + ] if len(transfer_rates.index_transfer) > 0: # Gather all on comm.rank 0 @@ -109,7 +114,7 @@ def deplete(func, chain, n, rates, dt, matrix_func=None, transfer_rates=None, if comm.rank == 0: # Expand lists matrices = [elm for matrix in matrices for elm in matrix] - n = [n_elm for n_mat in n for n_elm in n_mat] + n = [n_elm for n_mat in n for n_elm in n_mat] # Calculate transfer rate terms as diagonal matrices transfer_pair = { @@ -124,8 +129,10 @@ def deplete(func, chain, n, rates, dt, matrix_func=None, transfer_rates=None, for row in range(n_rows): cols = [] for col in range(n_cols): - mat_pair = (transfer_rates.burnable_mats[row], - transfer_rates.burnable_mats[col]) + mat_pair = ( + transfer_rates.burnable_mats[row], + transfer_rates.burnable_mats[col], + ) if row == col: # Fill the diagonals with the Bateman matrices cols.append(matrices[row]) diff --git a/openmc/deplete/reaction_rates.py b/openmc/deplete/reaction_rates.py index 714d9048b47..b2781afb859 100644 --- a/openmc/deplete/reaction_rates.py +++ b/openmc/deplete/reaction_rates.py @@ -80,9 +80,9 @@ def __new__(cls, local_mats, nuclides, reactions, from_results=False): def __array_finalize__(self, obj): if obj is None: return - self.index_mat = getattr(obj, 'index_mat', None) - self.index_nuc = getattr(obj, 'index_nuc', None) - self.index_rx = getattr(obj, 'index_rx', None) + self.index_mat = getattr(obj, "index_mat", None) + self.index_nuc = getattr(obj, "index_nuc", None) + self.index_rx = getattr(obj, "index_rx", None) # Reaction rates are distributed to other processes via multiprocessing, # which entails pickling the objects. In order to preserve the custom diff --git a/openmc/deplete/results.py b/openmc/deplete/results.py index f897a88422c..787742bac8c 100644 --- a/openmc/deplete/results.py +++ b/openmc/deplete/results.py @@ -58,11 +58,12 @@ class Results(list): Path to depletion result file """ - def __init__(self, filename='depletion_results.h5'): + + def __init__(self, filename="depletion_results.h5"): data = [] if filename is not None: with h5py.File(str(filename), "r") as fh: - cv.check_filetype_version(fh, 'depletion results', VERSION_RESULTS[0]) + cv.check_filetype_version(fh, "depletion results", VERSION_RESULTS[0]) # Get number of results stored n = fh["number"][...].shape[0] @@ -71,7 +72,6 @@ def __init__(self, filename='depletion_results.h5'): data.append(StepResult.from_hdf5(fh, i)) super().__init__(data) - @classmethod def from_hdf5(cls, filename: PathLike): """Load in depletion results from a previous file @@ -90,7 +90,7 @@ def from_hdf5(cls, filename: PathLike): warn( "The ResultsList.from_hdf5(...) method is no longer necessary and will " "be removed in a future version of OpenMC. Use Results(...) instead.", - FutureWarning + FutureWarning, ) return cls(filename) @@ -99,7 +99,7 @@ def get_activity( mat: Material | str, units: str = "Bq/cm3", by_nuclide: bool = False, - volume: float | None = None + volume: float | None = None, ) -> tuple[np.ndarray, np.ndarray | list[dict]]: """Get activity of material over time. @@ -134,7 +134,7 @@ def get_activity( elif isinstance(mat, str): mat_id = mat else: - raise TypeError('mat should be of type openmc.Material or str') + raise TypeError("mat should be of type openmc.Material or str") times = np.empty_like(self, dtype=float) if by_nuclide: @@ -145,7 +145,9 @@ def get_activity( # Evaluate activity for each depletion time for i, result in enumerate(self): times[i] = result.time[0] - activities[i] = result.get_material(mat_id).get_activity(units, by_nuclide, volume) + activities[i] = result.get_material(mat_id).get_activity( + units, by_nuclide, volume + ) return times, activities @@ -154,7 +156,7 @@ def get_atoms( mat: Material | str, nuc: str, nuc_units: str = "atoms", - time_units: str = "s" + time_units: str = "s", ) -> tuple[np.ndarray, np.ndarray]: """Get number of nuclides over time from a single material @@ -184,15 +186,14 @@ def get_atoms( """ cv.check_value("time_units", time_units, {"s", "d", "min", "h", "a"}) - cv.check_value("nuc_units", nuc_units, - {"atoms", "atom/b-cm", "atom/cm3"}) + cv.check_value("nuc_units", nuc_units, {"atoms", "atom/b-cm", "atom/cm3"}) if isinstance(mat, Material): mat_id = str(mat.id) elif isinstance(mat, str): mat_id = mat else: - raise TypeError('mat should be of type openmc.Material or str') + raise TypeError("mat should be of type openmc.Material or str") times = np.empty_like(self, dtype=float) concentrations = np.empty_like(self, dtype=float) @@ -213,11 +214,11 @@ def get_atoms( return times, concentrations def get_decay_heat( - self, - mat: Material | str, - units: str = "W", - by_nuclide: bool = False, - volume: float | None = None + self, + mat: Material | str, + units: str = "W", + by_nuclide: bool = False, + volume: float | None = None, ) -> tuple[np.ndarray, np.ndarray | list[dict]]: """Get decay heat of material over time. @@ -252,7 +253,7 @@ def get_decay_heat( elif isinstance(mat, str): mat_id = mat else: - raise TypeError('mat should be of type openmc.Material or str') + raise TypeError("mat should be of type openmc.Material or str") times = np.empty_like(self, dtype=float) if by_nuclide: @@ -264,15 +265,17 @@ def get_decay_heat( for i, result in enumerate(self): times[i] = result.time[0] decay_heat[i] = result.get_material(mat_id).get_decay_heat( - units, by_nuclide, volume) + units, by_nuclide, volume + ) return times, decay_heat - def get_mass(self, + def get_mass( + self, mat: Material | str, nuc: str, mass_units: str = "g", - time_units: str = "s" + time_units: str = "s", ) -> tuple[np.ndarray, np.ndarray]: """Get mass of nuclides over time from a single material @@ -306,7 +309,7 @@ def get_mass(self, elif isinstance(mat, str): mat_id = mat else: - raise TypeError('mat should be of type openmc.Material or str') + raise TypeError("mat should be of type openmc.Material or str") times, atoms = self.get_atoms(mat, nuc, time_units=time_units) @@ -322,10 +325,7 @@ def get_mass(self, return times, mass def get_reaction_rate( - self, - mat: Material | str, - nuc: str, - rx: str + self, mat: Material | str, nuc: str, rx: str ) -> tuple[np.ndarray, np.ndarray]: """Get reaction rate in a single material/nuclide over time @@ -354,7 +354,7 @@ def get_reaction_rate( elif isinstance(mat, str): mat_id = mat else: - raise TypeError('mat should be of type openmc.Material or str') + raise TypeError("mat should be of type openmc.Material or str") # Evaluate value in each region for i, result in enumerate(self): @@ -363,7 +363,7 @@ def get_reaction_rate( return times, rates - def get_keff(self, time_units: str = 's') -> tuple[np.ndarray, np.ndarray]: + def get_keff(self, time_units: str = "s") -> tuple[np.ndarray, np.ndarray]: """Evaluates the eigenvalue from a results list. .. versionadded:: 0.13.1 @@ -399,9 +399,12 @@ def get_keff(self, time_units: str = 's') -> tuple[np.ndarray, np.ndarray]: times = _get_time_as(times, time_units) return times, eigenvalues - def get_eigenvalue(self, time_units: str = 's') -> tuple[np.ndarray, np.ndarray]: - warn("The get_eigenvalue(...) function has been renamed get_keff and " - "will be removed in a future version of OpenMC.", FutureWarning) + def get_eigenvalue(self, time_units: str = "s") -> tuple[np.ndarray, np.ndarray]: + warn( + "The get_eigenvalue(...) function has been renamed get_keff and " + "will be removed in a future version of OpenMC.", + FutureWarning, + ) return self.get_keff(time_units) def get_depletion_time(self) -> np.ndarray: @@ -526,7 +529,7 @@ def export_to_materials( self, burnup_index: int, nuc_with_data: Iterable[str] | None = None, - path: PathLike = 'materials.xml' + path: PathLike = "materials.xml", ) -> Materials: """Return openmc.Materials object based on results at a given step @@ -570,7 +573,7 @@ def export_to_materials( # in the materials.xml file if provided, then finally from # openmc.config['cross_sections']. if nuc_with_data: - cv.check_iterable_type('nuclide names', nuc_with_data, str) + cv.check_iterable_type("nuclide names", nuc_with_data, str) available_cross_sections = nuc_with_data else: # select cross_sections.xml file to use @@ -582,10 +585,10 @@ def export_to_materials( # 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 lib["type"] == "neutron": + available_cross_sections.update(lib["materials"]) if not available_cross_sections: - raise DataError('No neutron libraries found in cross_sections.xml') + 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. @@ -599,7 +602,7 @@ def export_to_materials( for nuc, value in atoms_per_barn_cm.items(): mat.remove_nuclide(nuc) mat.add_nuclide(nuc, value) - mat.set_density('sum') + mat.set_density("sum") # For nuclides in chain that have cross sections, replace # density in original material with new density from results @@ -609,7 +612,7 @@ def export_to_materials( 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.remove_nuclide(nuc) # Replace if it's there mat.add_nuclide(nuc, atoms_per_barn_cm) return mat_file diff --git a/openmc/deplete/stepresult.py b/openmc/deplete/stepresult.py index 9cf33898f3b..d44acec9aaf 100644 --- a/openmc/deplete/stepresult.py +++ b/openmc/deplete/stepresult.py @@ -61,6 +61,7 @@ class StepResult: materials and processes """ + def __init__(self): self.k = None self.time = None @@ -191,8 +192,14 @@ def distribute(self, local_materials, ranges): new.index_mat = {mat: idx for (idx, mat) in enumerate(local_materials)} # Direct transfer - direct_attrs = ("time", "k", "source_rate", "index_nuc", - "mat_to_hdf5_ind", "proc_time") + direct_attrs = ( + "time", + "k", + "source_rate", + "index_nuc", + "mat_to_hdf5_ind", + "proc_time", + ) for attr in direct_attrs: setattr(new, attr, getattr(self, attr)) # Get applicable slice of data @@ -222,14 +229,14 @@ def get_material(self, mat_id): """ with warnings.catch_warnings(): - warnings.simplefilter('ignore', openmc.IDWarning) + warnings.simplefilter("ignore", openmc.IDWarning) material = openmc.Material(material_id=int(mat_id)) try: vol = self.volume[mat_id] except KeyError as e: raise KeyError( - f'mat_id {mat_id} not found in StepResult. Available mat_id ' - f'values are {list(self.volume.keys())}' + f"mat_id {mat_id} not found in StepResult. Available mat_id " + f"values are {list(self.volume.keys())}" ) from e for nuc, _ in sorted(self.index_nuc.items(), key=lambda x: x[1]): atoms = self[0, mat_id, nuc] @@ -252,12 +259,12 @@ def export_to_hdf5(self, filename, step): """ # Write new file if first time step, else add to existing file - kwargs = {'mode': "w" if step == 0 else "a"} + kwargs = {"mode": "w" if step == 0 else "a"} if h5py.get_config().mpi and comm.size > 1: # Write results in parallel - kwargs['driver'] = 'mpio' - kwargs['comm'] = comm + kwargs["driver"] = "mpio" + kwargs["comm"] = comm with h5py.File(filename, **kwargs) as handle: self._to_hdf5(handle, step, parallel=True) else: @@ -290,8 +297,8 @@ def _write_hdf5_metadata(self, handle): # Store concentration mat and nuclide dictionaries (along with volumes) - handle.attrs['version'] = np.array(VERSION_RESULTS) - handle.attrs['filetype'] = np.bytes_('depletion results') + handle.attrs["version"] = np.array(VERSION_RESULTS) + handle.attrs["filetype"] = np.bytes_("depletion results") mat_list = sorted(self.mat_to_hdf5_ind, key=int) nuc_list = sorted(self.index_nuc) @@ -316,7 +323,9 @@ def _write_hdf5_metadata(self, handle): nuc_single_group = nuc_group.create_group(nuc) nuc_single_group.attrs["atom number index"] = self.index_nuc[nuc] if nuc in self.rates[0].index_nuc: - nuc_single_group.attrs["reaction rate index"] = self.rates[0].index_nuc[nuc] + nuc_single_group.attrs["reaction rate index"] = self.rates[0].index_nuc[ + nuc + ] rxn_group = handle.create_group("reactions") @@ -326,28 +335,37 @@ def _write_hdf5_metadata(self, handle): # Construct array storage - handle.create_dataset("number", (1, n_stages, n_mats, n_nuc_number), - maxshape=(None, n_stages, n_mats, n_nuc_number), - chunks=(1, 1, n_mats, n_nuc_number), - dtype='float64') + handle.create_dataset( + "number", + (1, n_stages, n_mats, n_nuc_number), + maxshape=(None, n_stages, n_mats, n_nuc_number), + chunks=(1, 1, n_mats, n_nuc_number), + dtype="float64", + ) if n_nuc_rxn > 0 and n_rxn > 0: - handle.create_dataset("reaction rates", (1, n_stages, n_mats, n_nuc_rxn, n_rxn), - maxshape=(None, n_stages, n_mats, n_nuc_rxn, n_rxn), - chunks=(1, 1, n_mats, n_nuc_rxn, n_rxn), - dtype='float64') - - handle.create_dataset("eigenvalues", (1, n_stages, 2), - maxshape=(None, n_stages, 2), dtype='float64') + handle.create_dataset( + "reaction rates", + (1, n_stages, n_mats, n_nuc_rxn, n_rxn), + maxshape=(None, n_stages, n_mats, n_nuc_rxn, n_rxn), + chunks=(1, 1, n_mats, n_nuc_rxn, n_rxn), + dtype="float64", + ) - handle.create_dataset("time", (1, 2), maxshape=(None, 2), dtype='float64') + handle.create_dataset( + "eigenvalues", + (1, n_stages, 2), + maxshape=(None, n_stages, 2), + dtype="float64", + ) - handle.create_dataset("source_rate", (1, n_stages), maxshape=(None, n_stages), - dtype='float64') + handle.create_dataset("time", (1, 2), maxshape=(None, 2), dtype="float64") handle.create_dataset( - "depletion time", (1,), maxshape=(None,), - dtype="float64") + "source_rate", (1, n_stages), maxshape=(None, n_stages), dtype="float64" + ) + + handle.create_dataset("depletion time", (1,), maxshape=(None,), dtype="float64") def _to_hdf5(self, handle, index, parallel=False): """Converts results object into an hdf5 object. @@ -372,7 +390,7 @@ def _to_hdf5(self, handle, index, parallel=False): # Grab handles number_dset = handle["/number"] - has_reactions = ("reaction rates" in handle) + has_reactions = "reaction rates" in handle if has_reactions: rxn_dset = handle["/reaction rates"] eigenvalues_dset = handle["/eigenvalues"] @@ -423,18 +441,16 @@ def _to_hdf5(self, handle, index, parallel=False): low = min(inds) high = max(inds) for i in range(n_stages): - number_dset[index, i, low:high+1] = self.data[i] + number_dset[index, i, low : high + 1] = self.data[i] if has_reactions: - rxn_dset[index, i, low:high+1] = self.rates[i] + rxn_dset[index, i, low : high + 1] = self.rates[i] if comm.rank == 0: eigenvalues_dset[index, i] = self.k[i] if comm.rank == 0: time_dset[index] = self.time source_rate_dset[index] = self.source_rate if self.proc_time is not None: - proc_time_dset[index] = ( - self.proc_time / (comm.size * self.n_hdf5_mats) - ) + proc_time_dset[index] = self.proc_time / (comm.size * self.n_hdf5_mats) @classmethod def from_hdf5(cls, handle, step): @@ -508,8 +524,16 @@ def from_hdf5(cls, handle, step): return results @staticmethod - def save(op, x, op_results, t, source_rate, step_ind, proc_time=None, - path: PathLike = "depletion_results.h5"): + def save( + op, + x, + op_results, + t, + source_rate, + step_ind, + proc_time=None, + path: PathLike = "depletion_results.h5", + ): """Creates and writes depletion results to disk Parameters @@ -580,9 +604,7 @@ def transfer_volumes(self, model): """ if not model.materials: - materials = openmc.Materials( - model.geometry.get_all_materials().values() - ) + materials = openmc.Materials(model.geometry.get_all_materials().values()) else: materials = model.materials diff --git a/openmc/deplete/transfer_rates.py b/openmc/deplete/transfer_rates.py index 01c9b2e3534..cd55f0c81ee 100644 --- a/openmc/deplete/transfer_rates.py +++ b/openmc/deplete/transfer_rates.py @@ -46,7 +46,7 @@ def __init__(self, operator, model): self.burnable_mats = operator.burnable_mats self.local_mats = operator.local_mats - #initialize transfer rates container dict + # initialize transfer rates container dict self.transfer_rates = {mat: {} for mat in self.burnable_mats} self.index_transfer = set() @@ -63,19 +63,22 @@ def _get_material_id(self, val): """ if isinstance(val, Material): - check_value('Depeletable Material', str(val.id), self.burnable_mats) + check_value("Depeletable Material", str(val.id), self.burnable_mats) val = val.id elif isinstance(val, str): if val.isnumeric(): - check_value('Material ID', str(val), self.burnable_mats) + check_value("Material ID", str(val), self.burnable_mats) else: - check_value('Material name', val, - [mat.name for mat in self.materials if mat.depletable]) + check_value( + "Material name", + val, + [mat.name for mat in self.materials if mat.depletable], + ) val = [mat.id for mat in self.materials if mat.name == val][0] elif isinstance(val, int): - check_value('Material ID', str(val), self.burnable_mats) + check_value("Material ID", str(val), self.burnable_mats) return str(val) @@ -96,7 +99,7 @@ def get_transfer_rate(self, material, component): """ material_id = self._get_material_id(material) - check_type('component', component, str) + check_type("component", component, str) return [i[0] for i in self.transfer_rates[material_id][component]] def get_destination_material(self, material, component): @@ -118,7 +121,7 @@ def get_destination_material(self, material, component): """ material_id = self._get_material_id(material) - check_type('component', component, str) + check_type("component", component, str) if component in self.transfer_rates[material_id]: return [i[1] for i in self.transfer_rates[material_id][component]] else: @@ -142,8 +145,14 @@ def get_components(self, material): if material_id in self.transfer_rates: return self.transfer_rates[material_id].keys() - def set_transfer_rate(self, material, components, transfer_rate, - transfer_rate_units='1/s', destination_material=None): + def set_transfer_rate( + self, + material, + components, + transfer_rate, + transfer_rate_units="1/s", + destination_material=None, + ): """Set element and/or nuclide transfer rates in a depletable material. Parameters @@ -165,65 +174,75 @@ def set_transfer_rate(self, material, components, transfer_rate, """ material_id = self._get_material_id(material) - check_type('transfer_rate', transfer_rate, Real) - check_type('components', components, list, expected_iter_type=str) + check_type("transfer_rate", transfer_rate, Real) + check_type("components", components, list, expected_iter_type=str) if destination_material is not None: destination_material_id = self._get_material_id(destination_material) if len(self.burnable_mats) > 1: - check_value('destination_material', str(destination_material_id), - self.burnable_mats) + check_value( + "destination_material", + str(destination_material_id), + self.burnable_mats, + ) else: - raise ValueError('Transfer to material ' - f'{destination_material_id} is set, but there ' - 'is only one depletable material') + raise ValueError( + "Transfer to material " + f"{destination_material_id} is set, but there " + "is only one depletable material" + ) else: destination_material_id = None - if transfer_rate_units in ('1/s', '1/sec'): + if transfer_rate_units in ("1/s", "1/sec"): unit_conv = 1 - elif transfer_rate_units in ('1/min', '1/minute'): + elif transfer_rate_units in ("1/min", "1/minute"): unit_conv = 60 - elif transfer_rate_units in ('1/h', '1/hr', '1/hour'): - unit_conv = 60*60 - elif transfer_rate_units in ('1/d', '1/day'): - unit_conv = 24*60*60 - elif transfer_rate_units in ('1/a', '1/year'): - unit_conv = 365.25*24*60*60 + elif transfer_rate_units in ("1/h", "1/hr", "1/hour"): + unit_conv = 60 * 60 + elif transfer_rate_units in ("1/d", "1/day"): + unit_conv = 24 * 60 * 60 + elif transfer_rate_units in ("1/a", "1/year"): + unit_conv = 365.25 * 24 * 60 * 60 else: - raise ValueError('Invalid transfer rate unit ' - f'"{transfer_rate_units}"') + raise ValueError("Invalid transfer rate unit " f'"{transfer_rate_units}"') for component in components: current_components = self.transfer_rates[material_id].keys() - split_component = re.split(r'\d+', component) + split_component = re.split(r"\d+", component) element = split_component[0] if element not in ELEMENT_SYMBOL.values(): - raise ValueError(f'{component} is not a valid nuclide or ' - 'element.') + raise ValueError(f"{component} is not a valid nuclide or " "element.") else: if len(split_component) == 1: - element_nucs = [c for c in current_components - if re.match(component + r'\d', c)] + element_nucs = [ + c for c in current_components if re.match(component + r"\d", c) + ] if len(element_nucs) > 0: nuc_str = ", ".join(element_nucs) - raise ValueError('Cannot add transfer rate for element ' - f'{component} to material {material_id} ' - f'with transfer rate(s) for nuclide(s) ' - f'{nuc_str}.') + raise ValueError( + "Cannot add transfer rate for element " + f"{component} to material {material_id} " + f"with transfer rate(s) for nuclide(s) " + f"{nuc_str}." + ) else: if element in current_components: - raise ValueError('Cannot add transfer rate for nuclide ' - f'{component} to material {material_id} ' - f'where element {element} already has ' - 'a transfer rate.') + raise ValueError( + "Cannot add transfer rate for nuclide " + f"{component} to material {material_id} " + f"where element {element} already has " + "a transfer rate." + ) if component in self.transfer_rates[material_id]: self.transfer_rates[material_id][component].append( - (transfer_rate / unit_conv, destination_material_id)) + (transfer_rate / unit_conv, destination_material_id) + ) else: self.transfer_rates[material_id][component] = [ - (transfer_rate / unit_conv, destination_material_id)] + (transfer_rate / unit_conv, destination_material_id) + ] if destination_material_id is not None: self.index_transfer.add((destination_material_id, material_id)) diff --git a/openmc/element.py b/openmc/element.py index 082bee5226d..3a88d80522b 100644 --- a/openmc/element.py +++ b/openmc/element.py @@ -5,8 +5,12 @@ import openmc.checkvalue as cv import openmc -from openmc.data import NATURAL_ABUNDANCE, atomic_mass, zam, \ - isotopes as natural_isotopes +from openmc.data import ( + NATURAL_ABUNDANCE, + atomic_mass, + zam, + isotopes as natural_isotopes, +) class Element(str): @@ -28,17 +32,23 @@ class Element(str): """ def __new__(cls, name): - cv.check_type('element name', name, str) - cv.check_length('element name', name, 1, 2) + cv.check_type("element name", name, str) + cv.check_length("element name", name, 1, 2) return super().__new__(cls, name) @property def name(self): return self - def expand(self, percent, percent_type, enrichment=None, - enrichment_target=None, enrichment_type=None, - cross_sections=None): + def expand( + self, + percent, + percent_type, + enrichment=None, + enrichment_target=None, + enrichment_type=None, + cross_sections=None, + ): """Expand natural element into its naturally-occurring isotopes. An optional cross_sections argument or the ``cross_sections`` @@ -115,11 +125,11 @@ def expand(self, percent, percent_type, enrichment=None, """ # Check input if enrichment_type is not None: - cv.check_value('enrichment_type', enrichment_type, {'ao', 'wo'}) + cv.check_value("enrichment_type", enrichment_type, {"ao", "wo"}) if enrichment is not None: - cv.check_less_than('enrichment', enrichment, 100.0, equality=True) - cv.check_greater_than('enrichment', enrichment, 0., equality=True) + cv.check_less_than("enrichment", enrichment, 100.0, equality=True) + cv.check_greater_than("enrichment", enrichment, 0.0, equality=True) # Get the nuclides present in nature natural_nuclides = {name for name, abundance in natural_isotopes(self)} @@ -134,7 +144,7 @@ def expand(self, percent, percent_type, enrichment=None, # If cross_sections is None, get the cross sections from the global # configuration if cross_sections is None: - cross_sections = openmc.config.get('cross_sections') + cross_sections = openmc.config.get("cross_sections") # If a cross_sections library is present, check natural nuclides # against the nuclides in the library @@ -142,10 +152,9 @@ def expand(self, percent, percent_type, enrichment=None, library_nuclides = set() tree = ET.parse(cross_sections) root = tree.getroot() - for child in root.findall('library'): - nuclide = child.attrib['materials'] - if re.match(r'{}\d+'.format(self), nuclide) and \ - '_m' not in nuclide: + for child in root.findall("library"): + nuclide = child.attrib["materials"] + if re.match(r"{}\d+".format(self), nuclide) and "_m" not in nuclide: library_nuclides.add(nuclide) # Get a set of the mutual and absent nuclides. Convert to lists @@ -164,13 +173,15 @@ def expand(self, percent, percent_type, enrichment=None, # If some naturally occurring isotopes are not present in the # library, check if the "natural" nuclide (e.g., C0) is present. If # so, set the abundance to 1 for this nuclide. - elif (self + '0') in library_nuclides: - abundances[self + '0'] = 1.0 + elif (self + "0") in library_nuclides: + abundances[self + "0"] = 1.0 elif len(mutual_nuclides) == 0: - msg = (f'Unable to expand element {self} because the cross ' - 'section library provided does not contain any of ' - 'the natural isotopes for that element.') + msg = ( + f"Unable to expand element {self} because the cross " + "section library provided does not contain any of " + "the natural isotopes for that element." + ) raise ValueError(msg) # If some naturally occurring isotopes are in the library, add them. @@ -183,18 +194,20 @@ def expand(self, percent, percent_type, enrichment=None, # Adjust the abundances for the absent nuclides for nuclide in absent_nuclides: - if nuclide in ['O17', 'O18'] and 'O16' in mutual_nuclides: - abundances['O16'] += NATURAL_ABUNDANCE[nuclide] - elif nuclide == 'Ta180' and 'Ta181' in mutual_nuclides: - abundances['Ta181'] += NATURAL_ABUNDANCE[nuclide] - elif nuclide == 'W180' and 'W182' in mutual_nuclides: - abundances['W182'] += NATURAL_ABUNDANCE[nuclide] + if nuclide in ["O17", "O18"] and "O16" in mutual_nuclides: + abundances["O16"] += NATURAL_ABUNDANCE[nuclide] + elif nuclide == "Ta180" and "Ta181" in mutual_nuclides: + abundances["Ta181"] += NATURAL_ABUNDANCE[nuclide] + elif nuclide == "W180" and "W182" in mutual_nuclides: + abundances["W182"] += NATURAL_ABUNDANCE[nuclide] else: - msg = 'Unsure how to partition natural abundance of ' \ - 'isotope {0} into other natural isotopes of ' \ - 'this element that are present in the cross ' \ - 'section library provided. Consider adding ' \ - 'the isotopes of this element individually.' + msg = ( + "Unsure how to partition natural abundance of " + "isotope {0} into other natural isotopes of " + "this element that are present in the cross " + "section library provided. Consider adding " + "the isotopes of this element individually." + ) raise ValueError(msg) # If a cross_section library is not present, expand the element into @@ -208,22 +221,26 @@ def expand(self, percent, percent_type, enrichment=None, if enrichment is not None and enrichment_target is None: # Check that the element is Uranium - if self.name != 'U': - msg = ('Enrichment procedure for Uranium was requested, ' - f'but the isotope is {self} not U') + if self.name != "U": + msg = ( + "Enrichment procedure for Uranium was requested, " + f"but the isotope is {self} not U" + ) raise ValueError(msg) # Check that enrichment_type is not 'ao' - if enrichment_type == 'ao': - msg = ('Enrichment procedure for Uranium requires that ' - 'enrichment value is provided as wo%.') + if enrichment_type == "ao": + msg = ( + "Enrichment procedure for Uranium requires that " + "enrichment value is provided as wo%." + ) raise ValueError(msg) # Calculate the mass fractions of isotopes - abundances['U234'] = 0.0089 * enrichment - abundances['U235'] = enrichment - abundances['U236'] = 0.0046 * enrichment - abundances['U238'] = 100.0 - 1.0135 * enrichment + abundances["U234"] = 0.0089 * enrichment + abundances["U235"] = enrichment + abundances["U236"] = 0.0046 * enrichment + abundances["U238"] = 100.0 - 1.0135 * enrichment # Convert the mass fractions to mole fractions for nuclide in abundances.keys(): @@ -239,30 +256,38 @@ def expand(self, percent, percent_type, enrichment=None, elif enrichment is not None and enrichment_target is not None: # Provide more informative error message for U235 - if enrichment_target == 'U235': - msg = ("There is a special procedure for enrichment of U235 " - "in U. To invoke it, the arguments 'enrichment_target'" - "and 'enrichment_type' should be omitted. Provide " - "a value only for 'enrichment' in weight percent.") + if enrichment_target == "U235": + msg = ( + "There is a special procedure for enrichment of U235 " + "in U. To invoke it, the arguments 'enrichment_target'" + "and 'enrichment_type' should be omitted. Provide " + "a value only for 'enrichment' in weight percent." + ) raise ValueError(msg) # Check if it is two-isotope mixture if len(abundances) != 2: - msg = (f'Element {self} does not consist of two naturally-occurring ' - 'isotopes. Please enter isotopic abundances manually.') + msg = ( + f"Element {self} does not consist of two naturally-occurring " + "isotopes. Please enter isotopic abundances manually." + ) raise ValueError(msg) # Check if the target nuclide is present in the mixture if enrichment_target not in abundances: - msg = ('The target nuclide {} is not one of the naturally-occurring ' - 'isotopes ({})'.format(enrichment_target, list(abundances))) + msg = ( + "The target nuclide {} is not one of the naturally-occurring " + "isotopes ({})".format(enrichment_target, list(abundances)) + ) raise ValueError(msg) # If weight percent enrichment is requested convert to mass fractions - if enrichment_type == 'wo': + if enrichment_type == "wo": # Convert the atomic abundances to weight fractions # Compute the element atomic mass - element_am = sum(atomic_mass(nuc)*abundances[nuc] for nuc in abundances) + element_am = sum( + atomic_mass(nuc) * abundances[nuc] for nuc in abundances + ) # Convert Molar Fractions to mass fractions for nuclide in abundances: @@ -290,7 +315,7 @@ def expand(self, percent, percent_type, enrichment=None, abundances[enrichment_target] = enrichment / 100.0 # Convert back to atomic fractions if requested - if enrichment_type == 'wo': + if enrichment_type == "wo": # Convert the mass fractions to mole fractions for nuclide in abundances: abundances[nuclide] /= atomic_mass(nuclide) @@ -302,10 +327,10 @@ def expand(self, percent, percent_type, enrichment=None, # Compute the ratio of the nuclide atomic masses to the element # atomic mass - if percent_type == 'wo': + if percent_type == "wo": # Compute the element atomic mass - element_am = 0. + element_am = 0.0 for nuclide in abundances.keys(): element_am += atomic_mass(nuclide) * abundances[nuclide] diff --git a/openmc/examples.py b/openmc/examples.py index 538b6ea4ac7..059cc224f64 100644 --- a/openmc/examples.py +++ b/openmc/examples.py @@ -5,7 +5,6 @@ import openmc - def pwr_pin_cell() -> openmc.Model: """Create a PWR pin-cell model. @@ -23,46 +22,45 @@ def pwr_pin_cell() -> openmc.Model: model = openmc.Model() # Define materials. - fuel = openmc.Material(name='UO2 (2.4%)') - fuel.set_density('g/cm3', 10.29769) - fuel.add_nuclide('U234', 4.4843e-6) - fuel.add_nuclide('U235', 5.5815e-4) - fuel.add_nuclide('U238', 2.2408e-2) - fuel.add_nuclide('O16', 4.5829e-2) - - clad = openmc.Material(name='Zircaloy') - clad.set_density('g/cm3', 6.55) - clad.add_nuclide('Zr90', 2.1827e-2) - clad.add_nuclide('Zr91', 4.7600e-3) - clad.add_nuclide('Zr92', 7.2758e-3) - clad.add_nuclide('Zr94', 7.3734e-3) - clad.add_nuclide('Zr96', 1.1879e-3) - - hot_water = openmc.Material(name='Hot borated water') - hot_water.set_density('g/cm3', 0.740582) - hot_water.add_nuclide('H1', 4.9457e-2) - hot_water.add_nuclide('O16', 2.4672e-2) - hot_water.add_nuclide('B10', 8.0042e-6) - hot_water.add_nuclide('B11', 3.2218e-5) - hot_water.add_s_alpha_beta('c_H_in_H2O') + fuel = openmc.Material(name="UO2 (2.4%)") + fuel.set_density("g/cm3", 10.29769) + fuel.add_nuclide("U234", 4.4843e-6) + fuel.add_nuclide("U235", 5.5815e-4) + fuel.add_nuclide("U238", 2.2408e-2) + fuel.add_nuclide("O16", 4.5829e-2) + + clad = openmc.Material(name="Zircaloy") + clad.set_density("g/cm3", 6.55) + clad.add_nuclide("Zr90", 2.1827e-2) + clad.add_nuclide("Zr91", 4.7600e-3) + clad.add_nuclide("Zr92", 7.2758e-3) + clad.add_nuclide("Zr94", 7.3734e-3) + clad.add_nuclide("Zr96", 1.1879e-3) + + hot_water = openmc.Material(name="Hot borated water") + hot_water.set_density("g/cm3", 0.740582) + hot_water.add_nuclide("H1", 4.9457e-2) + hot_water.add_nuclide("O16", 2.4672e-2) + hot_water.add_nuclide("B10", 8.0042e-6) + hot_water.add_nuclide("B11", 3.2218e-5) + hot_water.add_s_alpha_beta("c_H_in_H2O") # Define the materials file. model.materials = (fuel, clad, hot_water) # Instantiate ZCylinder surfaces pitch = 1.26 - fuel_or = openmc.ZCylinder(x0=0, y0=0, r=0.39218, name='Fuel OR') - clad_or = openmc.ZCylinder(x0=0, y0=0, r=0.45720, name='Clad OR') - left = openmc.XPlane(x0=-pitch/2, name='left', boundary_type='reflective') - right = openmc.XPlane(x0=pitch/2, name='right', boundary_type='reflective') - bottom = openmc.YPlane(y0=-pitch/2, name='bottom', - boundary_type='reflective') - top = openmc.YPlane(y0=pitch/2, name='top', boundary_type='reflective') + fuel_or = openmc.ZCylinder(x0=0, y0=0, r=0.39218, name="Fuel OR") + clad_or = openmc.ZCylinder(x0=0, y0=0, r=0.45720, name="Clad OR") + left = openmc.XPlane(x0=-pitch / 2, name="left", boundary_type="reflective") + right = openmc.XPlane(x0=pitch / 2, name="right", boundary_type="reflective") + bottom = openmc.YPlane(y0=-pitch / 2, name="bottom", boundary_type="reflective") + top = openmc.YPlane(y0=pitch / 2, name="top", boundary_type="reflective") # Instantiate Cells - fuel_pin = openmc.Cell(name='Fuel', fill=fuel) - cladding = openmc.Cell(name='Cladding', fill=clad) - water = openmc.Cell(name='Water', fill=hot_water) + fuel_pin = openmc.Cell(name="Fuel", fill=fuel) + cladding = openmc.Cell(name="Cladding", fill=clad) + water = openmc.Cell(name="Water", fill=hot_water) # Use surface half-spaces to define regions fuel_pin.region = -fuel_or @@ -70,21 +68,20 @@ def pwr_pin_cell() -> openmc.Model: water.region = +clad_or & +left & -right & +bottom & -top # Create root universe - model.geometry.root_universe = openmc.Universe(0, name='root universe') + model.geometry.root_universe = openmc.Universe(0, name="root universe") model.geometry.root_universe.add_cells([fuel_pin, cladding, water]) model.settings.batches = 10 model.settings.inactive = 5 model.settings.particles = 100 model.settings.source = openmc.IndependentSource( - space=openmc.stats.Box([-pitch/2, -pitch/2, -1], - [pitch/2, pitch/2, 1]), - constraints={'fissionable': True} + space=openmc.stats.Box([-pitch / 2, -pitch / 2, -1], [pitch / 2, pitch / 2, 1]), + constraints={"fissionable": True}, ) plot = openmc.Plot.from_geometry(model.geometry) plot.pixels = (300, 300) - plot.color_by = 'material' + plot.color_by = "material" model.plots.append(plot) return model @@ -107,157 +104,167 @@ def pwr_core() -> openmc.Model: model = openmc.Model() # Define materials. - fuel = openmc.Material(1, name='UOX fuel') - fuel.set_density('g/cm3', 10.062) - fuel.add_nuclide('U234', 4.9476e-6) - fuel.add_nuclide('U235', 4.8218e-4) - fuel.add_nuclide('U238', 2.1504e-2) - fuel.add_nuclide('Xe135', 1.0801e-8) - fuel.add_nuclide('O16', 4.5737e-2) - - clad = openmc.Material(2, name='Zircaloy') - clad.set_density('g/cm3', 5.77) - clad.add_nuclide('Zr90', 0.5145) - clad.add_nuclide('Zr91', 0.1122) - clad.add_nuclide('Zr92', 0.1715) - clad.add_nuclide('Zr94', 0.1738) - clad.add_nuclide('Zr96', 0.0280) - - cold_water = openmc.Material(3, name='Cold borated water') - cold_water.set_density('atom/b-cm', 0.07416) - cold_water.add_nuclide('H1', 2.0) - cold_water.add_nuclide('O16', 1.0) - cold_water.add_nuclide('B10', 6.490e-4) - cold_water.add_nuclide('B11', 2.689e-3) - cold_water.add_s_alpha_beta('c_H_in_H2O') - - hot_water = openmc.Material(4, name='Hot borated water') - hot_water.set_density('atom/b-cm', 0.06614) - hot_water.add_nuclide('H1', 2.0) - hot_water.add_nuclide('O16', 1.0) - hot_water.add_nuclide('B10', 6.490e-4) - hot_water.add_nuclide('B11', 2.689e-3) - hot_water.add_s_alpha_beta('c_H_in_H2O') - - rpv_steel = openmc.Material(5, name='Reactor pressure vessel steel') - rpv_steel.set_density('g/cm3', 7.9) - rpv_steel.add_nuclide('Fe54', 0.05437098, 'wo') - rpv_steel.add_nuclide('Fe56', 0.88500663, 'wo') - rpv_steel.add_nuclide('Fe57', 0.0208008, 'wo') - rpv_steel.add_nuclide('Fe58', 0.00282159, 'wo') - rpv_steel.add_nuclide('Ni58', 0.0067198, 'wo') - rpv_steel.add_nuclide('Ni60', 0.0026776, 'wo') - rpv_steel.add_nuclide('Mn55', 0.01, 'wo') - rpv_steel.add_nuclide('Cr52', 0.002092475, 'wo') - rpv_steel.add_nuclide('C0', 0.0025, 'wo') - rpv_steel.add_nuclide('Cu63', 0.0013696, 'wo') - - lower_rad_ref = openmc.Material(6, name='Lower radial reflector') - lower_rad_ref.set_density('g/cm3', 4.32) - lower_rad_ref.add_nuclide('H1', 0.0095661, 'wo') - lower_rad_ref.add_nuclide('O16', 0.0759107, 'wo') - lower_rad_ref.add_nuclide('B10', 3.08409e-5, 'wo') - lower_rad_ref.add_nuclide('B11', 1.40499e-4, 'wo') - lower_rad_ref.add_nuclide('Fe54', 0.035620772088, 'wo') - lower_rad_ref.add_nuclide('Fe56', 0.579805982228, 'wo') - lower_rad_ref.add_nuclide('Fe57', 0.01362750048, 'wo') - lower_rad_ref.add_nuclide('Fe58', 0.001848545204, 'wo') - lower_rad_ref.add_nuclide('Ni58', 0.055298376566, 'wo') - lower_rad_ref.add_nuclide('Mn55', 0.0182870, 'wo') - lower_rad_ref.add_nuclide('Cr52', 0.145407678031, 'wo') - lower_rad_ref.add_s_alpha_beta('c_H_in_H2O') - - upper_rad_ref = openmc.Material( - 7, name='Upper radial reflector / Top plate region') - upper_rad_ref.set_density('g/cm3', 4.28) - upper_rad_ref.add_nuclide('H1', 0.0086117, 'wo') - upper_rad_ref.add_nuclide('O16', 0.0683369, 'wo') - upper_rad_ref.add_nuclide('B10', 2.77638e-5, 'wo') - upper_rad_ref.add_nuclide('B11', 1.26481e-4, 'wo') - upper_rad_ref.add_nuclide('Fe54', 0.035953677186, 'wo') - upper_rad_ref.add_nuclide('Fe56', 0.585224740891, 'wo') - upper_rad_ref.add_nuclide('Fe57', 0.01375486056, 'wo') - upper_rad_ref.add_nuclide('Fe58', 0.001865821363, 'wo') - upper_rad_ref.add_nuclide('Ni58', 0.055815129186, 'wo') - upper_rad_ref.add_nuclide('Mn55', 0.0184579, 'wo') - upper_rad_ref.add_nuclide('Cr52', 0.146766614995, 'wo') - upper_rad_ref.add_s_alpha_beta('c_H_in_H2O') - - bot_plate = openmc.Material(8, name='Bottom plate region') - bot_plate.set_density('g/cm3', 7.184) - bot_plate.add_nuclide('H1', 0.0011505, 'wo') - bot_plate.add_nuclide('O16', 0.0091296, 'wo') - bot_plate.add_nuclide('B10', 3.70915e-6, 'wo') - bot_plate.add_nuclide('B11', 1.68974e-5, 'wo') - bot_plate.add_nuclide('Fe54', 0.03855611055, 'wo') - bot_plate.add_nuclide('Fe56', 0.627585036425, 'wo') - bot_plate.add_nuclide('Fe57', 0.014750478, 'wo') - bot_plate.add_nuclide('Fe58', 0.002000875025, 'wo') - bot_plate.add_nuclide('Ni58', 0.059855207342, 'wo') - bot_plate.add_nuclide('Mn55', 0.0197940, 'wo') - bot_plate.add_nuclide('Cr52', 0.157390026871, 'wo') - bot_plate.add_s_alpha_beta('c_H_in_H2O') - - bot_nozzle = openmc.Material(9, name='Bottom nozzle region') - bot_nozzle.set_density('g/cm3', 2.53) - bot_nozzle.add_nuclide('H1', 0.0245014, 'wo') - bot_nozzle.add_nuclide('O16', 0.1944274, 'wo') - bot_nozzle.add_nuclide('B10', 7.89917e-5, 'wo') - bot_nozzle.add_nuclide('B11', 3.59854e-4, 'wo') - bot_nozzle.add_nuclide('Fe54', 0.030411411144, 'wo') - bot_nozzle.add_nuclide('Fe56', 0.495012237964, 'wo') - bot_nozzle.add_nuclide('Fe57', 0.01163454624, 'wo') - bot_nozzle.add_nuclide('Fe58', 0.001578204652, 'wo') - bot_nozzle.add_nuclide('Ni58', 0.047211231662, 'wo') - bot_nozzle.add_nuclide('Mn55', 0.0156126, 'wo') - bot_nozzle.add_nuclide('Cr52', 0.124142524198, 'wo') - bot_nozzle.add_s_alpha_beta('c_H_in_H2O') - - top_nozzle = openmc.Material(10, name='Top nozzle region') - top_nozzle.set_density('g/cm3', 1.746) - top_nozzle.add_nuclide('H1', 0.0358870, 'wo') - top_nozzle.add_nuclide('O16', 0.2847761, 'wo') - top_nozzle.add_nuclide('B10', 1.15699e-4, 'wo') - top_nozzle.add_nuclide('B11', 5.27075e-4, 'wo') - top_nozzle.add_nuclide('Fe54', 0.02644016154, 'wo') - top_nozzle.add_nuclide('Fe56', 0.43037146399, 'wo') - top_nozzle.add_nuclide('Fe57', 0.0101152584, 'wo') - top_nozzle.add_nuclide('Fe58', 0.00137211607, 'wo') - top_nozzle.add_nuclide('Ni58', 0.04104621835, 'wo') - top_nozzle.add_nuclide('Mn55', 0.0135739, 'wo') - top_nozzle.add_nuclide('Cr52', 0.107931450781, 'wo') - top_nozzle.add_s_alpha_beta('c_H_in_H2O') - - top_fa = openmc.Material(11, name='Top of fuel assemblies') - top_fa.set_density('g/cm3', 3.044) - top_fa.add_nuclide('H1', 0.0162913, 'wo') - top_fa.add_nuclide('O16', 0.1292776, 'wo') - top_fa.add_nuclide('B10', 5.25228e-5, 'wo') - top_fa.add_nuclide('B11', 2.39272e-4, 'wo') - top_fa.add_nuclide('Zr90', 0.43313403903, 'wo') - top_fa.add_nuclide('Zr91', 0.09549277374, 'wo') - top_fa.add_nuclide('Zr92', 0.14759527104, 'wo') - top_fa.add_nuclide('Zr94', 0.15280552077, 'wo') - top_fa.add_nuclide('Zr96', 0.02511169542, 'wo') - top_fa.add_s_alpha_beta('c_H_in_H2O') - - bot_fa = openmc.Material(12, name='Bottom of fuel assemblies') - bot_fa.set_density('g/cm3', 1.762) - bot_fa.add_nuclide('H1', 0.0292856, 'wo') - bot_fa.add_nuclide('O16', 0.2323919, 'wo') - bot_fa.add_nuclide('B10', 9.44159e-5, 'wo') - bot_fa.add_nuclide('B11', 4.30120e-4, 'wo') - bot_fa.add_nuclide('Zr90', 0.3741373658, 'wo') - bot_fa.add_nuclide('Zr91', 0.0824858164, 'wo') - bot_fa.add_nuclide('Zr92', 0.1274914944, 'wo') - bot_fa.add_nuclide('Zr94', 0.1319920622, 'wo') - bot_fa.add_nuclide('Zr96', 0.0216912612, 'wo') - bot_fa.add_s_alpha_beta('c_H_in_H2O') + fuel = openmc.Material(1, name="UOX fuel") + fuel.set_density("g/cm3", 10.062) + fuel.add_nuclide("U234", 4.9476e-6) + fuel.add_nuclide("U235", 4.8218e-4) + fuel.add_nuclide("U238", 2.1504e-2) + fuel.add_nuclide("Xe135", 1.0801e-8) + fuel.add_nuclide("O16", 4.5737e-2) + + clad = openmc.Material(2, name="Zircaloy") + clad.set_density("g/cm3", 5.77) + clad.add_nuclide("Zr90", 0.5145) + clad.add_nuclide("Zr91", 0.1122) + clad.add_nuclide("Zr92", 0.1715) + clad.add_nuclide("Zr94", 0.1738) + clad.add_nuclide("Zr96", 0.0280) + + cold_water = openmc.Material(3, name="Cold borated water") + cold_water.set_density("atom/b-cm", 0.07416) + cold_water.add_nuclide("H1", 2.0) + cold_water.add_nuclide("O16", 1.0) + cold_water.add_nuclide("B10", 6.490e-4) + cold_water.add_nuclide("B11", 2.689e-3) + cold_water.add_s_alpha_beta("c_H_in_H2O") + + hot_water = openmc.Material(4, name="Hot borated water") + hot_water.set_density("atom/b-cm", 0.06614) + hot_water.add_nuclide("H1", 2.0) + hot_water.add_nuclide("O16", 1.0) + hot_water.add_nuclide("B10", 6.490e-4) + hot_water.add_nuclide("B11", 2.689e-3) + hot_water.add_s_alpha_beta("c_H_in_H2O") + + rpv_steel = openmc.Material(5, name="Reactor pressure vessel steel") + rpv_steel.set_density("g/cm3", 7.9) + rpv_steel.add_nuclide("Fe54", 0.05437098, "wo") + rpv_steel.add_nuclide("Fe56", 0.88500663, "wo") + rpv_steel.add_nuclide("Fe57", 0.0208008, "wo") + rpv_steel.add_nuclide("Fe58", 0.00282159, "wo") + rpv_steel.add_nuclide("Ni58", 0.0067198, "wo") + rpv_steel.add_nuclide("Ni60", 0.0026776, "wo") + rpv_steel.add_nuclide("Mn55", 0.01, "wo") + rpv_steel.add_nuclide("Cr52", 0.002092475, "wo") + rpv_steel.add_nuclide("C0", 0.0025, "wo") + rpv_steel.add_nuclide("Cu63", 0.0013696, "wo") + + lower_rad_ref = openmc.Material(6, name="Lower radial reflector") + lower_rad_ref.set_density("g/cm3", 4.32) + lower_rad_ref.add_nuclide("H1", 0.0095661, "wo") + lower_rad_ref.add_nuclide("O16", 0.0759107, "wo") + lower_rad_ref.add_nuclide("B10", 3.08409e-5, "wo") + lower_rad_ref.add_nuclide("B11", 1.40499e-4, "wo") + lower_rad_ref.add_nuclide("Fe54", 0.035620772088, "wo") + lower_rad_ref.add_nuclide("Fe56", 0.579805982228, "wo") + lower_rad_ref.add_nuclide("Fe57", 0.01362750048, "wo") + lower_rad_ref.add_nuclide("Fe58", 0.001848545204, "wo") + lower_rad_ref.add_nuclide("Ni58", 0.055298376566, "wo") + lower_rad_ref.add_nuclide("Mn55", 0.0182870, "wo") + lower_rad_ref.add_nuclide("Cr52", 0.145407678031, "wo") + lower_rad_ref.add_s_alpha_beta("c_H_in_H2O") + + upper_rad_ref = openmc.Material(7, name="Upper radial reflector / Top plate region") + upper_rad_ref.set_density("g/cm3", 4.28) + upper_rad_ref.add_nuclide("H1", 0.0086117, "wo") + upper_rad_ref.add_nuclide("O16", 0.0683369, "wo") + upper_rad_ref.add_nuclide("B10", 2.77638e-5, "wo") + upper_rad_ref.add_nuclide("B11", 1.26481e-4, "wo") + upper_rad_ref.add_nuclide("Fe54", 0.035953677186, "wo") + upper_rad_ref.add_nuclide("Fe56", 0.585224740891, "wo") + upper_rad_ref.add_nuclide("Fe57", 0.01375486056, "wo") + upper_rad_ref.add_nuclide("Fe58", 0.001865821363, "wo") + upper_rad_ref.add_nuclide("Ni58", 0.055815129186, "wo") + upper_rad_ref.add_nuclide("Mn55", 0.0184579, "wo") + upper_rad_ref.add_nuclide("Cr52", 0.146766614995, "wo") + upper_rad_ref.add_s_alpha_beta("c_H_in_H2O") + + bot_plate = openmc.Material(8, name="Bottom plate region") + bot_plate.set_density("g/cm3", 7.184) + bot_plate.add_nuclide("H1", 0.0011505, "wo") + bot_plate.add_nuclide("O16", 0.0091296, "wo") + bot_plate.add_nuclide("B10", 3.70915e-6, "wo") + bot_plate.add_nuclide("B11", 1.68974e-5, "wo") + bot_plate.add_nuclide("Fe54", 0.03855611055, "wo") + bot_plate.add_nuclide("Fe56", 0.627585036425, "wo") + bot_plate.add_nuclide("Fe57", 0.014750478, "wo") + bot_plate.add_nuclide("Fe58", 0.002000875025, "wo") + bot_plate.add_nuclide("Ni58", 0.059855207342, "wo") + bot_plate.add_nuclide("Mn55", 0.0197940, "wo") + bot_plate.add_nuclide("Cr52", 0.157390026871, "wo") + bot_plate.add_s_alpha_beta("c_H_in_H2O") + + bot_nozzle = openmc.Material(9, name="Bottom nozzle region") + bot_nozzle.set_density("g/cm3", 2.53) + bot_nozzle.add_nuclide("H1", 0.0245014, "wo") + bot_nozzle.add_nuclide("O16", 0.1944274, "wo") + bot_nozzle.add_nuclide("B10", 7.89917e-5, "wo") + bot_nozzle.add_nuclide("B11", 3.59854e-4, "wo") + bot_nozzle.add_nuclide("Fe54", 0.030411411144, "wo") + bot_nozzle.add_nuclide("Fe56", 0.495012237964, "wo") + bot_nozzle.add_nuclide("Fe57", 0.01163454624, "wo") + bot_nozzle.add_nuclide("Fe58", 0.001578204652, "wo") + bot_nozzle.add_nuclide("Ni58", 0.047211231662, "wo") + bot_nozzle.add_nuclide("Mn55", 0.0156126, "wo") + bot_nozzle.add_nuclide("Cr52", 0.124142524198, "wo") + bot_nozzle.add_s_alpha_beta("c_H_in_H2O") + + top_nozzle = openmc.Material(10, name="Top nozzle region") + top_nozzle.set_density("g/cm3", 1.746) + top_nozzle.add_nuclide("H1", 0.0358870, "wo") + top_nozzle.add_nuclide("O16", 0.2847761, "wo") + top_nozzle.add_nuclide("B10", 1.15699e-4, "wo") + top_nozzle.add_nuclide("B11", 5.27075e-4, "wo") + top_nozzle.add_nuclide("Fe54", 0.02644016154, "wo") + top_nozzle.add_nuclide("Fe56", 0.43037146399, "wo") + top_nozzle.add_nuclide("Fe57", 0.0101152584, "wo") + top_nozzle.add_nuclide("Fe58", 0.00137211607, "wo") + top_nozzle.add_nuclide("Ni58", 0.04104621835, "wo") + top_nozzle.add_nuclide("Mn55", 0.0135739, "wo") + top_nozzle.add_nuclide("Cr52", 0.107931450781, "wo") + top_nozzle.add_s_alpha_beta("c_H_in_H2O") + + top_fa = openmc.Material(11, name="Top of fuel assemblies") + top_fa.set_density("g/cm3", 3.044) + top_fa.add_nuclide("H1", 0.0162913, "wo") + top_fa.add_nuclide("O16", 0.1292776, "wo") + top_fa.add_nuclide("B10", 5.25228e-5, "wo") + top_fa.add_nuclide("B11", 2.39272e-4, "wo") + top_fa.add_nuclide("Zr90", 0.43313403903, "wo") + top_fa.add_nuclide("Zr91", 0.09549277374, "wo") + top_fa.add_nuclide("Zr92", 0.14759527104, "wo") + top_fa.add_nuclide("Zr94", 0.15280552077, "wo") + top_fa.add_nuclide("Zr96", 0.02511169542, "wo") + top_fa.add_s_alpha_beta("c_H_in_H2O") + + bot_fa = openmc.Material(12, name="Bottom of fuel assemblies") + bot_fa.set_density("g/cm3", 1.762) + bot_fa.add_nuclide("H1", 0.0292856, "wo") + bot_fa.add_nuclide("O16", 0.2323919, "wo") + bot_fa.add_nuclide("B10", 9.44159e-5, "wo") + bot_fa.add_nuclide("B11", 4.30120e-4, "wo") + bot_fa.add_nuclide("Zr90", 0.3741373658, "wo") + bot_fa.add_nuclide("Zr91", 0.0824858164, "wo") + bot_fa.add_nuclide("Zr92", 0.1274914944, "wo") + bot_fa.add_nuclide("Zr94", 0.1319920622, "wo") + bot_fa.add_nuclide("Zr96", 0.0216912612, "wo") + bot_fa.add_s_alpha_beta("c_H_in_H2O") # Define the materials file. - model.materials = (fuel, clad, cold_water, hot_water, rpv_steel, - lower_rad_ref, upper_rad_ref, bot_plate, - bot_nozzle, top_nozzle, top_fa, bot_fa) + model.materials = ( + fuel, + clad, + cold_water, + hot_water, + rpv_steel, + lower_rad_ref, + upper_rad_ref, + bot_plate, + bot_nozzle, + top_nozzle, + top_fa, + bot_fa, + ) # Define surfaces. s1 = openmc.ZCylinder(r=0.41, surface_id=1) @@ -267,9 +274,9 @@ def pwr_core() -> openmc.Model: s5 = openmc.ZCylinder(r=187.6, surface_id=5) s6 = openmc.ZCylinder(r=209.0, surface_id=6) s7 = openmc.ZCylinder(r=229.0, surface_id=7) - s8 = openmc.ZCylinder(r=249.0, surface_id=8, boundary_type='vacuum') + s8 = openmc.ZCylinder(r=249.0, surface_id=8, boundary_type="vacuum") - s31 = openmc.ZPlane(z0=-229.0, surface_id=31, boundary_type='vacuum') + s31 = openmc.ZPlane(z0=-229.0, surface_id=31, boundary_type="vacuum") s32 = openmc.ZPlane(z0=-199.0, surface_id=32) s33 = openmc.ZPlane(z0=-193.0, surface_id=33) s34 = openmc.ZPlane(z0=-183.0, surface_id=34) @@ -277,130 +284,184 @@ def pwr_core() -> openmc.Model: s36 = openmc.ZPlane(z0=183.0, surface_id=36) s37 = openmc.ZPlane(z0=203.0, surface_id=37) s38 = openmc.ZPlane(z0=215.0, surface_id=38) - s39 = openmc.ZPlane(z0=223.0, surface_id=39, boundary_type='vacuum') + s39 = openmc.ZPlane(z0=223.0, surface_id=39, boundary_type="vacuum") # Define pin cells. - fuel_cold = openmc.Universe(name='Fuel pin, cladding, cold water', - universe_id=1) + fuel_cold = openmc.Universe(name="Fuel pin, cladding, cold water", universe_id=1) c21 = openmc.Cell(cell_id=21, fill=fuel, region=-s1) c22 = openmc.Cell(cell_id=22, fill=clad, region=+s1 & -s2) c23 = openmc.Cell(cell_id=23, fill=cold_water, region=+s2) fuel_cold.add_cells((c21, c22, c23)) - tube_cold = openmc.Universe(name='Instrumentation guide tube, ' - 'cold water', universe_id=2) + tube_cold = openmc.Universe( + name="Instrumentation guide tube, " "cold water", universe_id=2 + ) c24 = openmc.Cell(cell_id=24, fill=cold_water, region=-s3) c25 = openmc.Cell(cell_id=25, fill=clad, region=+s3 & -s4) c26 = openmc.Cell(cell_id=26, fill=cold_water, region=+s4) tube_cold.add_cells((c24, c25, c26)) - fuel_hot = openmc.Universe(name='Fuel pin, cladding, hot water', - universe_id=3) + fuel_hot = openmc.Universe(name="Fuel pin, cladding, hot water", universe_id=3) c27 = openmc.Cell(cell_id=27, fill=fuel, region=-s1) c28 = openmc.Cell(cell_id=28, fill=clad, region=+s1 & -s2) c29 = openmc.Cell(cell_id=29, fill=hot_water, region=+s2) fuel_hot.add_cells((c27, c28, c29)) - tube_hot = openmc.Universe(name='Instrumentation guide tube, hot water', - universe_id=4) + tube_hot = openmc.Universe( + name="Instrumentation guide tube, hot water", universe_id=4 + ) c30 = openmc.Cell(cell_id=30, fill=hot_water, region=-s3) c31 = openmc.Cell(cell_id=31, fill=clad, region=+s3 & -s4) c32 = openmc.Cell(cell_id=32, fill=hot_water, region=+s4) tube_hot.add_cells((c30, c31, c32)) # Set positions occupied by guide tubes - tube_x = np.array([5, 8, 11, 3, 13, 2, 5, 8, 11, 14, 2, 5, 8, 11, 14, - 2, 5, 8, 11, 14, 3, 13, 5, 8, 11]) - tube_y = np.array([2, 2, 2, 3, 3, 5, 5, 5, 5, 5, 8, 8, 8, 8, 8, - 11, 11, 11, 11, 11, 13, 13, 14, 14, 14]) + tube_x = np.array( + [ + 5, + 8, + 11, + 3, + 13, + 2, + 5, + 8, + 11, + 14, + 2, + 5, + 8, + 11, + 14, + 2, + 5, + 8, + 11, + 14, + 3, + 13, + 5, + 8, + 11, + ] + ) + tube_y = np.array( + [ + 2, + 2, + 2, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 8, + 8, + 8, + 8, + 8, + 11, + 11, + 11, + 11, + 11, + 13, + 13, + 14, + 14, + 14, + ] + ) # Define fuel lattices. - l100 = openmc.RectLattice( - name='Fuel assembly (lower half)', lattice_id=100) + l100 = openmc.RectLattice(name="Fuel assembly (lower half)", lattice_id=100) l100.lower_left = (-10.71, -10.71) l100.pitch = (1.26, 1.26) l100.universes = np.tile(fuel_cold, (17, 17)) l100.universes[tube_x, tube_y] = tube_cold - l101 = openmc.RectLattice( - name='Fuel assembly (upper half)', lattice_id=101) + l101 = openmc.RectLattice(name="Fuel assembly (upper half)", lattice_id=101) l101.lower_left = (-10.71, -10.71) l101.pitch = (1.26, 1.26) l101.universes = np.tile(fuel_hot, (17, 17)) l101.universes[tube_x, tube_y] = tube_hot # Define assemblies. - fa_cw = openmc.Universe(name='Water assembly (cold)', universe_id=5) + fa_cw = openmc.Universe(name="Water assembly (cold)", universe_id=5) c50 = openmc.Cell(cell_id=50, fill=cold_water, region=+s34 & -s35) fa_cw.add_cell(c50) - fa_hw = openmc.Universe(name='Water assembly (hot)', universe_id=7) + fa_hw = openmc.Universe(name="Water assembly (hot)", universe_id=7) c70 = openmc.Cell(cell_id=70, fill=hot_water, region=+s35 & -s36) fa_hw.add_cell(c70) - fa_cold = openmc.Universe(name='Fuel assembly (cold)', universe_id=6) + fa_cold = openmc.Universe(name="Fuel assembly (cold)", universe_id=6) c60 = openmc.Cell(cell_id=60, fill=l100, region=+s34 & -s35) fa_cold.add_cell(c60) - fa_hot = openmc.Universe(name='Fuel assembly (hot)', universe_id=8) + fa_hot = openmc.Universe(name="Fuel assembly (hot)", universe_id=8) c80 = openmc.Cell(cell_id=80, fill=l101, region=+s35 & -s36) fa_hot.add_cell(c80) # Define core lattices - l200 = openmc.RectLattice(name='Core lattice (lower half)', lattice_id=200) + l200 = openmc.RectLattice(name="Core lattice (lower half)", lattice_id=200) l200.lower_left = (-224.91, -224.91) l200.pitch = (21.42, 21.42) l200.universes = [ - [fa_cw]*21, - [fa_cw]*21, - [fa_cw]*7 + [fa_cold]*7 + [fa_cw]*7, - [fa_cw]*5 + [fa_cold]*11 + [fa_cw]*5, - [fa_cw]*4 + [fa_cold]*13 + [fa_cw]*4, - [fa_cw]*3 + [fa_cold]*15 + [fa_cw]*3, - [fa_cw]*3 + [fa_cold]*15 + [fa_cw]*3, - [fa_cw]*2 + [fa_cold]*17 + [fa_cw]*2, - [fa_cw]*2 + [fa_cold]*17 + [fa_cw]*2, - [fa_cw]*2 + [fa_cold]*17 + [fa_cw]*2, - [fa_cw]*2 + [fa_cold]*17 + [fa_cw]*2, - [fa_cw]*2 + [fa_cold]*17 + [fa_cw]*2, - [fa_cw]*2 + [fa_cold]*17 + [fa_cw]*2, - [fa_cw]*2 + [fa_cold]*17 + [fa_cw]*2, - [fa_cw]*3 + [fa_cold]*15 + [fa_cw]*3, - [fa_cw]*3 + [fa_cold]*15 + [fa_cw]*3, - [fa_cw]*4 + [fa_cold]*13 + [fa_cw]*4, - [fa_cw]*5 + [fa_cold]*11 + [fa_cw]*5, - [fa_cw]*7 + [fa_cold]*7 + [fa_cw]*7, - [fa_cw]*21, - [fa_cw]*21] - - l201 = openmc.RectLattice(name='Core lattice (lower half)', lattice_id=201) + [fa_cw] * 21, + [fa_cw] * 21, + [fa_cw] * 7 + [fa_cold] * 7 + [fa_cw] * 7, + [fa_cw] * 5 + [fa_cold] * 11 + [fa_cw] * 5, + [fa_cw] * 4 + [fa_cold] * 13 + [fa_cw] * 4, + [fa_cw] * 3 + [fa_cold] * 15 + [fa_cw] * 3, + [fa_cw] * 3 + [fa_cold] * 15 + [fa_cw] * 3, + [fa_cw] * 2 + [fa_cold] * 17 + [fa_cw] * 2, + [fa_cw] * 2 + [fa_cold] * 17 + [fa_cw] * 2, + [fa_cw] * 2 + [fa_cold] * 17 + [fa_cw] * 2, + [fa_cw] * 2 + [fa_cold] * 17 + [fa_cw] * 2, + [fa_cw] * 2 + [fa_cold] * 17 + [fa_cw] * 2, + [fa_cw] * 2 + [fa_cold] * 17 + [fa_cw] * 2, + [fa_cw] * 2 + [fa_cold] * 17 + [fa_cw] * 2, + [fa_cw] * 3 + [fa_cold] * 15 + [fa_cw] * 3, + [fa_cw] * 3 + [fa_cold] * 15 + [fa_cw] * 3, + [fa_cw] * 4 + [fa_cold] * 13 + [fa_cw] * 4, + [fa_cw] * 5 + [fa_cold] * 11 + [fa_cw] * 5, + [fa_cw] * 7 + [fa_cold] * 7 + [fa_cw] * 7, + [fa_cw] * 21, + [fa_cw] * 21, + ] + + l201 = openmc.RectLattice(name="Core lattice (lower half)", lattice_id=201) l201.lower_left = (-224.91, -224.91) l201.pitch = (21.42, 21.42) l201.universes = [ - [fa_hw]*21, - [fa_hw]*21, - [fa_hw]*7 + [fa_hot]*7 + [fa_hw]*7, - [fa_hw]*5 + [fa_hot]*11 + [fa_hw]*5, - [fa_hw]*4 + [fa_hot]*13 + [fa_hw]*4, - [fa_hw]*3 + [fa_hot]*15 + [fa_hw]*3, - [fa_hw]*3 + [fa_hot]*15 + [fa_hw]*3, - [fa_hw]*2 + [fa_hot]*17 + [fa_hw]*2, - [fa_hw]*2 + [fa_hot]*17 + [fa_hw]*2, - [fa_hw]*2 + [fa_hot]*17 + [fa_hw]*2, - [fa_hw]*2 + [fa_hot]*17 + [fa_hw]*2, - [fa_hw]*2 + [fa_hot]*17 + [fa_hw]*2, - [fa_hw]*2 + [fa_hot]*17 + [fa_hw]*2, - [fa_hw]*2 + [fa_hot]*17 + [fa_hw]*2, - [fa_hw]*3 + [fa_hot]*15 + [fa_hw]*3, - [fa_hw]*3 + [fa_hot]*15 + [fa_hw]*3, - [fa_hw]*4 + [fa_hot]*13 + [fa_hw]*4, - [fa_hw]*5 + [fa_hot]*11 + [fa_hw]*5, - [fa_hw]*7 + [fa_hot]*7 + [fa_hw]*7, - [fa_hw]*21, - [fa_hw]*21] + [fa_hw] * 21, + [fa_hw] * 21, + [fa_hw] * 7 + [fa_hot] * 7 + [fa_hw] * 7, + [fa_hw] * 5 + [fa_hot] * 11 + [fa_hw] * 5, + [fa_hw] * 4 + [fa_hot] * 13 + [fa_hw] * 4, + [fa_hw] * 3 + [fa_hot] * 15 + [fa_hw] * 3, + [fa_hw] * 3 + [fa_hot] * 15 + [fa_hw] * 3, + [fa_hw] * 2 + [fa_hot] * 17 + [fa_hw] * 2, + [fa_hw] * 2 + [fa_hot] * 17 + [fa_hw] * 2, + [fa_hw] * 2 + [fa_hot] * 17 + [fa_hw] * 2, + [fa_hw] * 2 + [fa_hot] * 17 + [fa_hw] * 2, + [fa_hw] * 2 + [fa_hot] * 17 + [fa_hw] * 2, + [fa_hw] * 2 + [fa_hot] * 17 + [fa_hw] * 2, + [fa_hw] * 2 + [fa_hot] * 17 + [fa_hw] * 2, + [fa_hw] * 3 + [fa_hot] * 15 + [fa_hw] * 3, + [fa_hw] * 3 + [fa_hot] * 15 + [fa_hw] * 3, + [fa_hw] * 4 + [fa_hot] * 13 + [fa_hw] * 4, + [fa_hw] * 5 + [fa_hot] * 11 + [fa_hw] * 5, + [fa_hw] * 7 + [fa_hot] * 7 + [fa_hw] * 7, + [fa_hw] * 21, + [fa_hw] * 21, + ] # Define root universe. - root = openmc.Universe(universe_id=0, name='root universe') + root = openmc.Universe(universe_id=0, name="root universe") c1 = openmc.Cell(cell_id=1, fill=l200, region=-s6 & +s34 & -s35) c2 = openmc.Cell(cell_id=2, fill=l201, region=-s6 & +s35 & -s36) c3 = openmc.Cell(cell_id=3, fill=bot_plate, region=-s7 & +s31 & -s32) @@ -409,14 +470,10 @@ def pwr_core() -> openmc.Model: c6 = openmc.Cell(cell_id=6, fill=top_fa, region=-s5 & +s36 & -s37) c7 = openmc.Cell(cell_id=7, fill=top_nozzle, region=-s5 & +s37 & -s38) c8 = openmc.Cell(cell_id=8, fill=upper_rad_ref, region=-s7 & +s38 & -s39) - c9 = openmc.Cell(cell_id=9, fill=bot_nozzle, - region=+s6 & -s7 & +s32 & -s38) - c10 = openmc.Cell(cell_id=10, fill=rpv_steel, - region=+s7 & -s8 & +s31 & -s39) - c11 = openmc.Cell(cell_id=11, fill=lower_rad_ref, - region=+s5 & -s6 & +s32 & -s34) - c12 = openmc.Cell(cell_id=12, fill=upper_rad_ref, - region=+s5 & -s6 & +s36 & -s38) + c9 = openmc.Cell(cell_id=9, fill=bot_nozzle, region=+s6 & -s7 & +s32 & -s38) + c10 = openmc.Cell(cell_id=10, fill=rpv_steel, region=+s7 & -s8 & +s31 & -s39) + c11 = openmc.Cell(cell_id=11, fill=lower_rad_ref, region=+s5 & -s6 & +s32 & -s34) + c12 = openmc.Cell(cell_id=12, fill=upper_rad_ref, region=+s5 & -s6 & +s36 & -s38) root.add_cells((c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12)) # Assign root universe to geometry @@ -425,14 +482,15 @@ def pwr_core() -> openmc.Model: model.settings.batches = 10 model.settings.inactive = 5 model.settings.particles = 100 - model.settings.source = openmc.IndependentSource(space=openmc.stats.Box( - [-160, -160, -183], [160, 160, 183])) + model.settings.source = openmc.IndependentSource( + space=openmc.stats.Box([-160, -160, -183], [160, 160, 183]) + ) plot = openmc.Plot() plot.origin = (125, 125, 0) plot.width = (250, 250) plot.pixels = (3000, 3000) - plot.color_by = 'material' + plot.color_by = "material" model.plots.append(plot) return model @@ -456,104 +514,159 @@ def pwr_assembly() -> openmc.Model: model = openmc.Model() # Define materials. - fuel = openmc.Material(name='Fuel') - fuel.set_density('g/cm3', 10.29769) - fuel.add_nuclide('U234', 4.4843e-6) - fuel.add_nuclide('U235', 5.5815e-4) - fuel.add_nuclide('U238', 2.2408e-2) - fuel.add_nuclide('O16', 4.5829e-2) - - clad = openmc.Material(name='Cladding') - clad.set_density('g/cm3', 6.55) - clad.add_nuclide('Zr90', 2.1827e-2) - clad.add_nuclide('Zr91', 4.7600e-3) - clad.add_nuclide('Zr92', 7.2758e-3) - clad.add_nuclide('Zr94', 7.3734e-3) - clad.add_nuclide('Zr96', 1.1879e-3) - - hot_water = openmc.Material(name='Hot borated water') - hot_water.set_density('g/cm3', 0.740582) - hot_water.add_nuclide('H1', 4.9457e-2) - hot_water.add_nuclide('O16', 2.4672e-2) - hot_water.add_nuclide('B10', 8.0042e-6) - hot_water.add_nuclide('B11', 3.2218e-5) - hot_water.add_s_alpha_beta('c_H_in_H2O') + fuel = openmc.Material(name="Fuel") + fuel.set_density("g/cm3", 10.29769) + fuel.add_nuclide("U234", 4.4843e-6) + fuel.add_nuclide("U235", 5.5815e-4) + fuel.add_nuclide("U238", 2.2408e-2) + fuel.add_nuclide("O16", 4.5829e-2) + + clad = openmc.Material(name="Cladding") + clad.set_density("g/cm3", 6.55) + clad.add_nuclide("Zr90", 2.1827e-2) + clad.add_nuclide("Zr91", 4.7600e-3) + clad.add_nuclide("Zr92", 7.2758e-3) + clad.add_nuclide("Zr94", 7.3734e-3) + clad.add_nuclide("Zr96", 1.1879e-3) + + hot_water = openmc.Material(name="Hot borated water") + hot_water.set_density("g/cm3", 0.740582) + hot_water.add_nuclide("H1", 4.9457e-2) + hot_water.add_nuclide("O16", 2.4672e-2) + hot_water.add_nuclide("B10", 8.0042e-6) + hot_water.add_nuclide("B11", 3.2218e-5) + hot_water.add_s_alpha_beta("c_H_in_H2O") # Define the materials file. model.materials = (fuel, clad, hot_water) # Instantiate ZCylinder surfaces - fuel_or = openmc.ZCylinder(x0=0, y0=0, r=0.39218, name='Fuel OR') - clad_or = openmc.ZCylinder(x0=0, y0=0, r=0.45720, name='Clad OR') + fuel_or = openmc.ZCylinder(x0=0, y0=0, r=0.39218, name="Fuel OR") + clad_or = openmc.ZCylinder(x0=0, y0=0, r=0.45720, name="Clad OR") # Create boundary planes to surround the geometry pitch = 21.42 - min_x = openmc.XPlane(x0=-pitch/2, boundary_type='reflective') - max_x = openmc.XPlane(x0=+pitch/2, boundary_type='reflective') - min_y = openmc.YPlane(y0=-pitch/2, boundary_type='reflective') - max_y = openmc.YPlane(y0=+pitch/2, boundary_type='reflective') + min_x = openmc.XPlane(x0=-pitch / 2, boundary_type="reflective") + max_x = openmc.XPlane(x0=+pitch / 2, boundary_type="reflective") + min_y = openmc.YPlane(y0=-pitch / 2, boundary_type="reflective") + max_y = openmc.YPlane(y0=+pitch / 2, boundary_type="reflective") # Create a fuel pin universe - fuel_pin_universe = openmc.Universe(name='Fuel Pin') - fuel_cell = openmc.Cell(name='fuel', fill=fuel, region=-fuel_or) - clad_cell = openmc.Cell(name='clad', fill=clad, region=+fuel_or & -clad_or) - hot_water_cell = openmc.Cell( - name='hot water', fill=hot_water, region=+clad_or) + fuel_pin_universe = openmc.Universe(name="Fuel Pin") + fuel_cell = openmc.Cell(name="fuel", fill=fuel, region=-fuel_or) + clad_cell = openmc.Cell(name="clad", fill=clad, region=+fuel_or & -clad_or) + hot_water_cell = openmc.Cell(name="hot water", fill=hot_water, region=+clad_or) fuel_pin_universe.add_cells([fuel_cell, clad_cell, hot_water_cell]) # Create a control rod guide tube universe - guide_tube_universe = openmc.Universe(name='Guide Tube') - gt_inner_cell = openmc.Cell(name='guide tube inner water', fill=hot_water, - region=-fuel_or) - gt_clad_cell = openmc.Cell(name='guide tube clad', fill=clad, - region=+fuel_or & -clad_or) - gt_outer_cell = openmc.Cell(name='guide tube outer water', fill=hot_water, - region=+clad_or) + guide_tube_universe = openmc.Universe(name="Guide Tube") + gt_inner_cell = openmc.Cell( + name="guide tube inner water", fill=hot_water, region=-fuel_or + ) + gt_clad_cell = openmc.Cell( + name="guide tube clad", fill=clad, region=+fuel_or & -clad_or + ) + gt_outer_cell = openmc.Cell( + name="guide tube outer water", fill=hot_water, region=+clad_or + ) guide_tube_universe.add_cells([gt_inner_cell, gt_clad_cell, gt_outer_cell]) # Create fuel assembly Lattice - assembly = openmc.RectLattice(name='Fuel Assembly') - assembly.pitch = (pitch/17, pitch/17) - assembly.lower_left = (-pitch/2, -pitch/2) + assembly = openmc.RectLattice(name="Fuel Assembly") + assembly.pitch = (pitch / 17, pitch / 17) + assembly.lower_left = (-pitch / 2, -pitch / 2) # Create array indices for guide tube locations in lattice - template_x = np.array([5, 8, 11, 3, 13, 2, 5, 8, 11, 14, 2, 5, 8, - 11, 14, 2, 5, 8, 11, 14, 3, 13, 5, 8, 11]) - template_y = np.array([2, 2, 2, 3, 3, 5, 5, 5, 5, 5, 8, 8, 8, 8, - 8, 11, 11, 11, 11, 11, 13, 13, 14, 14, 14]) + template_x = np.array( + [ + 5, + 8, + 11, + 3, + 13, + 2, + 5, + 8, + 11, + 14, + 2, + 5, + 8, + 11, + 14, + 2, + 5, + 8, + 11, + 14, + 3, + 13, + 5, + 8, + 11, + ] + ) + template_y = np.array( + [ + 2, + 2, + 2, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 8, + 8, + 8, + 8, + 8, + 11, + 11, + 11, + 11, + 11, + 13, + 13, + 14, + 14, + 14, + ] + ) # Create 17x17 array of universes assembly.universes = np.tile(fuel_pin_universe, (17, 17)) assembly.universes[template_x, template_y] = guide_tube_universe # Create root Cell - root_cell = openmc.Cell(name='root cell', fill=assembly) + root_cell = openmc.Cell(name="root cell", fill=assembly) root_cell.region = +min_x & -max_x & +min_y & -max_y # Create root Universe - model.geometry.root_universe = openmc.Universe(name='root universe') + model.geometry.root_universe = openmc.Universe(name="root universe") model.geometry.root_universe.add_cell(root_cell) model.settings.batches = 10 model.settings.inactive = 5 model.settings.particles = 100 model.settings.source = openmc.IndependentSource( - space=openmc.stats.Box([-pitch/2, -pitch/2, -1], - [pitch/2, pitch/2, 1]), - constraints={'fissionable': True} + space=openmc.stats.Box([-pitch / 2, -pitch / 2, -1], [pitch / 2, pitch / 2, 1]), + constraints={"fissionable": True}, ) plot = openmc.Plot() plot.origin = (0.0, 0.0, 0) plot.width = (21.42, 21.42) plot.pixels = (300, 300) - plot.color_by = 'material' + plot.color_by = "material" model.plots.append(plot) return model -def slab_mg(num_regions=1, mat_names=None, mgxslib_name='2g.h5') -> openmc.Model: +def slab_mg(num_regions=1, mat_names=None, mgxslib_name="2g.h5") -> openmc.Model: """Create a 1D slab model. Parameters @@ -575,24 +688,24 @@ def slab_mg(num_regions=1, mat_names=None, mgxslib_name='2g.h5') -> openmc.Model """ - openmc.check_type('num_regions', num_regions, Integral) - openmc.check_greater_than('num_regions', num_regions, 0) + openmc.check_type("num_regions", num_regions, Integral) + openmc.check_greater_than("num_regions", num_regions, 0) if mat_names is not None: - openmc.check_length('mat_names', mat_names, num_regions) - openmc.check_iterable_type('mat_names', mat_names, str) + openmc.check_length("mat_names", mat_names, num_regions) + openmc.check_iterable_type("mat_names", mat_names, str) else: mat_names = [] for i in range(num_regions): - mat_names.append('mat_' + str(i + 1)) + mat_names.append("mat_" + str(i + 1)) # # Make Materials materials_file = openmc.Materials() macros = [] mats = [] for i in range(len(mat_names)): - macros.append(openmc.Macroscopic('mat_' + str(i + 1))) + macros.append(openmc.Macroscopic("mat_" + str(i + 1))) mats.append(openmc.Material(name=mat_names[i])) - mats[-1].set_density('macro', 1.0) + mats[-1].set_density("macro", 1.0) mats[-1].add_macroscopic(macros[-1]) materials_file += mats @@ -602,17 +715,17 @@ def slab_mg(num_regions=1, mat_names=None, mgxslib_name='2g.h5') -> openmc.Model # # Make Geometry rad_outer = 929.45 # Set a cell boundary to exist for every material above (exclude the 0) - rads = np.linspace(0., rad_outer, len(mats) + 1, endpoint=True)[1:] + rads = np.linspace(0.0, rad_outer, len(mats) + 1, endpoint=True)[1:] # Instantiate Universe - root = openmc.Universe(universe_id=0, name='root universe') + root = openmc.Universe(universe_id=0, name="root universe") cells = [] surfs = [] - surfs.append(openmc.XPlane(x0=0., boundary_type='reflective')) + surfs.append(openmc.XPlane(x0=0.0, boundary_type="reflective")) for r, rad in enumerate(rads): if r == len(rads) - 1: - surfs.append(openmc.XPlane(x0=rad, boundary_type='vacuum')) + surfs.append(openmc.XPlane(x0=rad, boundary_type="vacuum")) else: surfs.append(openmc.XPlane(x0=rad)) @@ -620,7 +733,7 @@ def slab_mg(num_regions=1, mat_names=None, mgxslib_name='2g.h5') -> openmc.Model cells = [] for c in range(len(surfs) - 1): cells.append(openmc.Cell()) - cells[-1].region = (+surfs[c] & -surfs[c + 1]) + cells[-1].region = +surfs[c] & -surfs[c + 1] cells[-1].fill = mats[c] # Register Cells with Universe @@ -632,19 +745,19 @@ def slab_mg(num_regions=1, mat_names=None, mgxslib_name='2g.h5') -> openmc.Model # # Make Settings # Instantiate a Settings object, set all runtime parameters settings_file = openmc.Settings() - settings_file.energy_mode = 'multi-group' - settings_file.tabular_legendre = {'enable': False} + settings_file.energy_mode = "multi-group" + settings_file.tabular_legendre = {"enable": False} settings_file.batches = 10 settings_file.inactive = 5 settings_file.particles = 1000 # Build source distribution - INF = 1000. - bounds = [0., -INF, -INF, rads[0], INF, INF] + INF = 1000.0 + bounds = [0.0, -INF, -INF, rads[0], INF, INF] uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:]) settings_file.source = openmc.IndependentSource(space=uniform_dist) - settings_file.output = {'summary': False} + settings_file.output = {"summary": False} model = openmc.Model() model.geometry = geometry_file @@ -678,75 +791,230 @@ def random_ray_lattice() -> openmc.Model: groups = openmc.mgxs.EnergyGroups(group_edges) # Instantiate the 7-group (C5G7) cross section data - uo2_xsdata = openmc.XSdata('UO2', groups) + uo2_xsdata = openmc.XSdata("UO2", groups) uo2_xsdata.order = 0 uo2_xsdata.set_total( - [0.1779492, 0.3298048, 0.4803882, 0.5543674, 0.3118013, 0.3951678, - 0.5644058]) - uo2_xsdata.set_absorption([8.0248e-03, 3.7174e-03, 2.6769e-02, 9.6236e-02, - 3.0020e-02, 1.1126e-01, 2.8278e-01]) + [0.1779492, 0.3298048, 0.4803882, 0.5543674, 0.3118013, 0.3951678, 0.5644058] + ) + uo2_xsdata.set_absorption( + [ + 8.0248e-03, + 3.7174e-03, + 2.6769e-02, + 9.6236e-02, + 3.0020e-02, + 1.1126e-01, + 2.8278e-01, + ] + ) scatter_matrix = np.array( - [[[0.1275370, 0.0423780, 0.0000094, 0.0000000, 0.0000000, 0.0000000, 0.0000000], - [0.0000000, 0.3244560, 0.0016314, 0.0000000, - 0.0000000, 0.0000000, 0.0000000], - [0.0000000, 0.0000000, 0.4509400, 0.0026792, - 0.0000000, 0.0000000, 0.0000000], - [0.0000000, 0.0000000, 0.0000000, 0.4525650, - 0.0055664, 0.0000000, 0.0000000], - [0.0000000, 0.0000000, 0.0000000, 0.0001253, - 0.2714010, 0.0102550, 0.0000000], - [0.0000000, 0.0000000, 0.0000000, 0.0000000, - 0.0012968, 0.2658020, 0.0168090], - [0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0085458, 0.2730800]]]) + [ + [ + [ + 0.1275370, + 0.0423780, + 0.0000094, + 0.0000000, + 0.0000000, + 0.0000000, + 0.0000000, + ], + [ + 0.0000000, + 0.3244560, + 0.0016314, + 0.0000000, + 0.0000000, + 0.0000000, + 0.0000000, + ], + [ + 0.0000000, + 0.0000000, + 0.4509400, + 0.0026792, + 0.0000000, + 0.0000000, + 0.0000000, + ], + [ + 0.0000000, + 0.0000000, + 0.0000000, + 0.4525650, + 0.0055664, + 0.0000000, + 0.0000000, + ], + [ + 0.0000000, + 0.0000000, + 0.0000000, + 0.0001253, + 0.2714010, + 0.0102550, + 0.0000000, + ], + [ + 0.0000000, + 0.0000000, + 0.0000000, + 0.0000000, + 0.0012968, + 0.2658020, + 0.0168090, + ], + [ + 0.0000000, + 0.0000000, + 0.0000000, + 0.0000000, + 0.0000000, + 0.0085458, + 0.2730800, + ], + ] + ] + ) scatter_matrix = np.rollaxis(scatter_matrix, 0, 3) uo2_xsdata.set_scatter_matrix(scatter_matrix) - uo2_xsdata.set_fission([7.21206e-03, 8.19301e-04, 6.45320e-03, - 1.85648e-02, 1.78084e-02, 8.30348e-02, - 2.16004e-01]) - uo2_xsdata.set_nu_fission([2.005998e-02, 2.027303e-03, 1.570599e-02, - 4.518301e-02, 4.334208e-02, 2.020901e-01, - 5.257105e-01]) - uo2_xsdata.set_chi([5.8791e-01, 4.1176e-01, 3.3906e-04, 1.1761e-07, 0.0000e+00, - 0.0000e+00, 0.0000e+00]) - - h2o_xsdata = openmc.XSdata('LWTR', groups) + uo2_xsdata.set_fission( + [ + 7.21206e-03, + 8.19301e-04, + 6.45320e-03, + 1.85648e-02, + 1.78084e-02, + 8.30348e-02, + 2.16004e-01, + ] + ) + uo2_xsdata.set_nu_fission( + [ + 2.005998e-02, + 2.027303e-03, + 1.570599e-02, + 4.518301e-02, + 4.334208e-02, + 2.020901e-01, + 5.257105e-01, + ] + ) + uo2_xsdata.set_chi( + [ + 5.8791e-01, + 4.1176e-01, + 3.3906e-04, + 1.1761e-07, + 0.0000e00, + 0.0000e00, + 0.0000e00, + ] + ) + + h2o_xsdata = openmc.XSdata("LWTR", groups) h2o_xsdata.order = 0 - h2o_xsdata.set_total([0.15920605, 0.412969593, 0.59030986, 0.58435, - 0.718, 1.2544497, 2.650379]) - h2o_xsdata.set_absorption([6.0105e-04, 1.5793e-05, 3.3716e-04, - 1.9406e-03, 5.7416e-03, 1.5001e-02, - 3.7239e-02]) + h2o_xsdata.set_total( + [0.15920605, 0.412969593, 0.59030986, 0.58435, 0.718, 1.2544497, 2.650379] + ) + h2o_xsdata.set_absorption( + [ + 6.0105e-04, + 1.5793e-05, + 3.3716e-04, + 1.9406e-03, + 5.7416e-03, + 1.5001e-02, + 3.7239e-02, + ] + ) scatter_matrix = np.array( - [[[0.0444777, 0.1134000, 0.0007235, 0.0000037, 0.0000001, 0.0000000, 0.0000000], - [0.0000000, 0.2823340, 0.1299400, 0.0006234, - 0.0000480, 0.0000074, 0.0000010], - [0.0000000, 0.0000000, 0.3452560, 0.2245700, - 0.0169990, 0.0026443, 0.0005034], - [0.0000000, 0.0000000, 0.0000000, 0.0910284, - 0.4155100, 0.0637320, 0.0121390], - [0.0000000, 0.0000000, 0.0000000, 0.0000714, - 0.1391380, 0.5118200, 0.0612290], - [0.0000000, 0.0000000, 0.0000000, 0.0000000, - 0.0022157, 0.6999130, 0.5373200], - [0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.1324400, 2.4807000]]]) + [ + [ + [ + 0.0444777, + 0.1134000, + 0.0007235, + 0.0000037, + 0.0000001, + 0.0000000, + 0.0000000, + ], + [ + 0.0000000, + 0.2823340, + 0.1299400, + 0.0006234, + 0.0000480, + 0.0000074, + 0.0000010, + ], + [ + 0.0000000, + 0.0000000, + 0.3452560, + 0.2245700, + 0.0169990, + 0.0026443, + 0.0005034, + ], + [ + 0.0000000, + 0.0000000, + 0.0000000, + 0.0910284, + 0.4155100, + 0.0637320, + 0.0121390, + ], + [ + 0.0000000, + 0.0000000, + 0.0000000, + 0.0000714, + 0.1391380, + 0.5118200, + 0.0612290, + ], + [ + 0.0000000, + 0.0000000, + 0.0000000, + 0.0000000, + 0.0022157, + 0.6999130, + 0.5373200, + ], + [ + 0.0000000, + 0.0000000, + 0.0000000, + 0.0000000, + 0.0000000, + 0.1324400, + 2.4807000, + ], + ] + ] + ) scatter_matrix = np.rollaxis(scatter_matrix, 0, 3) h2o_xsdata.set_scatter_matrix(scatter_matrix) mg_cross_sections = openmc.MGXSLibrary(groups) mg_cross_sections.add_xsdatas([uo2_xsdata, h2o_xsdata]) - mg_cross_sections.export_to_hdf5('mgxs.h5') + mg_cross_sections.export_to_hdf5("mgxs.h5") ########################################################################### # Create materials for the problem # Instantiate some Materials and register the appropriate macroscopic data - uo2 = openmc.Material(name='UO2 fuel') - uo2.set_density('macro', 1.0) - uo2.add_macroscopic('UO2') + uo2 = openmc.Material(name="UO2 fuel") + uo2.set_density("macro", 1.0) + uo2.add_macroscopic("UO2") - water = openmc.Material(name='Water') - water.set_density('macro', 1.0) - water.add_macroscopic('LWTR') + water = openmc.Material(name="Water") + water.set_density("macro", 1.0) + water.add_macroscopic("LWTR") # Instantiate a Materials collection and export to XML materials = openmc.Materials([uo2, water]) @@ -761,61 +1029,68 @@ def random_ray_lattice() -> openmc.Model: pitch = 1.26 # Create a surface for the fuel outer radius - fuel_or = openmc.ZCylinder(r=0.54, name='Fuel OR') - inner_ring_a = openmc.ZCylinder(r=0.33, name='inner ring a') - inner_ring_b = openmc.ZCylinder(r=0.45, name='inner ring b') - outer_ring_a = openmc.ZCylinder(r=0.60, name='outer ring a') - outer_ring_b = openmc.ZCylinder(r=0.69, name='outer ring b') + fuel_or = openmc.ZCylinder(r=0.54, name="Fuel OR") + inner_ring_a = openmc.ZCylinder(r=0.33, name="inner ring a") + inner_ring_b = openmc.ZCylinder(r=0.45, name="inner ring b") + outer_ring_a = openmc.ZCylinder(r=0.60, name="outer ring a") + outer_ring_b = openmc.ZCylinder(r=0.69, name="outer ring b") # Instantiate Cells - fuel_a = openmc.Cell(fill=uo2, region=-inner_ring_a, name='fuel inner a') - fuel_b = openmc.Cell(fill=uo2, region=+inner_ring_a & - - inner_ring_b, name='fuel inner b') - fuel_c = openmc.Cell(fill=uo2, region=+inner_ring_b & - - fuel_or, name='fuel inner c') + fuel_a = openmc.Cell(fill=uo2, region=-inner_ring_a, name="fuel inner a") + fuel_b = openmc.Cell( + fill=uo2, region=+inner_ring_a & -inner_ring_b, name="fuel inner b" + ) + fuel_c = openmc.Cell(fill=uo2, region=+inner_ring_b & -fuel_or, name="fuel inner c") moderator_a = openmc.Cell( - fill=water, region=+fuel_or & -outer_ring_a, name='moderator inner a') + fill=water, region=+fuel_or & -outer_ring_a, name="moderator inner a" + ) moderator_b = openmc.Cell( - fill=water, region=+outer_ring_a & -outer_ring_b, name='moderator outer b') + fill=water, region=+outer_ring_a & -outer_ring_b, name="moderator outer b" + ) moderator_c = openmc.Cell( - fill=water, region=+outer_ring_b, name='moderator outer c') + fill=water, region=+outer_ring_b, name="moderator outer c" + ) # Create pincell universe pincell_base = openmc.Universe() # Register Cells with Universe pincell_base.add_cells( - [fuel_a, fuel_b, fuel_c, moderator_a, moderator_b, moderator_c]) + [fuel_a, fuel_b, fuel_c, moderator_a, moderator_b, moderator_c] + ) # Create planes for azimuthal sectors azimuthal_planes = [] for i in range(8): angle = 2 * i * openmc.pi / 8 normal_vector = (-openmc.sin(angle), openmc.cos(angle), 0) - azimuthal_planes.append(openmc.Plane( - a=normal_vector[0], b=normal_vector[1], c=normal_vector[2], d=0)) + azimuthal_planes.append( + openmc.Plane( + a=normal_vector[0], b=normal_vector[1], c=normal_vector[2], d=0 + ) + ) # Create a cell for each azimuthal sector azimuthal_cells = [] for i in range(8): - azimuthal_cell = openmc.Cell(name=f'azimuthal_cell_{i}') + azimuthal_cell = openmc.Cell(name=f"azimuthal_cell_{i}") azimuthal_cell.fill = pincell_base - azimuthal_cell.region = +azimuthal_planes[i] & -azimuthal_planes[(i+1) % 8] + azimuthal_cell.region = +azimuthal_planes[i] & -azimuthal_planes[(i + 1) % 8] azimuthal_cells.append(azimuthal_cell) # Create a geometry with the azimuthal universes - pincell = openmc.Universe(cells=azimuthal_cells, name='pincell') + pincell = openmc.Universe(cells=azimuthal_cells, name="pincell") ######################################## # Define a moderator lattice universe - moderator_infinite = openmc.Cell(fill=water, name='moderator infinite') + moderator_infinite = openmc.Cell(fill=water, name="moderator infinite") mu = openmc.Universe() mu.add_cells([moderator_infinite]) lattice = openmc.RectLattice() - lattice.lower_left = [-pitch/2.0, -pitch/2.0] - lattice.pitch = [pitch/10.0, pitch/10.0] + lattice.lower_left = [-pitch / 2.0, -pitch / 2.0] + lattice.pitch = [pitch / 10.0, pitch / 10.0] lattice.universes = np.full((10, 10), mu) mod_lattice_cell = openmc.Cell(fill=lattice) @@ -829,17 +1104,15 @@ def random_ray_lattice() -> openmc.Model: lattice2x2 = openmc.RectLattice() lattice2x2.lower_left = (-pitch, -pitch) lattice2x2.pitch = (pitch, pitch) - lattice2x2.universes = [ - [pincell, pincell], - [pincell, mod_lattice_uni] - ] + lattice2x2.universes = [[pincell, pincell], [pincell, mod_lattice_uni]] ######################################## # Define cell containing lattice and other stuff box = openmc.model.RectangularPrism( - pitch*2, pitch*2, boundary_type='reflective') + pitch * 2, pitch * 2, boundary_type="reflective" + ) - assembly = openmc.Cell(fill=lattice2x2, region=-box, name='assembly') + assembly = openmc.Cell(fill=lattice2x2, region=-box, name="assembly") # Create a geometry with the top-level cell geometry = openmc.Geometry([assembly]) @@ -860,10 +1133,10 @@ def random_ray_lattice() -> openmc.Model: uniform_dist = openmc.stats.Box(lower_left, upper_right) rr_source = openmc.IndependentSource(space=uniform_dist) - settings.random_ray['distance_active'] = 100.0 - settings.random_ray['distance_inactive'] = 20.0 - settings.random_ray['ray_source'] = rr_source - settings.random_ray['volume_normalized_flux_tallies'] = True + settings.random_ray["distance_active"] = 100.0 + settings.random_ray["distance_inactive"] = 20.0 + settings.random_ray["ray_source"] = rr_source + settings.random_ray["volume_normalized_flux_tallies"] = True ########################################################################### # Define tallies @@ -884,8 +1157,8 @@ def random_ray_lattice() -> openmc.Model: # Now use the mesh filter in a tally and indicate what scores are desired tally = openmc.Tally(name="Mesh tally") tally.filters = [mesh_filter, energy_filter] - tally.scores = ['flux', 'fission', 'nu-fission'] - tally.estimator = 'analog' + tally.scores = ["flux", "fission", "nu-fission"] + tally.estimator = "analog" # Instantiate a Tallies collection and export to XML tallies = openmc.Tallies([tally]) @@ -926,9 +1199,9 @@ def fill_cube(N, n_1, n_2, fill_1, fill_2, fill_3): for i in range(N): for j in range(N): for k in range(N): - if i < n_1 and j >= (N-n_1) and k < n_1: + if i < n_1 and j >= (N - n_1) and k < n_1: cube[i][j][k] = fill_1 - elif i < n_2 and j >= (N-n_2) and k < n_2: + elif i < n_2 and j >= (N - n_2) and k < n_2: cube[i][j][k] = fill_2 else: cube[i][j][k] = fill_3 @@ -943,56 +1216,58 @@ def fill_cube(N, n_1, n_2, fill_1, fill_2, fill_3): void_sigma_a = 4.0e-6 void_sigma_s = 3.0e-4 - void_mat_data = openmc.XSdata('void', groups) + void_mat_data = openmc.XSdata("void", groups) void_mat_data.order = 0 void_mat_data.set_total([void_sigma_a + void_sigma_s]) void_mat_data.set_absorption([void_sigma_a]) - void_mat_data.set_scatter_matrix( - np.rollaxis(np.array([[[void_sigma_s]]]), 0, 3)) + void_mat_data.set_scatter_matrix(np.rollaxis(np.array([[[void_sigma_s]]]), 0, 3)) absorber_sigma_a = 0.75 absorber_sigma_s = 0.25 - absorber_mat_data = openmc.XSdata('absorber', groups) + absorber_mat_data = openmc.XSdata("absorber", groups) absorber_mat_data.order = 0 absorber_mat_data.set_total([absorber_sigma_a + absorber_sigma_s]) absorber_mat_data.set_absorption([absorber_sigma_a]) absorber_mat_data.set_scatter_matrix( - np.rollaxis(np.array([[[absorber_sigma_s]]]), 0, 3)) + np.rollaxis(np.array([[[absorber_sigma_s]]]), 0, 3) + ) multiplier = 0.1 source_sigma_a = void_sigma_a * multiplier source_sigma_s = void_sigma_s * multiplier - source_mat_data = openmc.XSdata('source', groups) + source_mat_data = openmc.XSdata("source", groups) source_mat_data.order = 0 source_mat_data.set_total([source_sigma_a + source_sigma_s]) source_mat_data.set_absorption([source_sigma_a]) source_mat_data.set_scatter_matrix( - np.rollaxis(np.array([[[source_sigma_s]]]), 0, 3)) + np.rollaxis(np.array([[[source_sigma_s]]]), 0, 3) + ) mg_cross_sections_file = openmc.MGXSLibrary(groups) mg_cross_sections_file.add_xsdatas( - [source_mat_data, void_mat_data, absorber_mat_data]) + [source_mat_data, void_mat_data, absorber_mat_data] + ) mg_cross_sections_file.export_to_hdf5() ########################################################################### # Create materials for the problem # Instantiate some Macroscopic Data - source_data = openmc.Macroscopic('source') - void_data = openmc.Macroscopic('void') - absorber_data = openmc.Macroscopic('absorber') + source_data = openmc.Macroscopic("source") + void_data = openmc.Macroscopic("void") + absorber_data = openmc.Macroscopic("absorber") # Instantiate some Materials and register the appropriate Macroscopic objects - source_mat = openmc.Material(name='source') - source_mat.set_density('macro', 1.0) + source_mat = openmc.Material(name="source") + source_mat.set_density("macro", 1.0) source_mat.add_macroscopic(source_data) - void_mat = openmc.Material(name='void') - void_mat.set_density('macro', 1.0) + void_mat = openmc.Material(name="void") + void_mat.set_density("macro", 1.0) void_mat.add_macroscopic(void_data) - absorber_mat = openmc.Material(name='absorber') - absorber_mat.set_density('macro', 1.0) + absorber_mat = openmc.Material(name="absorber") + absorber_mat.set_density("macro", 1.0) absorber_mat.add_macroscopic(absorber_data) # Instantiate a Materials collection and export to XML @@ -1002,12 +1277,11 @@ def fill_cube(N, n_1, n_2, fill_1, fill_2, fill_3): ########################################################################### # Define problem geometry - source_cell = openmc.Cell(fill=source_mat, name='infinite source region') - void_cell = openmc.Cell(fill=void_mat, name='infinite void region') - absorber_cell = openmc.Cell( - fill=absorber_mat, name='infinite absorber region') + source_cell = openmc.Cell(fill=source_mat, name="infinite source region") + void_cell = openmc.Cell(fill=void_mat, name="infinite void region") + absorber_cell = openmc.Cell(fill=absorber_mat, name="infinite absorber region") - source_universe = openmc.Universe(name='source universe') + source_universe = openmc.Universe(name="source universe") source_universe.add_cells([source_cell]) void_universe = openmc.Universe() @@ -1025,8 +1299,14 @@ def fill_cube(N, n_1, n_2, fill_1, fill_2, fill_3): n = n_base * refinement_level pitch = absorber_width / n - pattern = fill_cube(n, 1*refinement_level, 5*refinement_level, - source_universe, void_universe, absorber_universe) + pattern = fill_cube( + n, + 1 * refinement_level, + 5 * refinement_level, + source_universe, + void_universe, + absorber_universe, + ) lattice = openmc.RectLattice() lattice.lower_left = [0.0, 0.0, 0.0] @@ -1038,19 +1318,22 @@ def fill_cube(N, n_1, n_2, fill_1, fill_2, fill_3): lattice_uni = openmc.Universe() lattice_uni.add_cells([lattice_cell]) - x_low = openmc.XPlane(x0=0.0, boundary_type='reflective') - x_high = openmc.XPlane(x0=absorber_width, boundary_type='vacuum') + x_low = openmc.XPlane(x0=0.0, boundary_type="reflective") + x_high = openmc.XPlane(x0=absorber_width, boundary_type="vacuum") - y_low = openmc.YPlane(y0=0.0, boundary_type='reflective') - y_high = openmc.YPlane(y0=absorber_width, boundary_type='vacuum') + y_low = openmc.YPlane(y0=0.0, boundary_type="reflective") + y_high = openmc.YPlane(y0=absorber_width, boundary_type="vacuum") - z_low = openmc.ZPlane(z0=0.0, boundary_type='reflective') - z_high = openmc.ZPlane(z0=absorber_width, boundary_type='vacuum') + z_low = openmc.ZPlane(z0=0.0, boundary_type="reflective") + z_high = openmc.ZPlane(z0=absorber_width, boundary_type="vacuum") - full_domain = openmc.Cell(fill=lattice_uni, region=+x_low & - - x_high & +y_low & -y_high & +z_low & -z_high, name='full domain') + full_domain = openmc.Cell( + fill=lattice_uni, + region=+x_low & -x_high & +y_low & -y_high & +z_low & -z_high, + name="full domain", + ) - root = openmc.Universe(name='root universe') + root = openmc.Universe(name="root universe") root.add_cell(full_domain) # Create a geometry with the two cells and export to XML @@ -1065,19 +1348,20 @@ def fill_cube(N, n_1, n_2, fill_1, fill_2, fill_3): settings.inactive = 5 settings.batches = 10 settings.particles = 90 - settings.run_mode = 'fixed source' + settings.run_mode = "fixed source" # Create an initial uniform spatial source for ray integration lower_left_ray = [0.0, 0.0, 0.0] upper_right_ray = [absorber_width, absorber_width, absorber_width] uniform_dist_ray = openmc.stats.Box( - lower_left_ray, upper_right_ray, only_fissionable=False) + lower_left_ray, upper_right_ray, only_fissionable=False + ) rr_source = openmc.IndependentSource(space=uniform_dist_ray) - settings.random_ray['distance_active'] = 500.0 - settings.random_ray['distance_inactive'] = 100.0 - settings.random_ray['ray_source'] = rr_source - settings.random_ray['volume_normalized_flux_tallies'] = True + settings.random_ray["distance_active"] = 500.0 + settings.random_ray["distance_inactive"] = 100.0 + settings.random_ray["ray_source"] = rr_source + settings.random_ray["volume_normalized_flux_tallies"] = True # Create the neutron source in the bottom right of the moderator # Good - fast group appears largest (besides most thermal) @@ -1085,32 +1369,35 @@ def fill_cube(N, n_1, n_2, fill_1, fill_2, fill_3): midpoints = [100.0] energy_distribution = openmc.stats.Discrete(x=midpoints, p=strengths) - source = openmc.IndependentSource(energy=energy_distribution, constraints={ - 'domains': [source_universe]}, strength=3.14) + source = openmc.IndependentSource( + energy=energy_distribution, + constraints={"domains": [source_universe]}, + strength=3.14, + ) settings.source = [source] ########################################################################### # Define tallies - estimator = 'tracklength' + estimator = "tracklength" absorber_filter = openmc.MaterialFilter(absorber_mat) absorber_tally = openmc.Tally(name="Absorber Tally") absorber_tally.filters = [absorber_filter] - absorber_tally.scores = ['flux'] + absorber_tally.scores = ["flux"] absorber_tally.estimator = estimator void_filter = openmc.MaterialFilter(void_mat) void_tally = openmc.Tally(name="Void Tally") void_tally.filters = [void_filter] - void_tally.scores = ['flux'] + void_tally.scores = ["flux"] void_tally.estimator = estimator source_filter = openmc.MaterialFilter(source_mat) source_tally = openmc.Tally(name="Source Tally") source_tally.filters = [source_filter] - source_tally.scores = ['flux'] + source_tally.scores = ["flux"] source_tally.estimator = estimator # Instantiate a Tallies collection and export to XML diff --git a/openmc/executor.py b/openmc/executor.py index aacc48b3fac..4f319e03ecb 100644 --- a/openmc/executor.py +++ b/openmc/executor.py @@ -7,10 +7,19 @@ from .plots import _get_plot_image -def _process_CLI_arguments(volume=False, geometry_debug=False, particles=None, - plot=False, restart_file=None, threads=None, - tracks=False, event_based=None, - openmc_exec='openmc', mpi_args=None, path_input=None): +def _process_CLI_arguments( + volume=False, + geometry_debug=False, + particles=None, + plot=False, + restart_file=None, + threads=None, + tracks=False, + event_based=None, + openmc_exec="openmc", + mpi_args=None, + path_input=None, +): """Converts user-readable flags in to command-line arguments to be run with the OpenMC executable via subprocess. @@ -59,29 +68,29 @@ def _process_CLI_arguments(volume=False, geometry_debug=False, particles=None, args = [openmc_exec] if volume: - args.append('--volume') + args.append("--volume") if isinstance(particles, Integral) and particles > 0: - args += ['-n', str(particles)] + args += ["-n", str(particles)] if isinstance(threads, Integral) and threads > 0: - args += ['-s', str(threads)] + args += ["-s", str(threads)] if geometry_debug: - args.append('-g') + args.append("-g") if event_based is not None: if event_based: - args.append('-e') + args.append("-e") if isinstance(restart_file, (str, os.PathLike)): - args += ['-r', str(restart_file)] + args += ["-r", str(restart_file)] if tracks: - args.append('-t') + args.append("-t") if plot: - args.append('-p') + args.append("-p") if mpi_args is not None: args = mpi_args + args @@ -94,8 +103,13 @@ def _process_CLI_arguments(volume=False, geometry_debug=False, particles=None, def _run(args, output, cwd): # Launch a subprocess - p = subprocess.Popen(args, cwd=cwd, stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, universal_newlines=True) + p = subprocess.Popen( + args, + cwd=cwd, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True, + ) # Capture and re-print OpenMC output in real-time lines = [] @@ -108,24 +122,24 @@ def _run(args, output, cwd): lines.append(line) if output: # If user requested output, print to screen - print(line, end='') + print(line, end="") # Raise an exception if return status is non-zero if p.returncode != 0: # Get error message from output and simplify whitespace - output = ''.join(lines) - if 'ERROR: ' in output: - _, _, error_msg = output.partition('ERROR: ') - elif 'what()' in output: - _, _, error_msg = output.partition('what(): ') + output = "".join(lines) + 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()) + error_msg = "OpenMC aborted unexpectedly." + error_msg = " ".join(error_msg.split()) raise RuntimeError(error_msg) -def plot_geometry(output=True, openmc_exec='openmc', cwd='.', path_input=None): +def plot_geometry(output=True, openmc_exec="openmc", cwd=".", path_input=None): """Run OpenMC in plotting mode Parameters @@ -148,13 +162,13 @@ def plot_geometry(output=True, openmc_exec='openmc', cwd='.', path_input=None): If the `openmc` executable returns a non-zero status """ - args = [openmc_exec, '-p'] + args = [openmc_exec, "-p"] if path_input is not None: args += [path_input] _run(args, output, cwd) -def plot_inline(plots, openmc_exec='openmc', cwd='.', path_input=None): +def plot_inline(plots, openmc_exec="openmc", cwd=".", path_input=None): """Display plots inline in a Jupyter notebook. .. versionchanged:: 0.13.0 @@ -198,9 +212,14 @@ def plot_inline(plots, openmc_exec='openmc', cwd='.', path_input=None): display(*images) -def calculate_volumes(threads=None, output=True, cwd='.', - openmc_exec='openmc', mpi_args=None, - path_input=None): +def calculate_volumes( + threads=None, + output=True, + cwd=".", + openmc_exec="openmc", + mpi_args=None, + path_input=None, +): """Run stochastic volume calculations in OpenMC. This function runs OpenMC in stochastic volume calculation mode. To specify @@ -247,17 +266,30 @@ def calculate_volumes(threads=None, output=True, cwd='.', """ - args = _process_CLI_arguments(volume=True, threads=threads, - openmc_exec=openmc_exec, mpi_args=mpi_args, - path_input=path_input) + args = _process_CLI_arguments( + volume=True, + threads=threads, + openmc_exec=openmc_exec, + mpi_args=mpi_args, + path_input=path_input, + ) _run(args, output, cwd) -def run(particles=None, threads=None, geometry_debug=False, - restart_file=None, tracks=False, output=True, cwd='.', - openmc_exec='openmc', mpi_args=None, event_based=False, - path_input=None): +def run( + particles=None, + threads=None, + geometry_debug=False, + restart_file=None, + tracks=False, + output=True, + cwd=".", + openmc_exec="openmc", + mpi_args=None, + event_based=False, + path_input=None, +): """Run an OpenMC simulation. Parameters @@ -306,9 +338,16 @@ def run(particles=None, threads=None, geometry_debug=False, """ args = _process_CLI_arguments( - volume=False, geometry_debug=geometry_debug, particles=particles, - restart_file=restart_file, threads=threads, tracks=tracks, - event_based=event_based, openmc_exec=openmc_exec, mpi_args=mpi_args, - path_input=path_input) + volume=False, + geometry_debug=geometry_debug, + particles=particles, + restart_file=restart_file, + threads=threads, + tracks=tracks, + event_based=event_based, + openmc_exec=openmc_exec, + mpi_args=mpi_args, + path_input=path_input, + ) _run(args, output, cwd) diff --git a/openmc/filter.py b/openmc/filter.py index b5926af5ad9..a4c6cb06274 100644 --- a/openmc/filter.py +++ b/openmc/filter.py @@ -21,20 +21,50 @@ _FILTER_TYPES = ( - 'universe', 'material', 'cell', 'cellborn', 'surface', 'mesh', 'energy', - 'energyout', 'mu', 'musurface', 'polar', 'azimuthal', 'distribcell', 'delayedgroup', - 'energyfunction', 'cellfrom', 'materialfrom', 'legendre', 'spatiallegendre', - 'sphericalharmonics', 'zernike', 'zernikeradial', 'particle', 'cellinstance', - 'collision', 'time' + "universe", + "material", + "cell", + "cellborn", + "surface", + "mesh", + "energy", + "energyout", + "mu", + "musurface", + "polar", + "azimuthal", + "distribcell", + "delayedgroup", + "energyfunction", + "cellfrom", + "materialfrom", + "legendre", + "spatiallegendre", + "sphericalharmonics", + "zernike", + "zernikeradial", + "particle", + "cellinstance", + "collision", + "time", ) _CURRENT_NAMES = ( - 'x-min out', 'x-min in', 'x-max out', 'x-max in', - 'y-min out', 'y-min in', 'y-max out', 'y-max in', - 'z-min out', 'z-min in', 'z-max out', 'z-max in' + "x-min out", + "x-min in", + "x-max out", + "x-max in", + "y-min out", + "y-min in", + "y-max out", + "y-max in", + "z-min out", + "z-min in", + "z-max out", + "z-max in", ) -_PARTICLES = {'neutron', 'photon', 'electron', 'positron'} +_PARTICLES = {"neutron", "photon", "electron", "positron"} class FilterMeta(ABCMeta): @@ -42,12 +72,12 @@ class FilterMeta(ABCMeta): def __new__(cls, name, bases, namespace, **kwargs): # Check the class name. - required_suffix = 'Filter' + required_suffix = "Filter" if not name.endswith(required_suffix): raise ValueError("All filter class names must end with 'Filter'") # Create a 'short_name' attribute that removes the 'Filter' suffix. - namespace['short_name'] = name[:-len(required_suffix)] + namespace["short_name"] = name[: -len(required_suffix)] # Subclass methods can sort of inherit the docstring of parent class # methods. If a function is defined without a docstring, most (all?) @@ -61,8 +91,7 @@ def __new__(cls, name, bases, namespace, **kwargs): for func_name in namespace: if func_name in Filter.__dict__: # Inherit the docstring from Filter if not defined. - if isinstance(namespace[func_name], - (classmethod, staticmethod)): + if isinstance(namespace[func_name], (classmethod, staticmethod)): new_doc = namespace[func_name].__func__.__doc__ old_doc = Filter.__dict__[func_name].__func__.__doc__ if new_doc is None and old_doc is not None: @@ -125,10 +154,10 @@ def __eq__(self, other): def __gt__(self, other): if type(self) is not type(other): - if self.short_name in _FILTER_TYPES and \ - other.short_name in _FILTER_TYPES: - delta = _FILTER_TYPES.index(self.short_name) - \ - _FILTER_TYPES.index(other.short_name) + if self.short_name in _FILTER_TYPES and other.short_name in _FILTER_TYPES: + delta = _FILTER_TYPES.index(self.short_name) - _FILTER_TYPES.index( + other.short_name + ) return delta > 0 else: return False @@ -139,14 +168,14 @@ def __lt__(self, other): return not self > other def __hash__(self): - string = type(self).__name__ + '\n' - string += '{: <16}=\t{}\n'.format('\tBins', self.bins) + string = type(self).__name__ + "\n" + string += "{: <16}=\t{}\n".format("\tBins", self.bins) return hash(string) def __repr__(self): - string = type(self).__name__ + '\n' - string += '{: <16}=\t{}\n'.format('\tBins', self.bins) - string += '{: <16}=\t{}\n'.format('\tID', self.id) + string = type(self).__name__ + "\n" + string += "{: <16}=\t{}\n".format("\tBins", self.bins) + string += "{: <16}=\t{}\n".format("\tID", self.id) return string @classmethod @@ -177,23 +206,24 @@ def from_hdf5(cls, group, **kwargs): """ - filter_id = int(group.name.split('/')[-1].lstrip('filter ')) + filter_id = int(group.name.split("/")[-1].lstrip("filter ")) # If the HDF5 'type' variable matches this class's short_name, then # there is no overridden from_hdf5 method. Pass the bins to __init__. - if group['type'][()].decode() == cls.short_name.lower(): - out = cls(group['bins'][()], filter_id=filter_id) - out._num_bins = group['n_bins'][()] + if group["type"][()].decode() == cls.short_name.lower(): + out = cls(group["bins"][()], filter_id=filter_id) + out._num_bins = group["n_bins"][()] return out # Search through all subclasses and find the one matching the HDF5 # 'type'. Call that class's from_hdf5 method. for subclass in cls._recursive_subclasses(): - if group['type'][()].decode() == subclass.short_name.lower(): + if group["type"][()].decode() == subclass.short_name.lower(): return subclass.from_hdf5(group, **kwargs) - raise ValueError("Unrecognized Filter class: '" - + group['type'][()].decode() + "'") + raise ValueError( + "Unrecognized Filter class: '" + group["type"][()].decode() + "'" + ) @property def bins(self): @@ -233,12 +263,12 @@ def to_xml_element(self): XML element containing filter data """ - element = ET.Element('filter') - element.set('id', str(self.id)) - element.set('type', self.short_name.lower()) + element = ET.Element("filter") + element.set("id", str(self.id)) + element.set("type", self.short_name.lower()) - subelement = ET.SubElement(element, 'bins') - subelement.text = ' '.join(str(b) for b in self.bins) + subelement = ET.SubElement(element, "bins") + subelement.text = " ".join(str(b) for b in self.bins) return element @classmethod @@ -258,17 +288,17 @@ def from_xml_element(cls, elem, **kwargs): Filter object """ - filter_type = elem.get('type') + filter_type = elem.get("type") if filter_type is None: - filter_type = elem.find('type').text + filter_type = elem.find("type").text # If the filter type matches this class's short_name, then # there is no overridden from_xml_element method if filter_type == cls.short_name.lower(): # Get bins from element -- the default here works for any filters # that just store a list of bins that can be represented as integers - filter_id = int(elem.get('id')) - bins = [int(x) for x in get_text(elem, 'bins').split()] + filter_id = int(elem.get("id")) + bins = [int(x) for x in get_text(elem, "bins").split()] return cls(bins, filter_id=filter_id) # Search through all subclasses and find the one matching the HDF5 @@ -277,7 +307,6 @@ def from_xml_element(cls, elem, **kwargs): if filter_type == subclass.short_name.lower(): return subclass.from_xml_element(elem, **kwargs) - def can_merge(self, other): """Determine if filter can be merged with another. @@ -369,8 +398,10 @@ def get_bin_index(self, filter_bin): """ if filter_bin not in self.bins: - msg = ('Unable to get the bin index for Filter since ' - f'"{filter_bin}" is not one of the bins') + msg = ( + "Unable to get the bin index for Filter since " + f'"{filter_bin}" is not one of the bins' + ) raise ValueError(msg) if isinstance(self.bins, np.ndarray): @@ -420,30 +451,28 @@ def get_pandas_dataframe(self, data_size, stride, **kwargs): filter_bins = np.repeat(self.bins, stride) tile_factor = data_size // len(filter_bins) filter_bins = np.tile(filter_bins, tile_factor) - df = pd.concat([df, pd.DataFrame( - {self.short_name.lower(): filter_bins})]) + df = pd.concat([df, pd.DataFrame({self.short_name.lower(): filter_bins})]) return df class WithIDFilter(Filter): """Abstract parent for filters of types with IDs (Cell, Material, etc.).""" + def __init__(self, bins, filter_id=None): bins = np.atleast_1d(bins) # Make sure bins are either integers or appropriate objects - cv.check_iterable_type('filter bins', bins, - (Integral, self.expected_type)) + cv.check_iterable_type("filter bins", bins, (Integral, self.expected_type)) # Extract ID values - bins = np.array([b if isinstance(b, Integral) else b.id - for b in bins]) + bins = np.array([b if isinstance(b, Integral) else b.id for b in bins]) super().__init__(bins, filter_id) def check_bins(self, bins): # Check the bin values. for edge in bins: - cv.check_greater_than('filter bin', edge, 0, equality=True) + cv.check_greater_than("filter bin", edge, 0, equality=True) class UniverseFilter(WithIDFilter): @@ -467,6 +496,7 @@ class UniverseFilter(WithIDFilter): The number of filter bins """ + expected_type = UniverseBase @@ -491,6 +521,7 @@ class MaterialFilter(WithIDFilter): The number of filter bins """ + expected_type = Material @@ -515,6 +546,7 @@ class MaterialFromFilter(WithIDFilter): The number of filter bins """ + expected_type = Material @@ -539,6 +571,7 @@ class CellFilter(WithIDFilter): The number of filter bins """ + expected_type = Cell @@ -563,6 +596,7 @@ class CellFromFilter(WithIDFilter): The number of filter bins """ + expected_type = Cell @@ -587,14 +621,18 @@ class CellBornFilter(WithIDFilter): The number of filter bins """ + expected_type = Cell # Temporary alias for CellbornFilter def CellbornFilter(*args, **kwargs): - warnings.warn('The name of "CellbornFilter" has changed to ' - '"CellBornFilter". "CellbornFilter" will be ' - 'removed in the future.', FutureWarning) + warnings.warn( + 'The name of "CellbornFilter" has changed to ' + '"CellBornFilter". "CellbornFilter" will be ' + "removed in the future.", + FutureWarning, + ) return CellBornFilter(*args, **kwargs) @@ -631,6 +669,7 @@ class CellInstanceFilter(Filter): DistribcellFilter """ + def __init__(self, bins, filter_id=None): self.bins = bins self.id = filter_id @@ -639,8 +678,8 @@ def __init__(self, bins, filter_id=None): def bins(self, bins): pairs = np.empty((len(bins), 2), dtype=int) for i, (cell, instance) in enumerate(bins): - cv.check_type('cell', cell, (openmc.Cell, Integral)) - cv.check_type('instance', instance, Integral) + cv.check_type("cell", cell, (openmc.Cell, Integral)) + cv.check_type("instance", instance, Integral) pairs[i, 0] = cell if isinstance(cell, Integral) else cell.id pairs[i, 1] = instance self._bins = pairs @@ -677,8 +716,9 @@ def get_pandas_dataframe(self, data_size, stride, **kwargs): tile_factor = data_size // len(bins) bins = np.tile(bins, (tile_factor, 1)) - columns = pd.MultiIndex.from_product([[self.short_name.lower()], - ['cell', 'instance']]) + columns = pd.MultiIndex.from_product( + [[self.short_name.lower()], ["cell", "instance"]] + ) return pd.DataFrame(bins, columns=columns) def to_xml_element(self): @@ -690,18 +730,18 @@ def to_xml_element(self): XML element containing filter data """ - element = ET.Element('filter') - element.set('id', str(self.id)) - element.set('type', self.short_name.lower()) + element = ET.Element("filter") + element.set("id", str(self.id)) + element.set("type", self.short_name.lower()) - subelement = ET.SubElement(element, 'bins') - subelement.text = ' '.join(str(i) for i in self.bins.ravel()) + subelement = ET.SubElement(element, "bins") + subelement.text = " ".join(str(i) for i in self.bins.ravel()) return element @classmethod def from_xml_element(cls, elem, **kwargs): - filter_id = int(elem.get('id')) - bins = [int(x) for x in get_text(elem, 'bins').split()] + filter_id = int(elem.get("id")) + bins = [int(x) for x in get_text(elem, "bins").split()] cell_instances = list(zip(bins[::2], bins[1::2])) return cls(cell_instances, filter_id=filter_id) @@ -728,6 +768,7 @@ class SurfaceFilter(WithIDFilter): The number of filter bins """ + expected_type = Surface @@ -752,6 +793,7 @@ class ParticleFilter(Filter): The number of filter bins """ + def __eq__(self, other): if type(self) is not type(other): return False @@ -764,27 +806,31 @@ def __eq__(self, other): @Filter.bins.setter def bins(self, bins): - cv.check_type('bins', bins, Sequence, str) + cv.check_type("bins", bins, Sequence, str) bins = np.atleast_1d(bins) for edge in bins: - cv.check_value('filter bin', edge, _PARTICLES) + cv.check_value("filter bin", edge, _PARTICLES) self._bins = bins @classmethod def from_hdf5(cls, group, **kwargs): - if group['type'][()].decode() != cls.short_name.lower(): - raise ValueError("Expected HDF5 data for filter type '" - + cls.short_name.lower() + "' but got '" - + group['type'][()].decode() + " instead") - - particles = [b.decode() for b in group['bins'][()]] - filter_id = int(group.name.split('/')[-1].lstrip('filter ')) + if group["type"][()].decode() != cls.short_name.lower(): + raise ValueError( + "Expected HDF5 data for filter type '" + + cls.short_name.lower() + + "' but got '" + + group["type"][()].decode() + + " instead" + ) + + particles = [b.decode() for b in group["bins"][()]] + filter_id = int(group.name.split("/")[-1].lstrip("filter ")) return cls(particles, filter_id=filter_id) @classmethod def from_xml_element(cls, elem, **kwargs): - filter_id = int(elem.get('id')) - bins = get_text(elem, 'bins').split() + filter_id = int(elem.get("id")) + bins = get_text(elem, "bins").split() return cls(bins, filter_id=filter_id) @@ -821,35 +867,38 @@ def __init__(self, mesh, filter_id=None): self._translation = None def __hash__(self): - string = type(self).__name__ + '\n' - string += '{: <16}=\t{}\n'.format('\tMesh ID', self.mesh.id) + string = type(self).__name__ + "\n" + string += "{: <16}=\t{}\n".format("\tMesh ID", self.mesh.id) return hash(string) def __repr__(self): - string = type(self).__name__ + '\n' - string += '{: <16}=\t{}\n'.format('\tMesh ID', self.mesh.id) - string += '{: <16}=\t{}\n'.format('\tID', self.id) - string += '{: <16}=\t{}\n'.format('\tTranslation', self.translation) + string = type(self).__name__ + "\n" + string += "{: <16}=\t{}\n".format("\tMesh ID", self.mesh.id) + string += "{: <16}=\t{}\n".format("\tID", self.id) + string += "{: <16}=\t{}\n".format("\tTranslation", self.translation) return string @classmethod def from_hdf5(cls, group, **kwargs): - if group['type'][()].decode() != cls.short_name.lower(): - raise ValueError("Expected HDF5 data for filter type '" - + cls.short_name.lower() + "' but got '" - + group['type'][()].decode() + " instead") - - if 'meshes' not in kwargs: - raise ValueError(cls.__name__ + " requires a 'meshes' keyword " - "argument.") - - mesh_id = group['bins'][()] - mesh_obj = kwargs['meshes'][mesh_id] - filter_id = int(group.name.split('/')[-1].lstrip('filter ')) + if group["type"][()].decode() != cls.short_name.lower(): + raise ValueError( + "Expected HDF5 data for filter type '" + + cls.short_name.lower() + + "' but got '" + + group["type"][()].decode() + + " instead" + ) + + if "meshes" not in kwargs: + raise ValueError(cls.__name__ + " requires a 'meshes' keyword " "argument.") + + mesh_id = group["bins"][()] + mesh_obj = kwargs["meshes"][mesh_id] + filter_id = int(group.name.split("/")[-1].lstrip("filter ")) out = cls(mesh_obj, filter_id=filter_id) - translation = group.get('translation') + translation = group.get("translation") if translation: out.translation = translation[()] @@ -861,7 +910,7 @@ def mesh(self): @mesh.setter def mesh(self, mesh): - cv.check_type('filter mesh', mesh, openmc.MeshBase) + cv.check_type("filter mesh", mesh, openmc.MeshBase) self._mesh = mesh if isinstance(mesh, openmc.UnstructuredMesh): if mesh.has_statepoint_data: @@ -883,8 +932,8 @@ def translation(self): @translation.setter def translation(self, t): - cv.check_type('mesh filter translation', t, Iterable, Real) - cv.check_length('mesh filter translation', t, 3) + cv.check_type("mesh filter translation", t, Iterable, Real) + cv.check_length("mesh filter translation", t, 3) self._translation = np.asarray(t) def can_merge(self, other): @@ -926,7 +975,7 @@ def get_pandas_dataframe(self, data_size, stride, **kwargs): filter_dict = {} # Append mesh ID as outermost index of multi-index - mesh_key = f'mesh {self.mesh.id}' + mesh_key = f"mesh {self.mesh.id}" # Find mesh dimensions - use 3D indices for simplicity n_dim = len(self.mesh.dimension) @@ -940,16 +989,19 @@ def get_pandas_dataframe(self, data_size, stride, **kwargs): ny = nz = 1 # Generate multi-index sub-column for x-axis - filter_dict[mesh_key, 'x'] = _repeat_and_tile( - np.arange(1, nx + 1), stride, data_size) + filter_dict[mesh_key, "x"] = _repeat_and_tile( + np.arange(1, nx + 1), stride, data_size + ) # Generate multi-index sub-column for y-axis - filter_dict[mesh_key, 'y'] = _repeat_and_tile( - np.arange(1, ny + 1), nx * stride, data_size) + filter_dict[mesh_key, "y"] = _repeat_and_tile( + np.arange(1, ny + 1), nx * stride, data_size + ) # Generate multi-index sub-column for z-axis - filter_dict[mesh_key, 'z'] = _repeat_and_tile( - np.arange(1, nz + 1), nx * ny * stride, data_size) + filter_dict[mesh_key, "z"] = _repeat_and_tile( + np.arange(1, nz + 1), nx * ny * stride, data_size + ) # Initialize a Pandas DataFrame from the mesh dictionary df = pd.concat([df, pd.DataFrame(filter_dict)]) @@ -968,17 +1020,17 @@ def to_xml_element(self): element = super().to_xml_element() element[0].text = str(self.mesh.id) if self.translation is not None: - element.set('translation', ' '.join(map(str, self.translation))) + element.set("translation", " ".join(map(str, self.translation))) return element @classmethod def from_xml_element(cls, elem: ET.Element, **kwargs) -> MeshFilter: - mesh_id = int(get_text(elem, 'bins')) - mesh_obj = kwargs['meshes'][mesh_id] - filter_id = int(elem.get('id')) + mesh_id = int(get_text(elem, "bins")) + mesh_obj = kwargs["meshes"][mesh_id] + filter_id = int(elem.get("id")) out = cls(mesh_obj, filter_id=filter_id) - translation = elem.get('translation') + translation = elem.get("translation") if translation: out.translation = [float(x) for x in translation.split()] return out @@ -1041,13 +1093,15 @@ class MeshSurfaceFilter(MeshFilter): @MeshFilter.mesh.setter def mesh(self, mesh): - cv.check_type('filter mesh', mesh, openmc.MeshBase) + cv.check_type("filter mesh", mesh, openmc.MeshBase) self._mesh = mesh # Take the product of mesh indices and current names n_dim = mesh.n_dimension - self.bins = [mesh_tuple + (surf,) for mesh_tuple, surf in - product(mesh.indices, _CURRENT_NAMES[:4*n_dim])] + self.bins = [ + mesh_tuple + (surf,) + for mesh_tuple, surf in product(mesh.indices, _CURRENT_NAMES[: 4 * n_dim]) + ] def get_pandas_dataframe(self, data_size, stride, **kwargs): """Builds a Pandas DataFrame for the Filter's bins. @@ -1084,7 +1138,7 @@ def get_pandas_dataframe(self, data_size, stride, **kwargs): filter_dict = {} # Append mesh ID as outermost index of multi-index - mesh_key = f'mesh {self.mesh.id}' + mesh_key = f"mesh {self.mesh.id}" # Find mesh dimensions - use 3D indices for simplicity n_surfs = 4 * len(self.mesh.dimension) @@ -1098,22 +1152,26 @@ def get_pandas_dataframe(self, data_size, stride, **kwargs): ny = nz = 1 # Generate multi-index sub-column for x-axis - filter_dict[mesh_key, 'x'] = _repeat_and_tile( - np.arange(1, nx + 1), n_surfs * stride, data_size) + filter_dict[mesh_key, "x"] = _repeat_and_tile( + np.arange(1, nx + 1), n_surfs * stride, data_size + ) # Generate multi-index sub-column for y-axis if len(self.mesh.dimension) > 1: - filter_dict[mesh_key, 'y'] = _repeat_and_tile( - np.arange(1, ny + 1), n_surfs * nx * stride, data_size) + filter_dict[mesh_key, "y"] = _repeat_and_tile( + np.arange(1, ny + 1), n_surfs * nx * stride, data_size + ) # Generate multi-index sub-column for z-axis if len(self.mesh.dimension) > 2: - filter_dict[mesh_key, 'z'] = _repeat_and_tile( - np.arange(1, nz + 1), n_surfs * nx * ny * stride, data_size) + filter_dict[mesh_key, "z"] = _repeat_and_tile( + np.arange(1, nz + 1), n_surfs * nx * ny * stride, data_size + ) # Generate multi-index sub-column for surface - filter_dict[mesh_key, 'surf'] = _repeat_and_tile( - _CURRENT_NAMES[:n_surfs], stride, data_size) + filter_dict[mesh_key, "surf"] = _repeat_and_tile( + _CURRENT_NAMES[:n_surfs], stride, data_size + ) # Initialize a Pandas DataFrame from the mesh dictionary return pd.concat([df, pd.DataFrame(filter_dict)]) @@ -1152,8 +1210,8 @@ def __init__(self, bins, filter_id=None): def check_bins(self, bins): for x in bins: # Values should be integers - cv.check_type('filter value', x, Integral) - cv.check_greater_than('filter value', x, 0, equality=True) + cv.check_type("filter value", x, Integral) + cv.check_greater_than("filter value", x, 0, equality=True) class RealFilter(Filter): @@ -1181,6 +1239,7 @@ class RealFilter(Filter): The number of filter bins """ + def __init__(self, values, filter_id=None): self.values = np.asarray(values) self.bins = np.vstack((self.values[:-1], self.values[1:])).T @@ -1195,9 +1254,9 @@ def __gt__(self, other): return super().__gt__(other) def __repr__(self): - string = type(self).__name__ + '\n' - string += '{: <16}=\t{}\n'.format('\tValues', self.values) - string += '{: <16}=\t{}\n'.format('\tID', self.id) + string = type(self).__name__ + "\n" + string += "{: <16}=\t{}\n".format("\tValues", self.values) + string += "{: <16}=\t{}\n".format("\tID", self.id) return string @Filter.bins.setter @@ -1207,19 +1266,19 @@ def bins(self, bins): def check_bins(self, bins): for v0, v1 in bins: # Values should be real - cv.check_type('filter value', v0, Real) - cv.check_type('filter value', v1, Real) + cv.check_type("filter value", v0, Real) + cv.check_type("filter value", v1, Real) # Make sure that each tuple has values that are increasing if v1 < v0: - raise ValueError(f'Values {v0} and {v1} appear to be out of ' - 'order') + raise ValueError(f"Values {v0} and {v1} appear to be out of " "order") for pair0, pair1 in zip(bins[:-1], bins[1:]): # Successive pairs should be ordered if pair1[1] < pair0[1]: - raise ValueError(f'Values {pair1[1]} and {pair0[1]} appear to ' - 'be out of order') + raise ValueError( + f"Values {pair1[1]} and {pair0[1]} appear to " "be out of order" + ) def can_merge(self, other): if type(self) is not type(other): @@ -1274,8 +1333,10 @@ def is_subset(self, other): def get_bin_index(self, filter_bin): i = np.where(self.bins[:, 1] == filter_bin[1])[0] if len(i) == 0: - msg = ('Unable to get the bin index for Filter since ' - f'"{filter_bin}" is not one of the bins') + msg = ( + "Unable to get the bin index for Filter since " + f'"{filter_bin}" is not one of the bins' + ) raise ValueError(msg) else: return i[0] @@ -1320,13 +1381,13 @@ def get_pandas_dataframe(self, data_size, stride, **kwargs): hi_bins = np.tile(hi_bins, tile_factor) # Add the new energy columns to the DataFrame. - if hasattr(self, 'units'): - units = f' [{self.units}]' + if hasattr(self, "units"): + units = f" [{self.units}]" else: - units = '' + units = "" - df.loc[:, self.short_name.lower() + ' low' + units] = lo_bins - df.loc[:, self.short_name.lower() + ' high' + units] = hi_bins + df.loc[:, self.short_name.lower() + " low" + units] = lo_bins + df.loc[:, self.short_name.lower() + " high" + units] = hi_bins return df @@ -1340,13 +1401,13 @@ def to_xml_element(self): """ element = super().to_xml_element() - element[0].text = ' '.join(str(x) for x in self.values) + element[0].text = " ".join(str(x) for x in self.values) return element @classmethod def from_xml_element(cls, elem, **kwargs): - filter_id = int(elem.get('id')) - bins = [float(x) for x in get_text(elem, 'bins').split()] + filter_id = int(elem.get("id")) + bins = [float(x) for x in get_text(elem, "bins").split()] return cls(bins, filter_id=filter_id) @@ -1375,28 +1436,31 @@ class EnergyFilter(RealFilter): The number of filter bins """ - units = 'eV' + + units = "eV" def __init__(self, values, filter_id=None): - cv.check_length('values', values, 2) + cv.check_length("values", values, 2) super().__init__(values, filter_id) def get_bin_index(self, filter_bin): # Use lower energy bound to find index for RealFilters deltas = np.abs(self.bins[:, 1] - filter_bin[1]) / filter_bin[1] min_delta = np.min(deltas) - if min_delta < 1E-3: + if min_delta < 1e-3: return deltas.argmin() else: - msg = ('Unable to get the bin index for Filter since ' - f'"{filter_bin}" is not one of the bins') + msg = ( + "Unable to get the bin index for Filter since " + f'"{filter_bin}" is not one of the bins' + ) raise ValueError(msg) def check_bins(self, bins): super().check_bins(bins) for v0, v1 in bins: - cv.check_greater_than('filter value', v0, 0., equality=True) - cv.check_greater_than('filter value', v1, 0., equality=True) + cv.check_greater_than("filter value", v0, 0.0, equality=True) + cv.check_greater_than("filter value", v1, 0.0, equality=True) def get_tabular(self, values, **kwargs): """Create a tabulated distribution based on tally results with an energy filter @@ -1427,7 +1491,7 @@ def get_tabular(self, values, **kwargs): probability_per_ev = probabilities / np.diff(self.values) probability_per_ev = np.append(probability_per_ev, 0.0) - kwargs.setdefault('interpolation', 'histogram') + kwargs.setdefault("interpolation", "histogram") return openmc.stats.Tabular(self.values, probability_per_ev, **kwargs) @property @@ -1440,7 +1504,7 @@ def lethargy_bin_width(self): numpy.array Array of bin widths """ - return np.log10(self.bins[:, 1]/self.bins[:, 0]) + return np.log10(self.bins[:, 1] / self.bins[:, 0]) @classmethod def from_group_structure(cls, group_structure): @@ -1456,7 +1520,9 @@ def from_group_structure(cls, group_structure): """ - cv.check_value('group_structure', group_structure, openmc.mgxs.GROUP_STRUCTURES.keys()) + cv.check_value( + "group_structure", group_structure, openmc.mgxs.GROUP_STRUCTURES.keys() + ) return cls(openmc.mgxs.GROUP_STRUCTURES[group_structure.upper()]) @@ -1486,6 +1552,7 @@ class EnergyoutFilter(EnergyFilter): """ + class TimeFilter(RealFilter): """Bins tally events based on the particle's time. @@ -1513,7 +1580,8 @@ class TimeFilter(RealFilter): The number of filter bins """ - units = 's' + + units = "s" def get_bin_index(self, filter_bin): # Use lower energy bound to find index for RealFilters @@ -1522,15 +1590,17 @@ def get_bin_index(self, filter_bin): if min_delta < 1e-3: return deltas.argmin() else: - msg = ('Unable to get the bin index for Filter since ' - f'"{filter_bin}" is not one of the bins') + msg = ( + "Unable to get the bin index for Filter since " + f'"{filter_bin}" is not one of the bins' + ) raise ValueError(msg) def check_bins(self, bins): super().check_bins(bins) for v0, v1 in bins: - cv.check_greater_than('filter value', v0, 0., equality=True) - cv.check_greater_than('filter value', v1, 0., equality=True) + cv.check_greater_than("filter value", v0, 0.0, equality=True) + cv.check_greater_than("filter value", v1, 0.0, equality=True) def _path_to_levels(path): @@ -1548,23 +1618,23 @@ def _path_to_levels(path): """ # Split path into universes/cells/lattices - path_items = path.split('->') + path_items = path.split("->") # Pair together universe and cell information from the same level - idx = [i for i, item in enumerate(path_items) if item.startswith('u')] + idx = [i for i, item in enumerate(path_items) if item.startswith("u")] for i in reversed(idx): univ_id = int(path_items.pop(i)[1:]) cell_id = int(path_items.pop(i)[1:]) - path_items.insert(i, ('universe', univ_id, cell_id)) + path_items.insert(i, ("universe", univ_id, cell_id)) # Reformat lattice into tuple idx = [i for i, item in enumerate(path_items) if isinstance(item, str)] for i in idx: item = path_items.pop(i)[1:-1] - lat_id, lat_xyz = item.split('(') + lat_id, lat_xyz = item.split("(") lat_id = int(lat_id) - lat_xyz = tuple(int(x) for x in lat_xyz.split(',')) - path_items.insert(i, ('lattice', lat_id, lat_xyz)) + lat_xyz = tuple(int(x) for x in lat_xyz.split(",")) + path_items.insert(i, ("lattice", lat_id, lat_xyz)) return path_items @@ -1609,15 +1679,19 @@ def __init__(self, cell, filter_id=None): @classmethod def from_hdf5(cls, group, **kwargs): - if group['type'][()].decode() != cls.short_name.lower(): - raise ValueError("Expected HDF5 data for filter type '" - + cls.short_name.lower() + "' but got '" - + group['type'][()].decode() + " instead") + if group["type"][()].decode() != cls.short_name.lower(): + raise ValueError( + "Expected HDF5 data for filter type '" + + cls.short_name.lower() + + "' but got '" + + group["type"][()].decode() + + " instead" + ) - filter_id = int(group.name.split('/')[-1].lstrip('filter ')) + filter_id = int(group.name.split("/")[-1].lstrip("filter ")) - out = cls(group['bins'][()], filter_id=filter_id) - out._num_bins = group['n_bins'][()] + out = cls(group["bins"][()], filter_id=filter_id) + out._num_bins = group["n_bins"][()] return out @@ -1633,7 +1707,7 @@ def paths(self): @paths.setter def paths(self, paths): - cv.check_iterable_type('paths', paths, str) + cv.check_iterable_type("paths", paths, str) self._paths = paths @Filter.bins.setter @@ -1643,12 +1717,14 @@ def bins(self, bins): # Make sure there is only 1 bin. if not len(bins) == 1: - msg = (f'Unable to add bins "{bins}" to a DistribcellFilter since ' - 'only a single distribcell can be used per tally') + msg = ( + f'Unable to add bins "{bins}" to a DistribcellFilter since ' + "only a single distribcell can be used per tally" + ) raise ValueError(msg) # Check the type and extract the id, if necessary. - cv.check_type('distribcell bin', bins[0], (Integral, openmc.Cell)) + cv.check_type("distribcell bin", bins[0], (Integral, openmc.Cell)) if isinstance(bins[0], openmc.Cell): bins = np.atleast_1d(bins[0].id) @@ -1710,15 +1786,17 @@ def get_pandas_dataframe(self, data_size, stride, **kwargs): level_df = None - paths = kwargs.setdefault('paths', True) + paths = kwargs.setdefault("paths", True) # Create Pandas Multi-index columns for each level in CSG tree if paths: # Distribcell paths require linked metadata from the Summary if self.paths is None: - msg = 'Unable to construct distribcell paths since ' \ - 'the Summary is not linked to the StatePoint' + msg = ( + "Unable to construct distribcell paths since " + "the Summary is not linked to the StatePoint" + ) raise ValueError(msg) # Make copy of array of distribcell paths to use in @@ -1730,7 +1808,7 @@ def get_pandas_dataframe(self, data_size, stride, **kwargs): num_levels = len(paths[0]) for i_level in range(num_levels): # Use level key as first index in Pandas Multi-index column - level_key = f'level {i_level + 1}' + level_key = f"level {i_level + 1}" # Create a dictionary for this level for Pandas Multi-index level_dict = {} @@ -1738,12 +1816,12 @@ def get_pandas_dataframe(self, data_size, stride, **kwargs): # Use the first distribcell path to determine if level # is a universe/cell or lattice level path = paths[0] - if path[i_level][0] == 'lattice': + if path[i_level][0] == "lattice": # Initialize prefix Multi-index keys - lat_id_key = (level_key, 'lat', 'id') - lat_x_key = (level_key, 'lat', 'x') - lat_y_key = (level_key, 'lat', 'y') - lat_z_key = (level_key, 'lat', 'z') + lat_id_key = (level_key, "lat", "id") + lat_x_key = (level_key, "lat", "x") + lat_y_key = (level_key, "lat", "y") + lat_z_key = (level_key, "lat", "z") # Allocate NumPy arrays for each CSG level and # each Multi-index column in the DataFrame @@ -1755,8 +1833,8 @@ def get_pandas_dataframe(self, data_size, stride, **kwargs): else: # Initialize prefix Multi-index keys - univ_key = (level_key, 'univ', 'id') - cell_key = (level_key, 'cell', 'id') + univ_key = (level_key, "univ", "id") + cell_key = (level_key, "cell", "id") # Allocate NumPy arrays for each CSG level and # each Multi-index column in the DataFrame @@ -1767,7 +1845,7 @@ def get_pandas_dataframe(self, data_size, stride, **kwargs): for i, path in enumerate(paths): level = path[i_level] - if level[0] == 'lattice': + if level[0] == "lattice": # Assign entry to Lattice Multi-index column level_dict[lat_id_key][i] = level[1] level_dict[lat_x_key][i] = level[2][0] @@ -1783,25 +1861,24 @@ def get_pandas_dataframe(self, data_size, stride, **kwargs): # Tile the Multi-index columns for level_key, level_bins in level_dict.items(): level_dict[level_key] = _repeat_and_tile( - level_bins, stride, data_size) + level_bins, stride, data_size + ) # Initialize a Pandas DataFrame from the level dictionary if level_df is None: level_df = pd.DataFrame(level_dict) else: - level_df = pd.concat([level_df, pd.DataFrame(level_dict)], - axis=1) + level_df = pd.concat([level_df, pd.DataFrame(level_dict)], axis=1) # Create DataFrame column for distribcell instance IDs # NOTE: This is performed regardless of whether the user # requests Summary geometric information - filter_bins = _repeat_and_tile( - np.arange(self.num_bins), stride, data_size) - df = pd.DataFrame({self.short_name.lower() : filter_bins}) + filter_bins = _repeat_and_tile(np.arange(self.num_bins), stride, data_size) + df = pd.DataFrame({self.short_name.lower(): filter_bins}) # Concatenate with DataFrame of distribcell instance IDs if level_df is not None: - level_df = level_df.dropna(axis=1, how='all') + level_df = level_df.dropna(axis=1, how="all") level_df = level_df.astype(int) df = pd.concat([level_df, df], axis=1) @@ -1836,18 +1913,19 @@ class MuFilter(RealFilter): The number of filter bins """ + def __init__(self, values, filter_id=None): if isinstance(values, Integral): - values = np.linspace(-1., 1., values + 1) + values = np.linspace(-1.0, 1.0, values + 1) super().__init__(values, filter_id) def check_bins(self, bins): super().check_bins(bins) for x in np.ravel(bins): - if not np.isclose(x, -1.): - cv.check_greater_than('filter value', x, -1., equality=True) - if not np.isclose(x, 1.): - cv.check_less_than('filter value', x, 1., equality=True) + if not np.isclose(x, -1.0): + cv.check_greater_than("filter value", x, -1.0, equality=True) + if not np.isclose(x, 1.0): + cv.check_less_than("filter value", x, 1.0, equality=True) class MuSurfaceFilter(MuFilter): @@ -1885,6 +1963,7 @@ class MuSurfaceFilter(MuFilter): The number of filter bins """ + # Note: inherits implementation from MuFilter @@ -1917,20 +1996,21 @@ class PolarFilter(RealFilter): The number of filter bins """ - units = 'rad' + + units = "rad" def __init__(self, values, filter_id=None): if isinstance(values, Integral): - values = np.linspace(0., np.pi, values + 1) + values = np.linspace(0.0, np.pi, values + 1) super().__init__(values, filter_id) def check_bins(self, bins): super().check_bins(bins) for x in np.ravel(bins): - if not np.isclose(x, 0.): - cv.check_greater_than('filter value', x, 0., equality=True) + if not np.isclose(x, 0.0): + cv.check_greater_than("filter value", x, 0.0, equality=True) if not np.isclose(x, np.pi): - cv.check_less_than('filter value', x, np.pi, equality=True) + cv.check_less_than("filter value", x, np.pi, equality=True) class AzimuthalFilter(RealFilter): @@ -1961,7 +2041,8 @@ class AzimuthalFilter(RealFilter): The number of filter bins """ - units = 'rad' + + units = "rad" def __init__(self, values, filter_id=None): if isinstance(values, Integral): @@ -1972,9 +2053,9 @@ def check_bins(self, bins): super().check_bins(bins) for x in np.ravel(bins): if not np.isclose(x, -np.pi): - cv.check_greater_than('filter value', x, -np.pi, equality=True) + cv.check_greater_than("filter value", x, -np.pi, equality=True) if not np.isclose(x, np.pi): - cv.check_less_than('filter value', x, np.pi, equality=True) + cv.check_less_than("filter value", x, np.pi, equality=True) class DelayedGroupFilter(Filter): @@ -2001,10 +2082,11 @@ class DelayedGroupFilter(Filter): The number of filter bins """ + def check_bins(self, bins): # Check the bin values. for g in bins: - cv.check_greater_than('delayed group', g, 0) + cv.check_greater_than("delayed group", g, 0) class EnergyFunctionFilter(Filter): @@ -2045,12 +2127,17 @@ class EnergyFunctionFilter(Filter): # keys selected to match those in function.py where possible # skip 6 b/c ENDF-6 reserves this value for # "special one-dimensional interpolation law" - INTERPOLATION_SCHEMES = {1: 'histogram', 2: 'linear-linear', - 3: 'linear-log', 4: 'log-linear', - 5: 'log-log', 7: 'quadratic', - 8: 'cubic'} - - def __init__(self, energy, y, interpolation='linear-linear', filter_id=None): + INTERPOLATION_SCHEMES = { + 1: "histogram", + 2: "linear-linear", + 3: "linear-log", + 4: "log-linear", + 5: "log-log", + 7: "quadratic", + 8: "cubic", + } + + def __init__(self, energy, y, interpolation="linear-linear", filter_id=None): self.energy = energy self.y = y self.id = filter_id @@ -2068,10 +2155,10 @@ def __eq__(self, other): def __gt__(self, other): if type(self) is not type(other): - if self.short_name in _FILTER_TYPES and \ - other.short_name in _FILTER_TYPES: - delta = _FILTER_TYPES.index(self.short_name) - \ - _FILTER_TYPES.index(other.short_name) + if self.short_name in _FILTER_TYPES and other.short_name in _FILTER_TYPES: + delta = _FILTER_TYPES.index(self.short_name) - _FILTER_TYPES.index( + other.short_name + ) return delta > 0 else: return False @@ -2080,10 +2167,10 @@ def __gt__(self, other): def __lt__(self, other): if type(self) is not type(other): - if self.short_name in _FILTER_TYPES and \ - other.short_name in _FILTER_TYPES: - delta = _FILTER_TYPES.index(self.short_name) - \ - _FILTER_TYPES.index(other.short_name) + if self.short_name in _FILTER_TYPES and other.short_name in _FILTER_TYPES: + delta = _FILTER_TYPES.index(self.short_name) - _FILTER_TYPES.index( + other.short_name + ) return delta < 0 else: return False @@ -2091,36 +2178,41 @@ def __lt__(self, other): return False def __hash__(self): - string = type(self).__name__ + '\n' - string += '{: <16}=\t{}\n'.format('\tEnergy', self.energy) - string += '{: <16}=\t{}\n'.format('\tInterpolant', self.y) - string += '{: <16}=\t{}\n'.format('\tInterpolation', self.interpolation) + string = type(self).__name__ + "\n" + string += "{: <16}=\t{}\n".format("\tEnergy", self.energy) + string += "{: <16}=\t{}\n".format("\tInterpolant", self.y) + string += "{: <16}=\t{}\n".format("\tInterpolation", self.interpolation) return hash(string) def __repr__(self): - string = type(self).__name__ + '\n' - string += '{: <16}=\t{}\n'.format('\tEnergy', self.energy) - string += '{: <16}=\t{}\n'.format('\tInterpolant', self.y) - string += '{: <16}=\t{}\n'.format('\tInterpolation', self.interpolation) - string += '{: <16}=\t{}\n'.format('\tID', self.id) + string = type(self).__name__ + "\n" + string += "{: <16}=\t{}\n".format("\tEnergy", self.energy) + string += "{: <16}=\t{}\n".format("\tInterpolant", self.y) + string += "{: <16}=\t{}\n".format("\tInterpolation", self.interpolation) + string += "{: <16}=\t{}\n".format("\tID", self.id) return string @classmethod def from_hdf5(cls, group, **kwargs): - if group['type'][()].decode() != cls.short_name.lower(): - raise ValueError("Expected HDF5 data for filter type '" - + cls.short_name.lower() + "' but got '" - + group['type'][()].decode() + " instead") - - energy = group['energy'][()] - y_grp = group['y'] + if group["type"][()].decode() != cls.short_name.lower(): + raise ValueError( + "Expected HDF5 data for filter type '" + + cls.short_name.lower() + + "' but got '" + + group["type"][()].decode() + + " instead" + ) + + energy = group["energy"][()] + y_grp = group["y"] y = y_grp[()] - filter_id = int(group.name.split('/')[-1].lstrip('filter ')) + filter_id = int(group.name.split("/")[-1].lstrip("filter ")) out = cls(energy, y, filter_id=filter_id) - if 'interpolation' in y_grp.attrs: - out.interpolation = \ - cls.INTERPOLATION_SCHEMES[y_grp.attrs['interpolation'][()]] + if "interpolation" in y_grp.attrs: + out.interpolation = cls.INTERPOLATION_SCHEMES[ + y_grp.attrs["interpolation"][()] + ] return out @@ -2139,15 +2231,17 @@ def from_tabulated1d(cls, tab1d): EnergyFunctionFilter """ - cv.check_type('EnergyFunctionFilter tab1d', tab1d, - openmc.data.Tabulated1D) + cv.check_type("EnergyFunctionFilter tab1d", tab1d, openmc.data.Tabulated1D) if tab1d.n_regions > 1: - raise ValueError('Only Tabulated1Ds with a single interpolation ' - 'region are supported') + raise ValueError( + "Only Tabulated1Ds with a single interpolation " "region are supported" + ) interpolation_val = tab1d.interpolation[0] if interpolation_val not in cls.INTERPOLATION_SCHEMES.keys(): - raise ValueError('Only histogram, linear-linear, linear-log, log-linear, and ' - 'log-log Tabulated1Ds are supported') + raise ValueError( + "Only histogram, linear-linear, linear-log, log-linear, and " + "log-log Tabulated1Ds are supported" + ) return cls(tab1d.x, tab1d.y, cls.INTERPOLATION_SCHEMES[interpolation_val]) @property @@ -2160,9 +2254,9 @@ def energy(self, energy): energy = np.atleast_1d(energy) # Make sure the values are Real and positive. - cv.check_type('filter energy grid', energy, Iterable, Real) + cv.check_type("filter energy grid", energy, Iterable, Real) for E in energy: - cv.check_greater_than('filter energy grid', E, 0, equality=True) + cv.check_greater_than("filter energy grid", E, 0, equality=True) self._energy = energy @@ -2176,7 +2270,7 @@ def y(self, y): y = np.atleast_1d(y) # Make sure the values are Real. - cv.check_type('filter interpolant values', y, Iterable, Real) + cv.check_type("filter interpolant values", y, Iterable, Real) self._y = y @@ -2186,24 +2280,24 @@ def interpolation(self): @interpolation.setter def interpolation(self, val): - cv.check_type('interpolation', val, str) - cv.check_value('interpolation', val, self.INTERPOLATION_SCHEMES.values()) + cv.check_type("interpolation", val, str) + cv.check_value("interpolation", val, self.INTERPOLATION_SCHEMES.values()) - if val == 'quadratic' and len(self.energy) < 3: - raise ValueError('Quadratic interpolation requires 3 or more values.') + if val == "quadratic" and len(self.energy) < 3: + raise ValueError("Quadratic interpolation requires 3 or more values.") - if val == 'cubic' and len(self.energy) < 4: - raise ValueError('Cubic interpolation requires 3 or more values.') + if val == "cubic" and len(self.energy) < 4: + raise ValueError("Cubic interpolation requires 3 or more values.") self._interpolation = val @property def bins(self): - raise AttributeError('EnergyFunctionFilters have no bins.') + raise AttributeError("EnergyFunctionFilters have no bins.") @bins.setter def bins(self, bins): - raise RuntimeError('EnergyFunctionFilters have no bins.') + raise RuntimeError("EnergyFunctionFilters have no bins.") @property def num_bins(self): @@ -2218,29 +2312,29 @@ def to_xml_element(self): XML element containing filter data """ - element = ET.Element('filter') - element.set('id', str(self.id)) - element.set('type', self.short_name.lower()) + element = ET.Element("filter") + element.set("id", str(self.id)) + element.set("type", self.short_name.lower()) - subelement = ET.SubElement(element, 'energy') - subelement.text = ' '.join(str(e) for e in self.energy) + subelement = ET.SubElement(element, "energy") + subelement.text = " ".join(str(e) for e in self.energy) - subelement = ET.SubElement(element, 'y') - subelement.text = ' '.join(str(y) for y in self.y) + subelement = ET.SubElement(element, "y") + subelement.text = " ".join(str(y) for y in self.y) - subelement = ET.SubElement(element, 'interpolation') + subelement = ET.SubElement(element, "interpolation") subelement.text = self.interpolation return element @classmethod def from_xml_element(cls, elem, **kwargs): - filter_id = int(elem.get('id')) - energy = [float(x) for x in get_text(elem, 'energy').split()] - y = [float(x) for x in get_text(elem, 'y').split()] + filter_id = int(elem.get("id")) + energy = [float(x) for x in get_text(elem, "energy").split()] + y = [float(x) for x in get_text(elem, "y").split()] out = cls(energy, y, filter_id=filter_id) - if elem.find('interpolation') is not None: - out.interpolation = elem.find('interpolation').text + if elem.find("interpolation") is not None: + out.interpolation = elem.find("interpolation").text return out def can_merge(self, other): @@ -2289,7 +2383,7 @@ def get_pandas_dataframe(self, data_size, stride, **kwargs): # reproducible after restarting the interpreter so we'll use hashlib.md5 # rather than the intrinsic hash(). hash_fun = hashlib.md5() - hash_fun.update(repr(self).encode('utf-8')) + hash_fun.update(repr(self).encode("utf-8")) out = hash_fun.hexdigest() # The full 16 bytes make for a really wide column. Just 7 bytes (14 @@ -2297,7 +2391,6 @@ def get_pandas_dataframe(self, data_size, stride, **kwargs): out = out[:14] filter_bins = _repeat_and_tile(out, stride, data_size) - df = pd.concat([df, pd.DataFrame( - {self.short_name.lower(): filter_bins})]) + df = pd.concat([df, pd.DataFrame({self.short_name.lower(): filter_bins})]) return df diff --git a/openmc/filter_expansion.py b/openmc/filter_expansion.py index f8e677578f1..35c00ed6206 100644 --- a/openmc/filter_expansion.py +++ b/openmc/filter_expansion.py @@ -25,8 +25,8 @@ def order(self): @order.setter def order(self, order): - cv.check_type('expansion order', order, Integral) - cv.check_greater_than('expansion order', order, 0, equality=True) + cv.check_type("expansion order", order, Integral) + cv.check_greater_than("expansion order", order, 0, equality=True) self._order = order def to_xml_element(self): @@ -38,19 +38,19 @@ def to_xml_element(self): XML element containing Legendre filter data """ - element = ET.Element('filter') - element.set('id', str(self.id)) - element.set('type', self.short_name.lower()) + element = ET.Element("filter") + element.set("id", str(self.id)) + element.set("type", self.short_name.lower()) - subelement = ET.SubElement(element, 'order') + subelement = ET.SubElement(element, "order") subelement.text = str(self.order) return element @classmethod def from_xml_element(cls, elem, **kwargs): - filter_id = int(elem.get('id')) - order = int(elem.find('order').text) + filter_id = int(elem.get("id")) + order = int(elem.find("order").text) return cls(order, filter_id=filter_id) def merge(self, other): @@ -107,31 +107,35 @@ class LegendreFilter(ExpansionFilter): """ def __hash__(self): - string = type(self).__name__ + '\n' - string += '{: <16}=\t{}\n'.format('\tOrder', self.order) + string = type(self).__name__ + "\n" + string += "{: <16}=\t{}\n".format("\tOrder", self.order) return hash(string) def __repr__(self): - string = type(self).__name__ + '\n' - string += '{: <16}=\t{}\n'.format('\tOrder', self.order) - string += '{: <16}=\t{}\n'.format('\tID', self.id) + string = type(self).__name__ + "\n" + string += "{: <16}=\t{}\n".format("\tOrder", self.order) + string += "{: <16}=\t{}\n".format("\tID", self.id) return string @ExpansionFilter.order.setter def order(self, order): ExpansionFilter.order.__set__(self, order) - self.bins = [f'P{i}' for i in range(order + 1)] + self.bins = [f"P{i}" for i in range(order + 1)] @classmethod def from_hdf5(cls, group, **kwargs): - if group['type'][()].decode() != cls.short_name.lower(): - raise ValueError("Expected HDF5 data for filter type '" - + cls.short_name.lower() + "' but got '" - + group['type'][()].decode() + " instead") + if group["type"][()].decode() != cls.short_name.lower(): + raise ValueError( + "Expected HDF5 data for filter type '" + + cls.short_name.lower() + + "' but got '" + + group["type"][()].decode() + + " instead" + ) - filter_id = int(group.name.split('/')[-1].lstrip('filter ')) + filter_id = int(group.name.split("/")[-1].lstrip("filter ")) - out = cls(group['order'][()], filter_id) + out = cls(group["order"][()], filter_id) return out @@ -180,26 +184,26 @@ def __init__(self, order, axis, minimum, maximum, filter_id=None): self.maximum = maximum def __hash__(self): - string = type(self).__name__ + '\n' - string += '{: <16}=\t{}\n'.format('\tOrder', self.order) - string += '{: <16}=\t{}\n'.format('\tAxis', self.axis) - string += '{: <16}=\t{}\n'.format('\tMin', self.minimum) - string += '{: <16}=\t{}\n'.format('\tMax', self.maximum) + string = type(self).__name__ + "\n" + string += "{: <16}=\t{}\n".format("\tOrder", self.order) + string += "{: <16}=\t{}\n".format("\tAxis", self.axis) + string += "{: <16}=\t{}\n".format("\tMin", self.minimum) + string += "{: <16}=\t{}\n".format("\tMax", self.maximum) return hash(string) def __repr__(self): - string = type(self).__name__ + '\n' - string += '{: <16}=\t{}\n'.format('\tOrder', self.order) - string += '{: <16}=\t{}\n'.format('\tAxis', self.axis) - string += '{: <16}=\t{}\n'.format('\tMin', self.minimum) - string += '{: <16}=\t{}\n'.format('\tMax', self.maximum) - string += '{: <16}=\t{}\n'.format('\tID', self.id) + string = type(self).__name__ + "\n" + string += "{: <16}=\t{}\n".format("\tOrder", self.order) + string += "{: <16}=\t{}\n".format("\tAxis", self.axis) + string += "{: <16}=\t{}\n".format("\tMin", self.minimum) + string += "{: <16}=\t{}\n".format("\tMax", self.maximum) + string += "{: <16}=\t{}\n".format("\tID", self.id) return string @ExpansionFilter.order.setter def order(self, order): ExpansionFilter.order.__set__(self, order) - self.bins = [f'P{i}' for i in range(order + 1)] + self.bins = [f"P{i}" for i in range(order + 1)] @property def axis(self): @@ -207,7 +211,7 @@ def axis(self): @axis.setter def axis(self, axis): - cv.check_value('axis', axis, ('x', 'y', 'z')) + cv.check_value("axis", axis, ("x", "y", "z")) self._axis = axis @property @@ -216,7 +220,7 @@ def minimum(self): @minimum.setter def minimum(self, minimum): - cv.check_type('minimum', minimum, Real) + cv.check_type("minimum", minimum, Real) self._minimum = minimum @property @@ -225,20 +229,24 @@ def maximum(self): @maximum.setter def maximum(self, maximum): - cv.check_type('maximum', maximum, Real) + cv.check_type("maximum", maximum, Real) self._maximum = maximum @classmethod def from_hdf5(cls, group, **kwargs): - if group['type'][()].decode() != cls.short_name.lower(): - raise ValueError("Expected HDF5 data for filter type '" - + cls.short_name.lower() + "' but got '" - + group['type'][()].decode() + " instead") - - filter_id = int(group.name.split('/')[-1].lstrip('filter ')) - order = group['order'][()] - axis = group['axis'][()].decode() - min_, max_ = group['min'][()], group['max'][()] + if group["type"][()].decode() != cls.short_name.lower(): + raise ValueError( + "Expected HDF5 data for filter type '" + + cls.short_name.lower() + + "' but got '" + + group["type"][()].decode() + + " instead" + ) + + filter_id = int(group.name.split("/")[-1].lstrip("filter ")) + order = group["order"][()] + axis = group["axis"][()].decode() + min_, max_ = group["min"][()], group["max"][()] return cls(order, axis, min_, max_, filter_id) @@ -252,22 +260,22 @@ def to_xml_element(self): """ element = super().to_xml_element() - subelement = ET.SubElement(element, 'axis') + subelement = ET.SubElement(element, "axis") subelement.text = self.axis - subelement = ET.SubElement(element, 'min') + subelement = ET.SubElement(element, "min") subelement.text = str(self.minimum) - subelement = ET.SubElement(element, 'max') + subelement = ET.SubElement(element, "max") subelement.text = str(self.maximum) return element @classmethod def from_xml_element(cls, elem, **kwargs): - filter_id = int(elem.get('id')) - order = int(elem.find('order').text) - axis = elem.find('axis').text - minimum = float(elem.find('min').text) - maximum = float(elem.find('max').text) + filter_id = int(elem.get("id")) + order = int(elem.find("order").text) + axis = elem.find("axis").text + minimum = float(elem.find("min").text) + maximum = float(elem.find("max").text) return cls(order, axis, minimum, maximum, filter_id=filter_id) @@ -301,27 +309,25 @@ class SphericalHarmonicsFilter(ExpansionFilter): def __init__(self, order, filter_id=None): super().__init__(order, filter_id) - self._cosine = 'particle' + self._cosine = "particle" def __hash__(self): - string = type(self).__name__ + '\n' - string += '{: <16}=\t{}\n'.format('\tOrder', self.order) - string += '{: <16}=\t{}\n'.format('\tCosine', self.cosine) + string = type(self).__name__ + "\n" + string += "{: <16}=\t{}\n".format("\tOrder", self.order) + string += "{: <16}=\t{}\n".format("\tCosine", self.cosine) return hash(string) def __repr__(self): - string = type(self).__name__ + '\n' - string += '{: <16}=\t{}\n'.format('\tOrder', self.order) - string += '{: <16}=\t{}\n'.format('\tCosine', self.cosine) - string += '{: <16}=\t{}\n'.format('\tID', self.id) + string = type(self).__name__ + "\n" + string += "{: <16}=\t{}\n".format("\tOrder", self.order) + string += "{: <16}=\t{}\n".format("\tCosine", self.cosine) + string += "{: <16}=\t{}\n".format("\tID", self.id) return string @ExpansionFilter.order.setter def order(self, order): ExpansionFilter.order.__set__(self, order) - self.bins = [f'Y{n},{m}' - for n in range(order + 1) - for m in range(-n, n + 1)] + self.bins = [f"Y{n},{m}" for n in range(order + 1) for m in range(-n, n + 1)] @property def cosine(self): @@ -329,21 +335,26 @@ def cosine(self): @cosine.setter def cosine(self, cosine): - cv.check_value('Spherical harmonics cosine treatment', cosine, - ('scatter', 'particle')) + cv.check_value( + "Spherical harmonics cosine treatment", cosine, ("scatter", "particle") + ) self._cosine = cosine @classmethod def from_hdf5(cls, group, **kwargs): - if group['type'][()].decode() != cls.short_name.lower(): - raise ValueError("Expected HDF5 data for filter type '" - + cls.short_name.lower() + "' but got '" - + group['type'][()].decode() + " instead") + if group["type"][()].decode() != cls.short_name.lower(): + raise ValueError( + "Expected HDF5 data for filter type '" + + cls.short_name.lower() + + "' but got '" + + group["type"][()].decode() + + " instead" + ) - filter_id = int(group.name.split('/')[-1].lstrip('filter ')) + filter_id = int(group.name.split("/")[-1].lstrip("filter ")) - out = cls(group['order'][()], filter_id) - out.cosine = group['cosine'][()].decode() + out = cls(group["order"][()], filter_id) + out.cosine = group["cosine"][()].decode() return out @@ -357,15 +368,15 @@ def to_xml_element(self): """ element = super().to_xml_element() - element.set('cosine', self.cosine) + element.set("cosine", self.cosine) return element @classmethod def from_xml_element(cls, elem, **kwargs): - filter_id = int(elem.get('id')) - order = int(elem.find('order').text) + filter_id = int(elem.get("id")) + order = int(elem.find("order").text) filter = cls(order, filter_id=filter_id) - filter.cosine = elem.get('cosine') + filter.cosine = elem.get("cosine") return filter @@ -437,25 +448,23 @@ def __init__(self, order, x=0.0, y=0.0, r=1.0, filter_id=None): self.r = r def __hash__(self): - string = type(self).__name__ + '\n' - string += '{: <16}=\t{}\n'.format('\tOrder', self.order) - string += '{: <16}=\t{}\n'.format('\tX', self.x) - string += '{: <16}=\t{}\n'.format('\tY', self.y) - string += '{: <16}=\t{}\n'.format('\tR', self.r) + string = type(self).__name__ + "\n" + string += "{: <16}=\t{}\n".format("\tOrder", self.order) + string += "{: <16}=\t{}\n".format("\tX", self.x) + string += "{: <16}=\t{}\n".format("\tY", self.y) + string += "{: <16}=\t{}\n".format("\tR", self.r) return hash(string) def __repr__(self): - string = type(self).__name__ + '\n' - string += '{: <16}=\t{}\n'.format('\tOrder', self.order) - string += '{: <16}=\t{}\n'.format('\tID', self.id) + string = type(self).__name__ + "\n" + string += "{: <16}=\t{}\n".format("\tOrder", self.order) + string += "{: <16}=\t{}\n".format("\tID", self.id) return string @ExpansionFilter.order.setter def order(self, order): ExpansionFilter.order.__set__(self, order) - self.bins = [f'Z{n},{m}' - for n in range(order + 1) - for m in range(-n, n + 1, 2)] + self.bins = [f"Z{n},{m}" for n in range(order + 1) for m in range(-n, n + 1, 2)] @property def x(self): @@ -463,7 +472,7 @@ def x(self): @x.setter def x(self, x): - cv.check_type('x', x, Real) + cv.check_type("x", x, Real) self._x = x @property @@ -472,7 +481,7 @@ def y(self): @y.setter def y(self, y): - cv.check_type('y', y, Real) + cv.check_type("y", y, Real) self._y = y @property @@ -481,19 +490,23 @@ def r(self): @r.setter def r(self, r): - cv.check_type('r', r, Real) + cv.check_type("r", r, Real) self._r = r @classmethod def from_hdf5(cls, group, **kwargs): - if group['type'][()].decode() != cls.short_name.lower(): - raise ValueError("Expected HDF5 data for filter type '" - + cls.short_name.lower() + "' but got '" - + group['type'][()].decode() + " instead") - - filter_id = int(group.name.split('/')[-1].lstrip('filter ')) - order = group['order'][()] - x, y, r = group['x'][()], group['y'][()], group['r'][()] + if group["type"][()].decode() != cls.short_name.lower(): + raise ValueError( + "Expected HDF5 data for filter type '" + + cls.short_name.lower() + + "' but got '" + + group["type"][()].decode() + + " instead" + ) + + filter_id = int(group.name.split("/")[-1].lstrip("filter ")) + order = group["order"][()] + x, y, r = group["x"][()], group["y"][()], group["r"][()] return cls(order, x, y, r, filter_id) @@ -507,22 +520,22 @@ def to_xml_element(self): """ element = super().to_xml_element() - subelement = ET.SubElement(element, 'x') + subelement = ET.SubElement(element, "x") subelement.text = str(self.x) - subelement = ET.SubElement(element, 'y') + subelement = ET.SubElement(element, "y") subelement.text = str(self.y) - subelement = ET.SubElement(element, 'r') + subelement = ET.SubElement(element, "r") subelement.text = str(self.r) return element @classmethod def from_xml_element(cls, elem, **kwargs): - filter_id = int(elem.get('id')) - order = int(elem.find('order').text) - x = float(elem.find('x').text) - y = float(elem.find('y').text) - r = float(elem.find('r').text) + filter_id = int(elem.get("id")) + order = int(elem.find("order").text) + x = float(elem.find("x").text) + y = float(elem.find("y").text) + r = float(elem.find("r").text) return cls(order, x, y, r, filter_id=filter_id) @@ -583,4 +596,4 @@ class ZernikeRadialFilter(ZernikeFilter): @ExpansionFilter.order.setter def order(self, order): ExpansionFilter.order.__set__(self, order) - self.bins = [f'Z{n},0' for n in range(0, order+1, 2)] + self.bins = [f"Z{n},0" for n in range(0, order + 1, 2)] diff --git a/openmc/geometry.py b/openmc/geometry.py index a52dc4418d2..10140013fd2 100644 --- a/openmc/geometry.py +++ b/openmc/geometry.py @@ -40,7 +40,7 @@ def __init__( self, root: openmc.UniverseBase | Iterable[openmc.Cell] | None = None, merge_surfaces: bool = False, - surface_precision: int = 10 + surface_precision: int = 10, ): self._root_universe = None self._offsets = {} @@ -61,7 +61,7 @@ def root_universe(self) -> openmc.UniverseBase: @root_universe.setter def root_universe(self, root_universe): - check_type('root universe', root_universe, openmc.UniverseBase) + check_type("root universe", root_universe, openmc.UniverseBase) self._root_universe = root_universe @property @@ -74,7 +74,7 @@ def merge_surfaces(self) -> bool: @merge_surfaces.setter def merge_surfaces(self, merge_surfaces): - check_type('merge surfaces', merge_surfaces, bool) + check_type("merge surfaces", merge_surfaces, bool) self._merge_surfaces = merge_surfaces @property @@ -83,9 +83,9 @@ def surface_precision(self) -> int: @surface_precision.setter def surface_precision(self, surface_precision): - check_type('surface precision', surface_precision, int) - check_less_than('surface_precision', surface_precision, 16) - check_greater_than('surface_precision', surface_precision, 0) + check_type("surface precision", surface_precision, int) + check_less_than("surface_precision", surface_precision, 16) + check_greater_than("surface_precision", surface_precision, 0) self._surface_precision = surface_precision def add_volume_information(self, volume_calc): @@ -97,15 +97,15 @@ def add_volume_information(self, volume_calc): Results from a stochastic volume calculation """ - if volume_calc.domain_type == 'cell': + if volume_calc.domain_type == "cell": for cell in self.get_all_cells().values(): if cell.id in volume_calc.volumes: cell.add_volume_information(volume_calc) - elif volume_calc.domain_type == 'material': + elif volume_calc.domain_type == "material": for material in self.get_all_materials().values(): if material.id in volume_calc.volumes: material.add_volume_information(volume_calc) - elif volume_calc.domain_type == 'universe': + elif volume_calc.domain_type == "universe": for universe in self.get_all_universes().values(): if universe.id in volume_calc.volumes: universe.add_volume_information(volume_calc) @@ -122,8 +122,10 @@ def to_xml_element(self, remove_surfs=False) -> ET.Element: """ # Find and remove redundant surfaces from the geometry if remove_surfs: - warnings.warn("remove_surfs kwarg will be deprecated soon, please " - "set the Geometry.merge_surfaces attribute instead.") + warnings.warn( + "remove_surfs kwarg will be deprecated soon, please " + "set the Geometry.merge_surfaces attribute instead." + ) self.merge_surfaces = True if self.merge_surfaces: @@ -134,8 +136,7 @@ def to_xml_element(self, remove_surfs=False) -> ET.Element: self.root_universe.create_xml_subelement(element) # Sort the elements in the file - element[:] = sorted(element, key=lambda x: ( - x.tag, int(x.get('id')))) + element[:] = sorted(element, key=lambda x: (x.tag, int(x.get("id")))) # Clean the indentation in the file to be user-readable xml.clean_indentation(element) @@ -143,7 +144,7 @@ def to_xml_element(self, remove_surfs=False) -> ET.Element: return element - def export_to_xml(self, path='geometry.xml', remove_surfs=False): + def export_to_xml(self, path="geometry.xml", remove_surfs=False): """Export geometry to an XML file. Parameters @@ -162,11 +163,11 @@ def export_to_xml(self, path='geometry.xml', remove_surfs=False): # Check if path is a directory p = Path(path) if p.is_dir(): - p /= 'geometry.xml' + p /= "geometry.xml" # Write the XML Tree to the geometry.xml file tree = ET.ElementTree(root_element) - tree.write(str(p), xml_declaration=True, encoding='utf-8') + tree.write(str(p), xml_declaration=True, encoding="utf-8") @classmethod def from_xml_element(cls, elem, materials=None) -> Geometry: @@ -189,10 +190,11 @@ def from_xml_element(cls, elem, materials=None) -> Geometry: mats = dict() if materials is not None: mats.update({str(m.id): m for m in materials}) - mats['void'] = None + mats["void"] = None # Helper function for keeping a cache of Universe instances universes = {} + def get_universe(univ_id): if univ_id not in universes: univ = openmc.Universe(univ_id) @@ -202,12 +204,12 @@ def get_universe(univ_id): # Get surfaces surfaces = {} periodic = {} - for surface in elem.findall('surface'): + for surface in elem.findall("surface"): s = openmc.Surface.from_xml_element(surface) surfaces[s.id] = s # Check for periodic surface - other_id = xml.get_text(surface, 'periodic_surface_id') + other_id = xml.get_text(surface, "periodic_surface_id") if other_id is not None: periodic[s.id] = int(other_id) @@ -216,7 +218,7 @@ def get_universe(univ_id): surfaces[s1].periodic_surface = surfaces[s2] # Add any DAGMC universes - for e in elem.findall('dagmc_universe'): + for e in elem.findall("dagmc_universe"): dag_univ = openmc.DAGMCUniverse.from_xml_element(e) universes[dag_univ.id] = dag_univ @@ -224,7 +226,7 @@ def get_universe(univ_id): # contain it (needed to determine which universe is the elem) child_of = defaultdict(list) - for e in elem.findall('lattice'): + for e in elem.findall("lattice"): lat = openmc.RectLattice.from_xml_element(e, get_universe) universes[lat.id] = lat if lat.outer is not None: @@ -232,7 +234,7 @@ def get_universe(univ_id): for u in lat.universes.ravel(): child_of[u].append(lat) - for e in elem.findall('hex_lattice'): + for e in elem.findall("hex_lattice"): lat = openmc.HexLattice.from_xml_element(e, get_universe) universes[lat.id] = lat if lat.outer is not None: @@ -247,9 +249,9 @@ def get_universe(univ_id): for u in ring: child_of[u].append(lat) - for e in elem.findall('cell'): + for e in elem.findall("cell"): c = openmc.Cell.from_xml_element(e, surfaces, mats, get_universe) - if c.fill_type in ('universe', 'lattice'): + if c.fill_type in ("universe", "lattice"): child_of[c.fill].append(c) # Determine which universe is the root by finding one which is not a @@ -258,13 +260,13 @@ def get_universe(univ_id): if not child_of[u]: return cls(u) else: - raise ValueError('Error determining root universe.') + raise ValueError("Error determining root universe.") @classmethod def from_xml( cls, - path: PathLike = 'geometry.xml', - materials: PathLike | 'openmc.Materials' | None = 'materials.xml' + path: PathLike = "geometry.xml", + materials: PathLike | "openmc.Materials" | None = "materials.xml", ) -> Geometry: """Generate geometry from XML file @@ -285,7 +287,7 @@ def from_xml( # Using str and os.PathLike here to avoid error when using just the imported PathLike # TypeError: Subscripted generics cannot be used with class and instance checks - check_type('materials', materials, (str, os.PathLike, openmc.Materials)) + check_type("materials", materials, (str, os.PathLike, openmc.Materials)) if isinstance(materials, (str, os.PathLike)): materials = openmc.Materials.from_xml(materials) @@ -335,21 +337,20 @@ def get_instances(self, paths) -> int | list[int]: """ # Make sure we are working with an iterable - return_list = (isinstance(paths, Iterable) and - not isinstance(paths, str)) + return_list = isinstance(paths, Iterable) and not isinstance(paths, str) path_list = paths if return_list else [paths] indices = [] for p in path_list: # Extract the cell id from the path - last_index = p.rfind('>') - last_path = p[last_index+1:] + last_index = p.rfind(">") + last_path = p[last_index + 1 :] uid = int(last_path[1:]) # Get corresponding cell/material - if last_path[0] == 'c': + if last_path[0] == "c": obj = self.get_all_cells()[uid] - elif last_path[0] == 'm': + elif last_path[0] == "m": obj = self.get_all_materials()[uid] # Determine index in paths array @@ -431,7 +432,7 @@ def get_all_material_cells(self) -> dict[int, openmc.Cell]: material_cells = {} for cell in self.get_all_cells().values(): - if cell.fill_type in ('material', 'distribmat'): + if cell.fill_type in ("material", "distribmat"): if cell not in material_cells: material_cells[cell.id] = cell @@ -454,7 +455,7 @@ def get_all_material_universes(self) -> dict[int, openmc.Universe]: for universe in self.get_all_universes().values(): for cell in universe.cells.values(): - if cell.fill_type in ('material', 'distribmat', 'void'): + if cell.fill_type in ("material", "distribmat", "void"): if universe not in material_universes: material_universes[universe.id] = universe @@ -472,7 +473,7 @@ def get_all_lattices(self) -> dict[int, openmc.Lattice]: lattices = {} for cell in self.get_all_cells().values(): - if cell.fill_type == 'lattice': + if cell.fill_type == "lattice": if cell.fill.id not in lattices: lattices[cell.fill.id] = cell.fill @@ -501,7 +502,7 @@ def _get_domains_by_name(self, name, case_sensitive, matching, domain_type) -> l domains = [] - func = getattr(self, f'get_all_{domain_type}s') + func = getattr(self, f"get_all_{domain_type}s") for domain in func().values(): domain_name = domain.name if case_sensitive else domain.name.lower() if domain_name == name: @@ -533,7 +534,7 @@ def get_materials_by_name( Materials matching the queried name """ - return self._get_domains_by_name(name, case_sensitive, matching, 'material') + return self._get_domains_by_name(name, case_sensitive, matching, "material") def get_cells_by_name( self, name, case_sensitive=False, matching=False @@ -556,7 +557,7 @@ def get_cells_by_name( Cells matching the queried name """ - return self._get_domains_by_name(name, case_sensitive, matching, 'cell') + return self._get_domains_by_name(name, case_sensitive, matching, "cell") def get_surfaces_by_name( self, name, case_sensitive=False, matching=False @@ -581,7 +582,7 @@ def get_surfaces_by_name( Surfaces matching the queried name """ - return self._get_domains_by_name(name, case_sensitive, matching, 'surface') + return self._get_domains_by_name(name, case_sensitive, matching, "surface") def get_cells_by_fill_name( self, name, case_sensitive=False, matching=False @@ -612,9 +613,9 @@ def get_cells_by_fill_name( for cell in self.get_all_cells().values(): names = [] - if cell.fill_type in ('material', 'universe', 'lattice'): + if cell.fill_type in ("material", "universe", "lattice"): names.append(cell.fill.name) - elif cell.fill_type == 'distribmat': + elif cell.fill_type == "distribmat": for mat in cell.fill: if mat is not None: names.append(mat.name) @@ -651,7 +652,7 @@ def get_universes_by_name( Universes matching the queried name """ - return self._get_domains_by_name(name, case_sensitive, matching, 'universe') + return self._get_domains_by_name(name, case_sensitive, matching, "universe") def get_lattices_by_name( self, name, case_sensitive=False, matching=False @@ -674,7 +675,7 @@ def get_lattices_by_name( Lattices matching the queried name """ - return self._get_domains_by_name(name, case_sensitive, matching, 'lattice') + return self._get_domains_by_name(name, case_sensitive, matching, "lattice") def remove_redundant_surfaces(self) -> dict[int, openmc.Surface]: """Remove and return all of the redundant surfaces. @@ -695,15 +696,18 @@ def remove_redundant_surfaces(self) -> dict[int, openmc.Surface]: # Get redundant surfaces redundancies = defaultdict(list) for surf in self.get_all_surfaces().values(): - coeffs = tuple(round(surf._coefficients[k], - self.surface_precision) - for k in surf._coeff_keys) + coeffs = tuple( + round(surf._coefficients[k], self.surface_precision) + for k in surf._coeff_keys + ) key = (surf._type, surf._boundary_type) + coeffs redundancies[key].append(surf) - redundant_surfaces = {replace.id: keep - for keep, *redundant in redundancies.values() - for replace in redundant} + redundant_surfaces = { + replace.id: keep + for keep, *redundant in redundancies.values() + for replace in redundant + } if redundant_surfaces: # Iterate through all cells contained in the geometry diff --git a/openmc/lattice.py b/openmc/lattice.py index 51806856051..8ffd268da10 100644 --- a/openmc/lattice.py +++ b/openmc/lattice.py @@ -44,7 +44,7 @@ class Lattice(IDManagerMixin, ABC): next_id = 1 used_ids = openmc.UniverseBase.used_ids - def __init__(self, lattice_id=None, name=''): + def __init__(self, lattice_id=None, name=""): # Initialize Lattice class attributes self.id = lattice_id self.name = name @@ -59,10 +59,10 @@ def name(self): @name.setter def name(self, name): if name is not None: - cv.check_type('lattice name', name, str) + cv.check_type("lattice name", name, str) self._name = name else: - self._name = '' + self._name = "" @property def pitch(self): @@ -74,7 +74,7 @@ def outer(self): @outer.setter def outer(self, outer): - cv.check_type('outer universe', outer, openmc.UniverseBase) + cv.check_type("outer universe", outer, openmc.UniverseBase) self._outer = outer @property @@ -99,13 +99,13 @@ def from_hdf5(group, universes): Instance of lattice subclass """ - lattice_type = group['type'][()].decode() - if lattice_type == 'rectangular': + lattice_type = group["type"][()].decode() + if lattice_type == "rectangular": return openmc.RectLattice.from_hdf5(group, universes) - elif lattice_type == 'hexagonal': + elif lattice_type == "hexagonal": return openmc.HexLattice.from_hdf5(group, universes) else: - raise ValueError(f'Unknown lattice type: {lattice_type}') + raise ValueError(f"Unknown lattice type: {lattice_type}") def get_unique_universes(self): """Determine all unique universes in the lattice @@ -320,23 +320,23 @@ def clone(self, clone_materials=True, clone_regions=True, memo=None): clone.id = None if self.outer is not None: - clone.outer = self.outer.clone(clone_materials, clone_regions, - memo) + clone.outer = self.outer.clone(clone_materials, clone_regions, memo) # Assign universe clones to the lattice clone for i in self.indices: if isinstance(self, RectLattice): clone.universes[i] = self.universes[i].clone( - clone_materials, clone_regions, memo) + clone_materials, clone_regions, memo + ) else: if self.ndim == 2: - clone.universes[i[0]][i[1]] = \ - self.universes[i[0]][i[1]].clone(clone_materials, - clone_regions, memo) + clone.universes[i[0]][i[1]] = self.universes[i[0]][i[1]].clone( + clone_materials, clone_regions, memo + ) else: - clone.universes[i[0]][i[1]][i[2]] = \ - self.universes[i[0]][i[1]][i[2]].clone( - clone_materials, clone_regions, memo) + clone.universes[i[0]][i[1]][i[2]] = self.universes[i[0]][i[1]][ + i[2] + ].clone(clone_materials, clone_regions, memo) # Memoize the clone memo[self] = clone @@ -403,44 +403,47 @@ class RectLattice(Lattice): """ - def __init__(self, lattice_id=None, name=''): + def __init__(self, lattice_id=None, name=""): super().__init__(lattice_id, name) # Initialize Lattice class attributes self._lower_left = None def __repr__(self): - string = 'RectLattice\n' - string += '{: <16}=\t{}\n'.format('\tID', self._id) - string += '{: <16}=\t{}\n'.format('\tName', self._name) - string += '{: <16}=\t{}\n'.format('\tShape', self.shape) - string += '{: <16}=\t{}\n'.format('\tLower Left', self._lower_left) - string += '{: <16}=\t{}\n'.format('\tPitch', self._pitch) - string += '{: <16}=\t{}\n'.format( - '\tOuter', self._outer._id if self._outer is not None else None) - - string += '{: <16}\n'.format('\tUniverses') + string = "RectLattice\n" + string += "{: <16}=\t{}\n".format("\tID", self._id) + string += "{: <16}=\t{}\n".format("\tName", self._name) + string += "{: <16}=\t{}\n".format("\tShape", self.shape) + string += "{: <16}=\t{}\n".format("\tLower Left", self._lower_left) + string += "{: <16}=\t{}\n".format("\tPitch", self._pitch) + string += "{: <16}=\t{}\n".format( + "\tOuter", self._outer._id if self._outer is not None else None + ) + + string += "{: <16}\n".format("\tUniverses") # Lattice nested Universe IDs for i, universe in enumerate(np.ravel(self._universes)): - string += f'{universe._id} ' + string += f"{universe._id} " # Add a newline character every time we reach end of row of cells if (i + 1) % self.shape[0] == 0: - string += '\n' + string += "\n" - string = string.rstrip('\n') + string = string.rstrip("\n") return string @property def indices(self): if self.ndim == 2: - return list(np.broadcast(*np.ogrid[ - :self.shape[1], :self.shape[0]])) + return list(np.broadcast(*np.ogrid[: self.shape[1], : self.shape[0]])) else: - return list(np.broadcast(*np.ogrid[ - :self.shape[2], :self.shape[1], :self.shape[0]])) + return list( + np.broadcast( + *np.ogrid[: self.shape[2], : self.shape[1], : self.shape[0]] + ) + ) @property def _natural_indices(self): @@ -469,8 +472,8 @@ def lower_left(self): @lower_left.setter def lower_left(self, lower_left): - cv.check_type('lattice lower left corner', lower_left, Iterable, Real) - cv.check_length('lattice lower left corner', lower_left, 2, 3) + cv.check_type("lattice lower left corner", lower_left, Iterable, Real) + cv.check_length("lattice lower left corner", lower_left, 2, 3) self._lower_left = lower_left @property @@ -478,8 +481,10 @@ def ndim(self): if self.pitch is not None: return len(self.pitch) else: - raise ValueError('Number of dimensions cannot be determined until ' - 'the lattice pitch has been set.') + raise ValueError( + "Number of dimensions cannot be determined until " + "the lattice pitch has been set." + ) @property def shape(self): @@ -487,16 +492,21 @@ def shape(self): @Lattice.pitch.setter def pitch(self, pitch): - cv.check_type('lattice pitch', pitch, Iterable, Real) - cv.check_length('lattice pitch', pitch, 2, 3) + cv.check_type("lattice pitch", pitch, Iterable, Real) + cv.check_length("lattice pitch", pitch, 2, 3) for dim in pitch: - cv.check_greater_than('lattice pitch', dim, 0.0) + cv.check_greater_than("lattice pitch", dim, 0.0) self._pitch = pitch @Lattice.universes.setter def universes(self, universes): - cv.check_iterable_type('lattice universes', universes, openmc.UniverseBase, - min_depth=2, max_depth=3) + cv.check_iterable_type( + "lattice universes", + universes, + openmc.UniverseBase, + min_depth=2, + max_depth=3, + ) self._universes = np.asarray(universes) def find_element(self, point): @@ -516,12 +526,12 @@ def find_element(self, point): element coordinate system """ - ix = floor((point[0] - self.lower_left[0])/self.pitch[0]) - iy = floor((point[1] - self.lower_left[1])/self.pitch[1]) + ix = floor((point[0] - self.lower_left[0]) / self.pitch[0]) + iy = floor((point[1] - self.lower_left[1]) / self.pitch[1]) if self.ndim == 2: idx = (ix, iy) else: - iz = floor((point[2] - self.lower_left[2])/self.pitch[2]) + iz = floor((point[2] - self.lower_left[2]) / self.pitch[2]) idx = (ix, iy, iz) return idx, self.get_local_coordinates(point, idx) @@ -543,12 +553,12 @@ def get_local_coordinates(self, point, idx): system """ - x = point[0] - (self.lower_left[0] + (idx[0] + 0.5)*self.pitch[0]) - y = point[1] - (self.lower_left[1] + (idx[1] + 0.5)*self.pitch[1]) + x = point[0] - (self.lower_left[0] + (idx[0] + 0.5) * self.pitch[0]) + y = point[1] - (self.lower_left[1] + (idx[1] + 0.5) * self.pitch[1]) if self.ndim == 2: z = point[2] else: - z = point[2] - (self.lower_left[2] + (idx[2] + 0.5)*self.pitch[2]) + z = point[2] - (self.lower_left[2] + (idx[2] + 0.5) * self.pitch[2]) return (x, y, z) def get_universe_index(self, idx): @@ -589,17 +599,22 @@ def is_valid_index(self, idx): """ if self.ndim == 2: - return (0 <= idx[0] < self.shape[0] and - 0 <= idx[1] < self.shape[1]) + return 0 <= idx[0] < self.shape[0] and 0 <= idx[1] < self.shape[1] else: - return (0 <= idx[0] < self.shape[0] and - 0 <= idx[1] < self.shape[1] and - 0 <= idx[2] < self.shape[2]) - - def discretize(self, strategy="degenerate", - universes_to_ignore=[], - materials_to_clone=[], - lattice_neighbors=[], key=lambda univ: univ.id): + return ( + 0 <= idx[0] < self.shape[0] + and 0 <= idx[1] < self.shape[1] + and 0 <= idx[2] < self.shape[2] + ) + + def discretize( + self, + strategy="degenerate", + universes_to_ignore=[], + materials_to_clone=[], + lattice_neighbors=[], + key=lambda univ: univ.id, + ): """Discretize the lattice with either a degenerate or a local neighbor symmetry strategy @@ -635,26 +650,28 @@ def discretize(self, strategy="degenerate", # Check routine inputs if self.ndim != 2: - raise NotImplementedError("LNS discretization is not implemented " - "for 1D and 3D lattices") - - cv.check_value('strategy', strategy, ('degenerate', 'lns')) - cv.check_type('universes_to_ignore', universes_to_ignore, Iterable, - openmc.UniverseBase) - cv.check_type('materials_to_clone', materials_to_clone, Iterable, - openmc.Material) - cv.check_type('lattice_neighbors', lattice_neighbors, Iterable, - openmc.UniverseBase) - cv.check_value('number of lattice_neighbors', len(lattice_neighbors), - (0, 8)) - cv.check_type('key', key, types.FunctionType) + raise NotImplementedError( + "LNS discretization is not implemented " "for 1D and 3D lattices" + ) + + cv.check_value("strategy", strategy, ("degenerate", "lns")) + cv.check_type( + "universes_to_ignore", universes_to_ignore, Iterable, openmc.UniverseBase + ) + cv.check_type( + "materials_to_clone", materials_to_clone, Iterable, openmc.Material + ) + cv.check_type( + "lattice_neighbors", lattice_neighbors, Iterable, openmc.UniverseBase + ) + cv.check_value("number of lattice_neighbors", len(lattice_neighbors), (0, 8)) + cv.check_type("key", key, types.FunctionType) # Use outer universe if neighbors are missing and outer is defined if self.outer is not None and len(lattice_neighbors) == 0: lattice_neighbors = [key(self.outer) for i in range(8)] elif len(lattice_neighbors) == 8: - lattice_neighbors = [key(universe) for universe in - lattice_neighbors] + lattice_neighbors = [key(universe) for universe in lattice_neighbors] # Dictionary that will keep track of where each pattern appears, how # it was rotated and/or symmetrized @@ -710,34 +727,34 @@ def find_lattice_neighbors(pattern, i, j): # Away from left edge if i != 0: if j > 0: - pattern[0, 0] = key(self.universes[j-1][i-1]) - pattern[1, 0] = key(self.universes[j][i-1]) + pattern[0, 0] = key(self.universes[j - 1][i - 1]) + pattern[1, 0] = key(self.universes[j][i - 1]) if j < self.shape[1] - 1: - pattern[2, 0] = key(self.universes[j+1][i-1]) + pattern[2, 0] = key(self.universes[j + 1][i - 1]) # Away from bottom edge if j != 0: if i > 0: - pattern[0, 0] = key(self.universes[j-1][i-1]) - pattern[0, 1] = key(self.universes[j-1][i]) + pattern[0, 0] = key(self.universes[j - 1][i - 1]) + pattern[0, 1] = key(self.universes[j - 1][i]) if i < self.shape[0] - 1: - pattern[0, 2] = key(self.universes[j-1][i+1]) + pattern[0, 2] = key(self.universes[j - 1][i + 1]) # Away from right edge if i != self.shape[0] - 1: if j > 0: - pattern[0, 2] = key(self.universes[j-1][i+1]) - pattern[1, 2] = key(self.universes[j][i+1]) + pattern[0, 2] = key(self.universes[j - 1][i + 1]) + pattern[1, 2] = key(self.universes[j][i + 1]) if j < self.shape[1] - 1: - pattern[2, 2] = key(self.universes[j+1][i+1]) + pattern[2, 2] = key(self.universes[j + 1][i + 1]) # Away from top edge if j != self.shape[1] - 1: if i > 0: - pattern[2, 0] = key(self.universes[j+1][i-1]) - pattern[2, 1] = key(self.universes[j+1][i]) + pattern[2, 0] = key(self.universes[j + 1][i - 1]) + pattern[2, 1] = key(self.universes[j + 1][i]) if i < self.shape[0] - 1: - pattern[2, 2] = key(self.universes[j+1][i+1]) + pattern[2, 2] = key(self.universes[j + 1][i + 1]) # Analyze lattice, find unique patterns in groups of universes for j in range(self.shape[1]): @@ -752,7 +769,7 @@ def find_lattice_neighbors(pattern, i, j): # Degenerate discretization has all universes be different if strategy == "degenerate": - patterns[(i, j)] = {'locations': [(i, j)]} + patterns[(i, j)] = {"locations": [(i, j)]} continue # Find neighbors among lattice's neighbors at the edges @@ -769,12 +786,11 @@ def find_lattice_neighbors(pattern, i, j): # Look at all rotations of pattern for rot in range(4): - if not found and tuple(map(tuple, pattern)) ==\ - known_pattern: + if not found and tuple(map(tuple, pattern)) == known_pattern: found = True # Save location of the pattern in the lattice - pattern_data['locations'].append((i, j)) + pattern_data["locations"].append((i, j)) # Rotate pattern pattern = np.rot90(pattern) @@ -782,12 +798,11 @@ def find_lattice_neighbors(pattern, i, j): # Look at transpose of pattern and its rotations pattern = np.transpose(pattern) for rot in range(4): - if not found and tuple(map(tuple, pattern)) ==\ - known_pattern: + if not found and tuple(map(tuple, pattern)) == known_pattern: found = True # Save location of the pattern in the lattice - pattern_data['locations'].append((i, j)) + pattern_data["locations"].append((i, j)) # Rotate pattern pattern = np.rot90(pattern) @@ -797,17 +812,17 @@ def find_lattice_neighbors(pattern, i, j): # Create new pattern and add to the patterns dictionary if not found: - patterns[tuple(map(tuple, pattern))] =\ - {'locations': [(i, j)]} + patterns[tuple(map(tuple, pattern))] = {"locations": [(i, j)]} # Discretize lattice for pattern, pattern_data in patterns.items(): - first_pos = pattern_data['locations'][0] + first_pos = pattern_data["locations"][0] # Create a clone of the universe, without cloning materials new_universe = self.universes[first_pos[1]][first_pos[0]].clone( - clone_materials=False, clone_regions=False) + clone_materials=False, clone_regions=False + ) # Replace only the materials in materials_to_clone for material in materials_to_clone: @@ -815,7 +830,7 @@ def find_lattice_neighbors(pattern, i, j): for cell in new_universe.get_all_cells().values(): - if cell.fill_type == 'material': + if cell.fill_type == "material": if cell.fill.id == material.id: # Only a single clone of each material is necessary @@ -824,15 +839,21 @@ def find_lattice_neighbors(pattern, i, j): material_cloned = True cell.fill = material_clone - elif cell.fill_type == 'distribmat': - raise(ValueError, "Lattice discretization should not " - "be used with distributed materials") + elif cell.fill_type == "distribmat": + raise ( + ValueError, + "Lattice discretization should not " + "be used with distributed materials", + ) elif len(cell.temperature) > 1 or len(cell.fill) > 1: - raise(ValueError, "Lattice discretization should not " - "be used with distributed cells") + raise ( + ValueError, + "Lattice discretization should not " + "be used with distributed cells", + ) # Rebuild lattice from list of locations with this pattern - for index, location in enumerate(pattern_data['locations']): + for index, location in enumerate(pattern_data["locations"]): self.universes[location[1]][location[0]] = new_universe def create_xml_subelement(self, xml_element, memo=None): @@ -872,7 +893,7 @@ def create_xml_subelement(self, xml_element, memo=None): # Export the Lattice cell pitch pitch = ET.SubElement(lattice_subelement, "pitch") - pitch.text = ' '.join(map(str, self._pitch)) + pitch.text = " ".join(map(str, self._pitch)) # Export the Lattice outer Universe (if specified) if self._outer is not None: @@ -882,7 +903,7 @@ def create_xml_subelement(self, xml_element, memo=None): # Export Lattice cell dimensions dimension = ET.SubElement(lattice_subelement, "dimension") - dimension.text = ' '.join(map(str, self.shape)) + dimension.text = " ".join(map(str, self.shape)) # Make sure lower_left has been specified if self.lower_left is None: @@ -890,10 +911,10 @@ def create_xml_subelement(self, xml_element, memo=None): # Export Lattice lower left lower_left = ET.SubElement(lattice_subelement, "lower_left") - lower_left.text = ' '.join(map(str, self._lower_left)) + lower_left.text = " ".join(map(str, self._lower_left)) # Export the Lattice nested Universe IDs - universe_ids = '\n' + universe_ids = "\n" # 3D Lattices if self.ndim == 3: @@ -903,16 +924,16 @@ def create_xml_subelement(self, xml_element, memo=None): universe = self._universes[z][y][x] # Append Universe ID to the Lattice XML subelement - universe_ids += f'{universe._id} ' + universe_ids += f"{universe._id} " # Create XML subelement for this Universe universe.create_xml_subelement(xml_element, memo) # Add newline character when we reach end of row of cells - universe_ids += '\n' + universe_ids += "\n" # Add newline character when we reach end of row of cells - universe_ids += '\n' + universe_ids += "\n" # 2D Lattices else: @@ -921,16 +942,16 @@ def create_xml_subelement(self, xml_element, memo=None): universe = self._universes[y][x] # Append Universe ID to Lattice XML subelement - universe_ids += f'{universe._id} ' + universe_ids += f"{universe._id} " # Create XML subelement for this Universe universe.create_xml_subelement(xml_element, memo) # Add newline character when we reach end of row of cells - universe_ids += '\n' + universe_ids += "\n" # Remove trailing newline character from Universe IDs string - universe_ids = universe_ids.rstrip('\n') + universe_ids = universe_ids.rstrip("\n") universes = ET.SubElement(lattice_subelement, "universes") universes.text = universe_ids @@ -956,21 +977,21 @@ def from_xml_element(cls, elem, get_universe): Rectangular lattice """ - lat_id = int(get_text(elem, 'id')) - name = get_text(elem, 'name') + lat_id = int(get_text(elem, "id")) + name = get_text(elem, "name") lat = cls(lat_id, name) - lat.lower_left = [float(i) - for i in get_text(elem, 'lower_left').split()] - lat.pitch = [float(i) for i in get_text(elem, 'pitch').split()] - outer = get_text(elem, 'outer') + lat.lower_left = [float(i) for i in get_text(elem, "lower_left").split()] + lat.pitch = [float(i) for i in get_text(elem, "pitch").split()] + outer = get_text(elem, "outer") if outer is not None: lat.outer = get_universe(int(outer)) # Get array of universes - dimension = get_text(elem, 'dimension').split() + dimension = get_text(elem, "dimension").split() shape = np.array(dimension, dtype=int)[::-1] - uarray = np.array([get_universe(int(i)) for i in - get_text(elem, 'universes').split()]) + uarray = np.array( + [get_universe(int(i)) for i in get_text(elem, "universes").split()] + ) uarray.shape = shape lat.universes = uarray return lat @@ -993,15 +1014,15 @@ def from_hdf5(cls, group, universes): Rectangular lattice """ - dimension = group['dimension'][...] - lower_left = group['lower_left'][...] - pitch = group['pitch'][...] - outer = group['outer'][()] - universe_ids = group['universes'][...] + dimension = group["dimension"][...] + lower_left = group["lower_left"][...] + pitch = group["pitch"][...] + outer = group["outer"][()] + universe_ids = group["universes"][...] # Create the Lattice - lattice_id = int(group.name.split('/')[-1].lstrip('lattice ')) - name = group['name'][()].decode() if 'name' in group else '' + lattice_id = int(group.name.split("/")[-1].lstrip("lattice ")) + name = group["name"][()].decode() if "name" in group else "" lattice = cls(lattice_id, name) lattice.lower_left = lower_left lattice.pitch = pitch @@ -1093,39 +1114,35 @@ class HexLattice(Lattice): """ - def __init__(self, lattice_id=None, name=''): + def __init__(self, lattice_id=None, name=""): super().__init__(lattice_id, name) # Initialize Lattice class attributes self._num_rings = None self._num_axial = None self._center = None - self._orientation = 'y' + self._orientation = "y" def __repr__(self): - string = 'HexLattice\n' - string += '{0: <16}{1}{2}\n'.format('\tID', '=\t', self._id) - string += '{0: <16}{1}{2}\n'.format('\tName', '=\t', self._name) - string += '{0: <16}{1}{2}\n'.format('\tOrientation', '=\t', - self._orientation) - string += '{0: <16}{1}{2}\n'.format('\t# Rings', '=\t', self._num_rings) - string += '{0: <16}{1}{2}\n'.format('\t# Axial', '=\t', self._num_axial) - string += '{0: <16}{1}{2}\n'.format('\tCenter', '=\t', - self._center) - string += '{0: <16}{1}{2}\n'.format('\tPitch', '=\t', self._pitch) + string = "HexLattice\n" + string += "{0: <16}{1}{2}\n".format("\tID", "=\t", self._id) + string += "{0: <16}{1}{2}\n".format("\tName", "=\t", self._name) + string += "{0: <16}{1}{2}\n".format("\tOrientation", "=\t", self._orientation) + string += "{0: <16}{1}{2}\n".format("\t# Rings", "=\t", self._num_rings) + string += "{0: <16}{1}{2}\n".format("\t# Axial", "=\t", self._num_axial) + string += "{0: <16}{1}{2}\n".format("\tCenter", "=\t", self._center) + string += "{0: <16}{1}{2}\n".format("\tPitch", "=\t", self._pitch) if self._outer is not None: - string += '{0: <16}{1}{2}\n'.format('\tOuter', '=\t', - self._outer._id) + string += "{0: <16}{1}{2}\n".format("\tOuter", "=\t", self._outer._id) else: - string += '{0: <16}{1}{2}\n'.format('\tOuter', '=\t', - self._outer) + string += "{0: <16}{1}{2}\n".format("\tOuter", "=\t", self._outer) - string += '{0: <16}\n'.format('\tUniverses') + string += "{0: <16}\n".format("\tUniverses") if self._num_axial is not None: slices = [self._repr_axial_slice(x) for x in self._universes] - string += '\n'.join(slices) + string += "\n".join(slices) else: string += self._repr_axial_slice(self._universes) @@ -1142,7 +1159,7 @@ def orientation(self): @orientation.setter def orientation(self, orientation): - cv.check_value('orientation', orientation.lower(), ('x', 'y')) + cv.check_value("orientation", orientation.lower(), ("x", "y")) self._orientation = orientation.lower() @property @@ -1155,19 +1172,25 @@ def center(self): @center.setter def center(self, center): - cv.check_type('lattice center', center, Iterable, Real) - cv.check_length('lattice center', center, 2, 3) + cv.check_type("lattice center", center, Iterable, Real) + cv.check_length("lattice center", center, 2, 3) self._center = center @property def indices(self): if self.num_axial is None: - return [(r, i) for r in range(self.num_rings) - for i in range(max(6*(self.num_rings - 1 - r), 1))] + return [ + (r, i) + for r in range(self.num_rings) + for i in range(max(6 * (self.num_rings - 1 - r), 1)) + ] else: - return [(z, r, i) for z in range(self.num_axial) - for r in range(self.num_rings) - for i in range(max(6*(self.num_rings - 1 - r), 1))] + return [ + (z, r, i) + for z in range(self.num_axial) + for r in range(self.num_rings) + for i in range(max(6 * (self.num_rings - 1 - r), 1)) + ] @property def _natural_indices(self): @@ -1200,16 +1223,21 @@ def ndim(self): @Lattice.pitch.setter def pitch(self, pitch): - cv.check_type('lattice pitch', pitch, Iterable, Real) - cv.check_length('lattice pitch', pitch, 1, 2) + cv.check_type("lattice pitch", pitch, Iterable, Real) + cv.check_length("lattice pitch", pitch, 1, 2) for dim in pitch: - cv.check_greater_than('lattice pitch', dim, 0) + cv.check_greater_than("lattice pitch", dim, 0) self._pitch = pitch @Lattice.universes.setter def universes(self, universes): - cv.check_iterable_type('lattice universes', universes, openmc.UniverseBase, - min_depth=2, max_depth=3) + cv.check_iterable_type( + "lattice universes", + universes, + openmc.UniverseBase, + min_depth=2, + max_depth=3, + ) self._universes = universes # NOTE: This routine assumes that the user creates a "ragged" list of @@ -1230,8 +1258,10 @@ def universes(self, universes): 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 position'.format(self._id) + msg = ( + "HexLattice ID={0:d} has an inconsistent number of " + "rings per axial position".format(self._id) + ) raise ValueError(msg) else: @@ -1242,38 +1272,46 @@ def universes(self, universes): 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) + 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)) + 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) + 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)) + 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 find_element(self, point): @@ -1301,24 +1339,28 @@ def find_element(self, point): iz = 1 else: z = point[2] - self.center[2] - iz = floor(z/self.pitch[1] + 0.5*self.num_axial) - if self._orientation == 'x': - alpha = y - x*sqrt(3.) - i1 = floor(-alpha/(sqrt(3.0) * self.pitch[0])) - i2 = floor(y/(sqrt(0.75) * self.pitch[0])) + iz = floor(z / self.pitch[1] + 0.5 * self.num_axial) + if self._orientation == "x": + alpha = y - x * sqrt(3.0) + i1 = floor(-alpha / (sqrt(3.0) * self.pitch[0])) + i2 = floor(y / (sqrt(0.75) * self.pitch[0])) else: - alpha = y - x/sqrt(3.) - i1 = floor(x/(sqrt(0.75) * self.pitch[0])) - i2 = floor(alpha/self.pitch[0]) + alpha = y - x / sqrt(3.0) + i1 = floor(x / (sqrt(0.75) * self.pitch[0])) + i2 = floor(alpha / self.pitch[0]) # Check four lattice elements to see which one is closest based on local # coordinates - indices = [(i1, i2, iz), (i1 + 1, i2, iz), (i1, i2 + 1, iz), - (i1 + 1, i2 + 1, iz)] + indices = [ + (i1, i2, iz), + (i1 + 1, i2, iz), + (i1, i2 + 1, iz), + (i1 + 1, i2 + 1, iz), + ] d_min = np.inf for idx in indices: p = self.get_local_coordinates(point, idx) - d = p[0]**2 + p[1]**2 + d = p[0] ** 2 + p[1] ** 2 if d < d_min: d_min = d idx_min = idx @@ -1344,18 +1386,19 @@ def get_local_coordinates(self, point, idx): system """ - if self._orientation == 'x': - x = point[0] - (self.center[0] + (idx[0] + 0.5*idx[1])*self.pitch[0]) - y = point[1] - (self.center[1] + sqrt(0.75)*self.pitch[0]*idx[1]) + if self._orientation == "x": + x = point[0] - (self.center[0] + (idx[0] + 0.5 * idx[1]) * self.pitch[0]) + y = point[1] - (self.center[1] + sqrt(0.75) * self.pitch[0] * idx[1]) else: - x = point[0] - (self.center[0] + sqrt(0.75)*self.pitch[0]*idx[0]) - y = point[1] - (self.center[1] + (0.5*idx[0] + idx[1])*self.pitch[0]) + x = point[0] - (self.center[0] + sqrt(0.75) * self.pitch[0] * idx[0]) + y = point[1] - (self.center[1] + (0.5 * idx[0] + idx[1]) * self.pitch[0]) if self._num_axial is None: z = point[2] else: - z = point[2] - (self.center[2] + (idx[2] + 0.5 - 0.5*self.num_axial) * - self.pitch[1]) + z = point[2] - ( + self.center[2] + (idx[2] + 0.5 - 0.5 * self.num_axial) * self.pitch[1] + ) return (x, y, z) def get_universe_index(self, idx): @@ -1389,15 +1432,15 @@ def get_universe_index(self, idx): if a >= 0: i_within = x else: - i_within = 2*g + z + i_within = 2 * g + z else: if a <= 0: - i_within = 3*g - x + i_within = 3 * g - x else: - i_within = 5*g - z + i_within = 5 * g - z - if self._orientation == 'x' and g > 0: - i_within = (i_within + 5*g) % (6*g) + if self._orientation == "x" and g > 0: + i_within = (i_within + 5 * g) % (6 * g) if self.num_axial is None: return (i_ring, i_within) @@ -1444,7 +1487,7 @@ def create_xml_subelement(self, xml_element, memo=None): # Export the Lattice cell pitch pitch = ET.SubElement(lattice_subelement, "pitch") - pitch.text = ' '.join(map(str, self._pitch)) + pitch.text = " ".join(map(str, self._pitch)) # Export the Lattice outer Universe (if specified) if self._outer is not None: @@ -1454,7 +1497,7 @@ def create_xml_subelement(self, xml_element, memo=None): lattice_subelement.set("n_rings", str(self._num_rings)) # If orientation is "x" export it to XML - if self._orientation == 'x': + if self._orientation == "x": lattice_subelement.set("orientation", "x") if self._num_axial is not None: @@ -1462,7 +1505,7 @@ def create_xml_subelement(self, xml_element, memo=None): # Export Lattice cell center center = ET.SubElement(lattice_subelement, "center") - center.text = ' '.join(map(str, self._center)) + center.text = " ".join(map(str, self._center)) # Export the Lattice nested Universe IDs. if self.universes is None: @@ -1477,8 +1520,8 @@ def create_xml_subelement(self, xml_element, memo=None): universe.create_xml_subelement(xml_element, memo) # Initialize the remaining universes. - for r in range(self._num_rings-1): - for theta in range(6*(self._num_rings - 1 - r)): + for r in range(self._num_rings - 1): + for theta in range(6 * (self._num_rings - 1 - r)): universe = self._universes[z][r][theta] universe.create_xml_subelement(xml_element, memo) @@ -1486,7 +1529,7 @@ def create_xml_subelement(self, xml_element, memo=None): slices.append(self._repr_axial_slice(self._universes[z])) # Collapse the list of axial slices into a single string. - universe_ids = '\n'.join(slices) + universe_ids = "\n".join(slices) # 2D Lattices else: @@ -1496,7 +1539,7 @@ def create_xml_subelement(self, xml_element, memo=None): # Initialize the remaining universes. for r in range(self._num_rings - 1): - for theta in range(6*(self._num_rings - 1 - r)): + for theta in range(6 * (self._num_rings - 1 - r)): universe = self._universes[r][theta] universe.create_xml_subelement(xml_element, memo) @@ -1504,7 +1547,7 @@ def create_xml_subelement(self, xml_element, memo=None): universe_ids = self._repr_axial_slice(self._universes) universes = ET.SubElement(lattice_subelement, "universes") - universes.text = '\n' + universe_ids + universes.text = "\n" + universe_ids # Append the XML subelement for this Lattice to the XML element xml_element.append(lattice_subelement) @@ -1527,29 +1570,31 @@ def from_xml_element(cls, elem, get_universe): Hexagonal lattice """ - lat_id = int(get_text(elem, 'id')) - name = get_text(elem, 'name') + lat_id = int(get_text(elem, "id")) + name = get_text(elem, "name") lat = cls(lat_id, name) - lat.center = [float(i) for i in get_text(elem, 'center').split()] - lat.pitch = [float(i) for i in get_text(elem, 'pitch').split()] - lat.orientation = get_text(elem, 'orientation', 'y') - outer = get_text(elem, 'outer') + lat.center = [float(i) for i in get_text(elem, "center").split()] + lat.pitch = [float(i) for i in get_text(elem, "pitch").split()] + lat.orientation = get_text(elem, "orientation", "y") + outer = get_text(elem, "outer") if outer is not None: lat.outer = get_universe(int(outer)) # Get nested lists of universes - lat._num_rings = n_rings = int(get_text(elem, 'n_rings')) - lat._num_axial = n_axial = int(get_text(elem, 'n_axial', 1)) + lat._num_rings = n_rings = int(get_text(elem, "n_rings")) + lat._num_axial = n_axial = int(get_text(elem, "n_axial", 1)) # Create empty nested lists for one axial level - univs = [[None for _ in range(max(6*(n_rings - 1 - r), 1))] - for r in range(n_rings)] + univs = [ + [None for _ in range(max(6 * (n_rings - 1 - r), 1))] for r in range(n_rings) + ] if n_axial > 1: univs = [deepcopy(univs) for i in range(n_axial)] # Get flat array of universes - uarray = np.array([get_universe(int(i)) for i in - get_text(elem, 'universes').split()]) + uarray = np.array( + [get_universe(int(i)) for i in get_text(elem, "universes").split()] + ) # Fill nested lists j = 0 @@ -1557,7 +1602,7 @@ def from_xml_element(cls, elem, get_universe): # Get list for a single axial level axial_level = univs[z] if n_axial > 1 else univs - if lat.orientation == 'y': + if lat.orientation == "y": # Start iterating from top x, alpha = 0, n_rings - 1 while True: @@ -1616,7 +1661,7 @@ def _repr_axial_slice(self, universes): each sub-list represents a single ring. The first list should be the outer ring. """ - if self._orientation == 'x': + if self._orientation == "x": return self._repr_axial_slice_x(universes) else: return self._repr_axial_slice_y(universes) @@ -1632,14 +1677,13 @@ def _repr_axial_slice_x(self, universes): # Find the largest universe ID and count the number of digits so we can # properly pad the output string later. - largest_id = max([max([univ._id for univ in ring]) - for ring in universes]) + largest_id = max([max([univ._id for univ in ring]) for ring in universes]) n_digits = len(str(largest_id)) - pad = ' '*n_digits - id_form = '{: ^' + str(n_digits) + 'd}' + pad = " " * n_digits + id_form = "{: ^" + str(n_digits) + "d}" # Initialize the list for each row. - rows = [[] for i in range(2*self._num_rings - 1)] + rows = [[] for i in range(2 * self._num_rings - 1)] middle = self._num_rings - 1 # Start with the degenerate first ring. @@ -1716,11 +1760,11 @@ def _repr_axial_slice_x(self, universes): # Pad the beginning of the rows so they line up properly. for y in range(self._num_rings - 1): - rows[y] = (self._num_rings - 1 - y)*pad + rows[y] - rows[-1 - y] = (self._num_rings - 1 - y)*pad + rows[-1 - y] + rows[y] = (self._num_rings - 1 - y) * pad + rows[y] + rows[-1 - y] = (self._num_rings - 1 - y) * pad + rows[-1 - y] # Join the rows together and return the string. - universe_ids = '\n'.join(rows) + universe_ids = "\n".join(rows) return universe_ids def _repr_axial_slice_y(self, universes): @@ -1734,14 +1778,13 @@ def _repr_axial_slice_y(self, universes): # Find the largest universe ID and count the number of digits so we can # properly pad the output string later. - largest_id = max([max([univ._id for univ in ring]) - for ring in universes]) + largest_id = max([max([univ._id for univ in ring]) for ring in universes]) n_digits = len(str(largest_id)) - pad = ' '*n_digits - id_form = '{: ^' + str(n_digits) + 'd}' + pad = " " * n_digits + id_form = "{: ^" + str(n_digits) + "d}" # Initialize the list for each row. - rows = [[] for i in range(1 + 4 * (self._num_rings-1))] + rows = [[] for i in range(1 + 4 * (self._num_rings - 1))] middle = 2 * (self._num_rings - 1) # Start with the degenerate first ring. @@ -1753,7 +1796,7 @@ def _repr_axial_slice_y(self, universes): # r_prime increments down while r increments up. r_prime = self._num_rings - 1 - r theta = 0 - y = middle + 2*r + y = middle + 2 * r # Climb down the top-right. for i in range(r): @@ -1820,8 +1863,8 @@ def _repr_axial_slice_y(self, universes): # Pad the beginning of the rows so they line up properly. for y in range(self._num_rings - 1): - rows[y] = (self._num_rings - 1 - y)*pad + rows[y] - rows[-1 - y] = (self._num_rings - 1 - y)*pad + rows[-1 - y] + rows[y] = (self._num_rings - 1 - y) * pad + rows[y] + rows[-1 - y] = (self._num_rings - 1 - y) * pad + rows[-1 - y] for y in range(self._num_rings % 2, self._num_rings, 2): rows[middle + y] = pad + rows[middle + y] @@ -1829,7 +1872,7 @@ def _repr_axial_slice_y(self, universes): rows[middle - y] = pad + rows[middle - y] # Join the rows together and return the string. - universe_ids = '\n'.join(rows) + universe_ids = "\n".join(rows) return universe_ids @staticmethod @@ -1864,14 +1907,14 @@ def _show_indices_y(num_rings): # Find the largest string and count the number of digits so we can # properly pad the output string later - largest_index = 6*(num_rings - 1) + largest_index = 6 * (num_rings - 1) n_digits_index = len(str(largest_index)) n_digits_ring = len(str(num_rings - 1)) - str_form = f'({{:{n_digits_ring}}},{{:{n_digits_index}}})' - pad = ' '*(n_digits_index + n_digits_ring + 3) + str_form = f"({{:{n_digits_ring}}},{{:{n_digits_index}}})" + pad = " " * (n_digits_index + n_digits_ring + 3) # Initialize the list for each row. - rows = [[] for i in range(1 + 4 * (num_rings-1))] + rows = [[] for i in range(1 + 4 * (num_rings - 1))] middle = 2 * (num_rings - 1) # Start with the degenerate first ring. @@ -1882,7 +1925,7 @@ def _show_indices_y(num_rings): # r_prime increments down while r increments up. r_prime = num_rings - 1 - r theta = 0 - y = middle + 2*r + y = middle + 2 * r for i in range(r): # Climb down the top-right. @@ -1925,8 +1968,8 @@ def _show_indices_y(num_rings): # Pad the beginning of the rows so they line up properly. for y in range(num_rings - 1): - rows[y] = (num_rings - 1 - y)*pad + rows[y] - rows[-1 - y] = (num_rings - 1 - y)*pad + rows[-1 - y] + rows[y] = (num_rings - 1 - y) * pad + rows[y] + rows[-1 - y] = (num_rings - 1 - y) * pad + rows[-1 - y] for y in range(num_rings % 2, num_rings, 2): rows[middle + y] = pad + rows[middle + y] @@ -1934,7 +1977,7 @@ def _show_indices_y(num_rings): rows[middle - y] = pad + rows[middle - y] # Join the rows together and return the string. - return '\n'.join(rows) + return "\n".join(rows) @staticmethod def _show_indices_x(num_rings): @@ -1969,14 +2012,14 @@ def _show_indices_x(num_rings): # Find the largest string and count the number of digits so we can # properly pad the output string later - largest_index = 6*(num_rings - 1) + largest_index = 6 * (num_rings - 1) n_digits_index = len(str(largest_index)) n_digits_ring = len(str(num_rings - 1)) - str_form = f'({{:{n_digits_ring}}},{{:{n_digits_index}}})' - pad = ' '*(n_digits_index + n_digits_ring + 3) + str_form = f"({{:{n_digits_ring}}},{{:{n_digits_index}}})" + pad = " " * (n_digits_index + n_digits_ring + 3) # Initialize the list for each row. - rows = [[] for i in range(2*num_rings - 1)] + rows = [[] for i in range(2 * num_rings - 1)] middle = num_rings - 1 # Start with the degenerate first ring. @@ -2028,11 +2071,11 @@ def _show_indices_x(num_rings): # Pad the beginning of the rows so they line up properly. for y in range(num_rings - 1): - rows[y] = (num_rings - 1 - y)*pad + rows[y] - rows[-1 - y] = (num_rings - 1 - y)*pad + rows[-1 - y] + rows[y] = (num_rings - 1 - y) * pad + rows[y] + rows[-1 - y] = (num_rings - 1 - y) * pad + rows[-1 - y] # Join the rows together and return the string. - return '\n\n'.join(rows) + return "\n\n".join(rows) @staticmethod def show_indices(num_rings, orientation="y"): @@ -2052,7 +2095,7 @@ def show_indices(num_rings, orientation="y"): """ - if orientation == 'x': + if orientation == "x": return HexLattice._show_indices_x(num_rings) else: return HexLattice._show_indices_y(num_rings) @@ -2075,20 +2118,20 @@ def from_hdf5(cls, group, universes): Hexagonal lattice """ - n_rings = group['n_rings'][()] - n_axial = group['n_axial'][()] - center = group['center'][()] - pitch = group['pitch'][()] - outer = group['outer'][()] - if 'orientation' in group: - orientation = group['orientation'][()].decode() + n_rings = group["n_rings"][()] + n_axial = group["n_axial"][()] + center = group["center"][()] + pitch = group["pitch"][()] + outer = group["outer"][()] + if "orientation" in group: + orientation = group["orientation"][()].decode() else: orientation = "y" - universe_ids = group['universes'][()] + universe_ids = group["universes"][()] # Create the Lattice - lattice_id = int(group.name.split('/')[-1].lstrip('lattice ')) - name = group['name'][()].decode() if 'name' in group else '' + lattice_id = int(group.name.split("/")[-1].lstrip("lattice ")) + name = group["name"][()].decode() if "name" in group else "" lattice = openmc.HexLattice(lattice_id, name) lattice.center = center lattice.pitch = pitch @@ -2106,7 +2149,7 @@ def from_hdf5(cls, group, universes): # Add a list for this axial level. uarray.append([]) x = n_rings - 1 - a = 2*n_rings - 2 + a = 2 * n_rings - 2 for r in range(n_rings - 1, 0, -1): # Add a list for this ring. uarray[-1].append([]) @@ -2147,8 +2190,7 @@ def from_hdf5(cls, group, universes): a -= 1 # Convert the ids into Universe objects. - uarray[-1][-1] = [universes[u_id] - for u_id in uarray[-1][-1]] + uarray[-1][-1] = [universes[u_id] for u_id in uarray[-1][-1]] # Handle the degenerate center ring separately. u_id = universe_ids[z, a, x] @@ -2162,7 +2204,7 @@ def from_hdf5(cls, group, universes): for z in range(n_axial): # Add a list for this axial level. uarray.append([]) - a = 2*n_rings - 2 + a = 2 * n_rings - 2 y = n_rings - 1 for r in range(n_rings - 1, 0, -1): # Add a list for this ring. @@ -2204,8 +2246,7 @@ def from_hdf5(cls, group, universes): a -= 1 # Convert the ids into Universe objects. - uarray[-1][-1] = [universes[u_id] - for u_id in uarray[-1][-1]] + uarray[-1][-1] = [universes[u_id] for u_id in uarray[-1][-1]] # Handle the degenerate center ring separately. u_id = universe_ids[z, y, a] diff --git a/openmc/lib/__init__.py b/openmc/lib/__init__.py index 9bb2efb38af..4b3eb62b623 100644 --- a/openmc/lib/__init__.py +++ b/openmc/lib/__init__.py @@ -19,14 +19,14 @@ # Determine shared-library suffix -if sys.platform == 'darwin': - _suffix = 'dylib' +if sys.platform == "darwin": + _suffix = "dylib" else: - _suffix = 'so' + _suffix = "so" -if os.environ.get('READTHEDOCS', None) != 'True': +if os.environ.get("READTHEDOCS", None) != "True": # Open shared library - _filename = importlib.resources.files(__name__) / f'libopenmc.{_suffix}' + _filename = importlib.resources.files(__name__) / f"libopenmc.{_suffix}" _dll = CDLL(str(_filename)) # TODO: Remove str() when Python 3.12+ else: # For documentation builds, we don't actually have the shared library @@ -34,24 +34,30 @@ # within the openmc.lib package try to configure arguments and return # values for symbols, no errors occur from unittest.mock import Mock + _dll = Mock() def _dagmc_enabled(): return c_bool.in_dll(_dll, "DAGMC_ENABLED").value + def _ncrystal_enabled(): return c_bool.in_dll(_dll, "NCRYSTAL_ENABLED").value + def _coord_levels(): return c_int.in_dll(_dll, "n_coord_levels").value + def _libmesh_enabled(): return c_bool.in_dll(_dll, "LIBMESH_ENABLED").value + def _mcpl_enabled(): return c_bool.in_dll(_dll, "MCPL_ENABLED").value + def _uwuw_enabled(): return c_bool.in_dll(_dll, "UWUW_ENABLED").value diff --git a/openmc/lib/cell.py b/openmc/lib/cell.py index 971a24cba91..f890e5378f4 100644 --- a/openmc/lib/cell.py +++ b/openmc/lib/cell.py @@ -14,7 +14,7 @@ from ..bounding_box import BoundingBox -__all__ = ['Cell', 'cells'] +__all__ = ["Cell", "cells"] # Cell functions _dll.openmc_extend_cells.argtypes = [c_int32, POINTER(c_int32), POINTER(c_int32)] @@ -24,14 +24,21 @@ _dll.openmc_cell_get_id.restype = c_int _dll.openmc_cell_get_id.errcheck = _error_handler _dll.openmc_cell_get_fill.argtypes = [ - c_int32, POINTER(c_int), POINTER(POINTER(c_int32)), POINTER(c_int32)] + c_int32, + POINTER(c_int), + POINTER(POINTER(c_int32)), + POINTER(c_int32), +] _dll.openmc_cell_get_fill.restype = c_int _dll.openmc_cell_get_fill.errcheck = _error_handler _dll.openmc_cell_get_num_instances.argtypes = [c_int32, POINTER(c_int32)] _dll.openmc_cell_get_num_instances.restype = c_int _dll.openmc_cell_get_num_instances.errcheck = _error_handler _dll.openmc_cell_get_temperature.argtypes = [ - c_int32, POINTER(c_int32), POINTER(c_double)] + c_int32, + POINTER(c_int32), + POINTER(c_double), +] _dll.openmc_cell_get_temperature.restype = c_int _dll.openmc_cell_get_temperature.errcheck = _error_handler _dll.openmc_cell_get_name.argtypes = [c_int32, POINTER(c_char_p)] @@ -40,38 +47,37 @@ _dll.openmc_cell_get_translation.argtypes = [c_int32, POINTER(c_double)] _dll.openmc_cell_get_translation.restype = c_int _dll.openmc_cell_get_translation.errcheck = _error_handler -_dll.openmc_cell_get_rotation.argtypes = [c_int32, POINTER(c_double), - POINTER(c_size_t)] +_dll.openmc_cell_get_rotation.argtypes = [c_int32, POINTER(c_double), POINTER(c_size_t)] _dll.openmc_cell_get_rotation.restype = c_int _dll.openmc_cell_get_rotation.errcheck = _error_handler _dll.openmc_cell_set_name.argtypes = [c_int32, c_char_p] _dll.openmc_cell_set_name.restype = c_int _dll.openmc_cell_set_name.errcheck = _error_handler -_dll.openmc_cell_set_fill.argtypes = [ - c_int32, c_int, c_int32, POINTER(c_int32)] +_dll.openmc_cell_set_fill.argtypes = [c_int32, c_int, c_int32, POINTER(c_int32)] _dll.openmc_cell_set_fill.restype = c_int _dll.openmc_cell_set_fill.errcheck = _error_handler _dll.openmc_cell_set_id.argtypes = [c_int32, c_int32] _dll.openmc_cell_set_id.restype = c_int _dll.openmc_cell_set_id.errcheck = _error_handler _dll.openmc_cell_set_temperature.argtypes = [ - c_int32, c_double, POINTER(c_int32), c_bool] + c_int32, + c_double, + POINTER(c_int32), + c_bool, +] _dll.openmc_cell_set_temperature.restype = c_int _dll.openmc_cell_set_temperature.errcheck = _error_handler _dll.openmc_cell_set_translation.argtypes = [c_int32, POINTER(c_double)] _dll.openmc_cell_set_translation.restype = c_int _dll.openmc_cell_set_translation.errcheck = _error_handler -_dll.openmc_cell_set_rotation.argtypes = [ - c_int32, POINTER(c_double), c_size_t] +_dll.openmc_cell_set_rotation.argtypes = [c_int32, POINTER(c_double), c_size_t] _dll.openmc_cell_set_rotation.restype = c_int _dll.openmc_cell_set_rotation.errcheck = _error_handler _dll.openmc_get_cell_index.argtypes = [c_int32, POINTER(c_int32)] _dll.openmc_get_cell_index.restype = c_int _dll.openmc_get_cell_index.errcheck = _error_handler _dll.cells_size.restype = c_int -_dll.openmc_cell_bounding_box.argtypes = [c_int, - POINTER(c_double), - POINTER(c_double)] +_dll.openmc_cell_bounding_box.argtypes = [c_int, POINTER(c_double), POINTER(c_double)] _dll.openmc_cell_bounding_box.restype = c_int _dll.openmc_cell_bounding_box.errcheck = _error_handler @@ -114,6 +120,7 @@ class Cell(_FortranObjectWithID): respectively. """ + __instances = WeakValueDictionary() def __new__(cls, uid=None, new=True, index=None): @@ -125,8 +132,10 @@ def __new__(cls, uid=None, new=True, index=None): uid = max(mapping, default=0) + 1 else: if uid in mapping: - raise AllocationError('A cell with ID={} has already ' - 'been allocated.'.format(uid)) + raise AllocationError( + "A cell with ID={} has already " + "been allocated.".format(uid) + ) index = c_int32() _dll.openmc_extend_cells(1, index, None) @@ -172,7 +181,7 @@ def fill(self): _dll.openmc_cell_get_fill(self._index, fill_type, indices, n) if fill_type.value == 0: if n.value > 1: - return [Material(index=i) for i in indices[:n.value]] + return [Material(index=i) for i in indices[: n.value]] else: index = indices[0] return Material(index=index) @@ -183,14 +192,13 @@ def fill(self): def fill(self, fill): if isinstance(fill, Iterable): n = len(fill) - indices = (c_int32*n)(*(m._index if m is not None else -1 - for m in fill)) + indices = (c_int32 * n)(*(m._index if m is not None else -1 for m in fill)) _dll.openmc_cell_set_fill(self._index, 0, n, indices) elif isinstance(fill, Material): - indices = (c_int32*1)(fill._index) + indices = (c_int32 * 1)(fill._index) _dll.openmc_cell_set_fill(self._index, 0, 1, indices) elif fill is None: - indices = (c_int32*1)(-1) + indices = (c_int32 * 1)(-1) _dll.openmc_cell_set_fill(self._index, 0, 1, indices) @property @@ -240,14 +248,16 @@ def set_temperature(self, T, instance=None, set_contained=False): def translation(self): translation = np.zeros(3) _dll.openmc_cell_get_translation( - self._index, translation.ctypes.data_as(POINTER(c_double))) + self._index, translation.ctypes.data_as(POINTER(c_double)) + ) return translation @translation.setter def translation(self, translation_vec): vector = np.asarray(translation_vec, dtype=float) _dll.openmc_cell_set_translation( - self._index, vector.ctypes.data_as(POINTER(c_double))) + self._index, vector.ctypes.data_as(POINTER(c_double)) + ) @property def rotation(self): @@ -255,8 +265,8 @@ def rotation(self): rot_size = c_size_t() _dll.openmc_cell_get_rotation( - self._index, rotation_data.ctypes.data_as(POINTER(c_double)), - rot_size) + self._index, rotation_data.ctypes.data_as(POINTER(c_double)), rot_size + ) rot_size = rot_size.value if rot_size == 9: @@ -267,25 +277,28 @@ def rotation(self): # an iterable of floats return rotation_data[9:] else: - raise ValueError( - f'Invalid size of rotation matrix: {rot_size}') + raise ValueError(f"Invalid size of rotation matrix: {rot_size}") @rotation.setter def rotation(self, rotation_data): flat_rotation = np.asarray(rotation_data, dtype=float).flatten() _dll.openmc_cell_set_rotation( - self._index, flat_rotation.ctypes.data_as(POINTER(c_double)), - c_size_t(len(flat_rotation))) + self._index, + flat_rotation.ctypes.data_as(POINTER(c_double)), + c_size_t(len(flat_rotation)), + ) @property def bounding_box(self): inf = sys.float_info.max llc = np.zeros(3) urc = np.zeros(3) - _dll.openmc_cell_bounding_box(self._index, - llc.ctypes.data_as(POINTER(c_double)), - urc.ctypes.data_as(POINTER(c_double))) + _dll.openmc_cell_bounding_box( + self._index, + llc.ctypes.data_as(POINTER(c_double)), + urc.ctypes.data_as(POINTER(c_double)), + ) llc[llc == inf] = np.inf urc[urc == inf] = np.inf llc[llc == -inf] = -np.inf @@ -314,4 +327,5 @@ def __len__(self): def __repr__(self): return repr(dict(self)) + cells = _CellMapping() diff --git a/openmc/lib/core.py b/openmc/lib/core.py index 8561602e670..f0a201ed771 100644 --- a/openmc/lib/core.py +++ b/openmc/lib/core.py @@ -1,7 +1,19 @@ from contextlib import contextmanager -from ctypes import (c_bool, c_int, c_int32, c_int64, c_double, c_char_p, - c_char, POINTER, Structure, c_void_p, create_string_buffer, - c_uint64, c_size_t) +from ctypes import ( + c_bool, + c_int, + c_int32, + c_int64, + c_double, + c_char_p, + c_char, + POINTER, + Structure, + c_void_p, + create_string_buffer, + c_uint64, + c_size_t, +) import sys import os from random import getrandbits @@ -17,24 +29,24 @@ class _SourceSite(Structure): - _fields_ = [('r', c_double*3), - ('u', c_double*3), - ('E', c_double), - ('time', c_double), - ('wgt', c_double), - ('delayed_group', c_int), - ('surf_id', c_int), - ('particle', c_int), - ('parent_id', c_int64), - ('progeny_id', c_int64)] + _fields_ = [ + ("r", c_double * 3), + ("u", c_double * 3), + ("E", c_double), + ("time", c_double), + ("wgt", c_double), + ("delayed_group", c_int), + ("surf_id", c_int), + ("particle", c_int), + ("parent_id", c_int64), + ("progeny_id", c_int64), + ] # Define input type for numpy arrays that will be passed into C++ functions # Must be an int or double array, with single dimension that is contiguous -_array_1d_int = np.ctypeslib.ndpointer(dtype=np.int32, ndim=1, - flags='CONTIGUOUS') -_array_1d_dble = np.ctypeslib.ndpointer(dtype=np.double, ndim=1, - flags='CONTIGUOUS') +_array_1d_int = np.ctypeslib.ndpointer(dtype=np.int32, ndim=1, flags="CONTIGUOUS") +_array_1d_dble = np.ctypeslib.ndpointer(dtype=np.double, ndim=1, flags="CONTIGUOUS") _dll.openmc_calculate_volumes.restype = c_int _dll.openmc_calculate_volumes.errcheck = _error_handler @@ -42,8 +54,11 @@ class _SourceSite(Structure): _dll.openmc_cmfd_reweight.restype = None _dll.openmc_finalize.restype = c_int _dll.openmc_finalize.errcheck = _error_handler -_dll.openmc_find_cell.argtypes = [POINTER(c_double*3), POINTER(c_int32), - POINTER(c_int32)] +_dll.openmc_find_cell.argtypes = [ + POINTER(c_double * 3), + POINTER(c_int32), + POINTER(c_int32), +] _dll.openmc_find_cell.restype = c_int _dll.openmc_find_cell.errcheck = _error_handler _dll.openmc_hard_reset.restype = c_int @@ -51,15 +66,21 @@ class _SourceSite(Structure): _dll.openmc_init.argtypes = [c_int, POINTER(POINTER(c_char)), c_void_p] _dll.openmc_init.restype = c_int _dll.openmc_init.errcheck = _error_handler -_dll.openmc_get_keff.argtypes = [POINTER(c_double*2)] +_dll.openmc_get_keff.argtypes = [POINTER(c_double * 2)] _dll.openmc_get_keff.restype = c_int _dll.openmc_get_keff.errcheck = _error_handler -_dll.openmc_initialize_mesh_egrid.argtypes = [ - c_int, _array_1d_int, c_double -] +_dll.openmc_initialize_mesh_egrid.argtypes = [c_int, _array_1d_int, c_double] _dll.openmc_initialize_mesh_egrid.restype = None -_init_linsolver_argtypes = [_array_1d_int, c_int, _array_1d_int, c_int, c_int, - c_double, _array_1d_int, c_bool] +_init_linsolver_argtypes = [ + _array_1d_int, + c_int, + _array_1d_int, + c_int, + c_int, + c_double, + _array_1d_int, + c_bool, +] _dll.openmc_initialize_linsolver.argtypes = _init_linsolver_argtypes _dll.openmc_initialize_linsolver.restype = None _dll.openmc_is_statepoint_batch.restype = c_bool @@ -81,8 +102,7 @@ class _SourceSite(Structure): _dll.openmc_reset.errcheck = _error_handler _dll.openmc_reset_timers.restype = c_int _dll.openmc_reset_timers.errcheck = _error_handler -_run_linsolver_argtypes = [_array_1d_dble, _array_1d_dble, _array_1d_dble, - c_double] +_run_linsolver_argtypes = [_array_1d_dble, _array_1d_dble, _array_1d_dble, c_double] _dll.openmc_run_linsolver.argtypes = _run_linsolver_argtypes _dll.openmc_run_linsolver.restype = c_int _dll.openmc_source_bank.argtypes = [POINTER(POINTER(_SourceSite)), POINTER(c_int64)] @@ -100,21 +120,26 @@ class _SourceSite(Structure): _dll.openmc_statepoint_load.errcheck = _error_handler _dll.openmc_statepoint_write.restype = c_int _dll.openmc_statepoint_write.errcheck = _error_handler -_dll.openmc_global_bounding_box.argtypes = [POINTER(c_double), - POINTER(c_double)] +_dll.openmc_global_bounding_box.argtypes = [POINTER(c_double), POINTER(c_double)] _dll.openmc_global_bounding_box.restype = c_int _dll.openmc_global_bounding_box.errcheck = _error_handler -_dll.openmc_sample_external_source.argtypes = [c_size_t, POINTER(c_uint64), POINTER(_SourceSite)] +_dll.openmc_sample_external_source.argtypes = [ + c_size_t, + POINTER(c_uint64), + POINTER(_SourceSite), +] _dll.openmc_sample_external_source.restype = c_int _dll.openmc_sample_external_source.errcheck = _error_handler + def global_bounding_box(): """Calculate a global bounding box for the model""" inf = sys.float_info.max llc = np.zeros(3) urc = np.zeros(3) - _dll.openmc_global_bounding_box(llc.ctypes.data_as(POINTER(c_double)), - urc.ctypes.data_as(POINTER(c_double))) + _dll.openmc_global_bounding_box( + llc.ctypes.data_as(POINTER(c_double)), urc.ctypes.data_as(POINTER(c_double)) + ) llc[llc == inf] = np.inf urc[urc == inf] = np.inf llc[llc == -inf] = -np.inf @@ -149,7 +174,7 @@ def current_batch(): Current batch of the simulation """ - return c_int.in_dll(_dll, 'current_batch').value + return c_int.in_dll(_dll, "current_batch").value def export_properties(filename=None, output=True): @@ -200,7 +225,7 @@ def export_weight_windows(filename="weight_windows.h5", output=True): _dll.openmc_weight_windows_export(filename) -def import_weight_windows(filename='weight_windows.h5', output=True): +def import_weight_windows(filename="weight_windows.h5", output=True): """Import weight windows. .. versionadded:: 0.14.0 @@ -249,7 +274,7 @@ def find_cell(xyz): """ index = c_int32() instance = c_int32() - _dll.openmc_find_cell((c_double*3)(*xyz), index, instance) + _dll.openmc_find_cell((c_double * 3)(*xyz), index, instance) return openmc.lib.Cell(index=index.value), instance.value @@ -269,7 +294,7 @@ def find_material(xyz): """ index = c_int32() instance = c_int32() - _dll.openmc_find_cell((c_double*3)(*xyz), index, instance) + _dll.openmc_find_cell((c_double * 3)(*xyz), index, instance) mats = openmc.lib.Cell(index=index.value).fill if isinstance(mats, (openmc.lib.Material, type(None))): @@ -318,9 +343,9 @@ def init(args=None, intracomm=None, output=True): """ if args is not None: - args = ['openmc'] + list(args) + args = ["openmc"] + list(args) else: - args = ['openmc'] + args = ["openmc"] argc = len(args) # Create the argv array. Note that it is actually expected to be of @@ -400,7 +425,7 @@ def keff(): Mean k-eigenvalue and standard deviation of the mean """ - k = (c_double*2)() + k = (c_double * 2)() _dll.openmc_get_keff(k) return tuple(k) @@ -475,8 +500,7 @@ def run(output=True): def sample_external_source( - n_samples: int = 1000, - prn_seed: int | None = None + n_samples: int = 1000, prn_seed: int | None = None ) -> openmc.ParticleList: """Sample external source and return source particles. @@ -503,16 +527,26 @@ def sample_external_source( # Call into C API to sample source sites_array = (_SourceSite * n_samples)() - _dll.openmc_sample_external_source(c_size_t(n_samples), c_uint64(prn_seed), sites_array) + _dll.openmc_sample_external_source( + c_size_t(n_samples), c_uint64(prn_seed), sites_array + ) # Convert to list of SourceParticle and return - return openmc.ParticleList([openmc.SourceParticle( - r=site.r, u=site.u, E=site.E, time=site.time, wgt=site.wgt, - delayed_group=site.delayed_group, surf_id=site.surf_id, - particle=openmc.ParticleType(site.particle) - ) - for site in sites_array - ]) + return openmc.ParticleList( + [ + openmc.SourceParticle( + r=site.r, + u=site.u, + E=site.E, + time=site.time, + wgt=site.wgt, + delayed_group=site.delayed_group, + surf_id=site.surf_id, + particle=openmc.ParticleType(site.particle), + ) + for site in sites_array + ] + ) def simulation_init(): @@ -549,11 +583,13 @@ def source_bank(): # /14214), re-raise with a more helpful error message. if len(err.args) == 0: raise err - if err.args[0].startswith('invalid shape in fixed-type tuple'): - raise ValueError('The source bank is too large to access via ' - 'openmc.lib with this version of numpy. Use a different ' - 'version of numpy or reduce the bank size (fewer particles ' - 'per MPI process) so that it is smaller than 2 GB.') from err + if err.args[0].startswith("invalid shape in fixed-type tuple"): + raise ValueError( + "The source bank is too large to access via " + "openmc.lib with this version of numpy. Use a different " + "version of numpy or reduce the bank size (fewer particles " + "per MPI process) so that it is smaller than 2 GB." + ) from err else: raise err @@ -618,6 +654,7 @@ def run_in_memory(**kwargs): class _DLLGlobal: """Data descriptor that exposes global variables from libopenmc.""" + def __init__(self, ctype, name): self.ctype = ctype self.name = name @@ -678,7 +715,7 @@ def quiet_dll(output=True): os.dup2(devnull, 1) os.close(devnull) # Now point stdout to the re-defined stdout - sys.stdout = os.fdopen(new_stdout, 'w') + sys.stdout = os.fdopen(new_stdout, "w") try: yield diff --git a/openmc/lib/error.py b/openmc/lib/error.py index dbe08e1ef8b..77529685e72 100644 --- a/openmc/lib/error.py +++ b/openmc/lib/error.py @@ -13,27 +13,27 @@ def errcode(s): return c_int.in_dll(_dll, s).value # Get error message set by OpenMC library - errmsg = (c_char*256).in_dll(_dll, 'openmc_err_msg') + errmsg = (c_char * 256).in_dll(_dll, "openmc_err_msg") msg = errmsg.value.decode() # Raise exception type corresponding to error code - if err == errcode('OPENMC_E_ALLOCATE'): + if err == errcode("OPENMC_E_ALLOCATE"): raise exc.AllocationError(msg) - elif err == errcode('OPENMC_E_OUT_OF_BOUNDS'): + elif err == errcode("OPENMC_E_OUT_OF_BOUNDS"): raise exc.OutOfBoundsError(msg) - elif err == errcode('OPENMC_E_INVALID_ARGUMENT'): + elif err == errcode("OPENMC_E_INVALID_ARGUMENT"): raise exc.InvalidArgumentError(msg) - elif err == errcode('OPENMC_E_INVALID_TYPE'): + elif err == errcode("OPENMC_E_INVALID_TYPE"): raise exc.InvalidTypeError(msg) - if err == errcode('OPENMC_E_INVALID_ID'): + if err == errcode("OPENMC_E_INVALID_ID"): raise exc.InvalidIDError(msg) - elif err == errcode('OPENMC_E_GEOMETRY'): + elif err == errcode("OPENMC_E_GEOMETRY"): raise exc.GeometryError(msg) - elif err == errcode('OPENMC_E_DATA'): + elif err == errcode("OPENMC_E_DATA"): raise exc.DataError(msg) - elif err == errcode('OPENMC_E_PHYSICS'): + elif err == errcode("OPENMC_E_PHYSICS"): raise exc.PhysicsError(msg) - elif err == errcode('OPENMC_E_WARNING'): + elif err == errcode("OPENMC_E_WARNING"): warn(msg) elif err < 0: if not msg: diff --git a/openmc/lib/filter.py b/openmc/lib/filter.py index b30f5e66282..99541bf814b 100644 --- a/openmc/lib/filter.py +++ b/openmc/lib/filter.py @@ -1,6 +1,13 @@ from collections.abc import Mapping -from ctypes import c_int, c_int32, c_double, c_char_p, POINTER, \ - create_string_buffer, c_size_t +from ctypes import ( + c_int, + c_int32, + c_double, + c_char_p, + POINTER, + create_string_buffer, + c_size_t, +) from weakref import WeakValueDictionary import numpy as np @@ -17,22 +24,50 @@ __all__ = [ - 'Filter', 'AzimuthalFilter', 'CellFilter', 'CellbornFilter', 'CellfromFilter', - 'CellInstanceFilter', 'CollisionFilter', 'DistribcellFilter', 'DelayedGroupFilter', - 'EnergyFilter', 'EnergyoutFilter', 'EnergyFunctionFilter', 'LegendreFilter', - 'MaterialFilter', 'MaterialFromFilter', 'MeshFilter', 'MeshBornFilter', - 'MeshSurfaceFilter', 'MuFilter', 'MuSurfaceFilter', 'ParticleFilter', 'PolarFilter', - 'SphericalHarmonicsFilter', 'SpatialLegendreFilter', 'SurfaceFilter', 'UniverseFilter', - 'ZernikeFilter', 'ZernikeRadialFilter', 'filters' + "Filter", + "AzimuthalFilter", + "CellFilter", + "CellbornFilter", + "CellfromFilter", + "CellInstanceFilter", + "CollisionFilter", + "DistribcellFilter", + "DelayedGroupFilter", + "EnergyFilter", + "EnergyoutFilter", + "EnergyFunctionFilter", + "LegendreFilter", + "MaterialFilter", + "MaterialFromFilter", + "MeshFilter", + "MeshBornFilter", + "MeshSurfaceFilter", + "MuFilter", + "MuSurfaceFilter", + "ParticleFilter", + "PolarFilter", + "SphericalHarmonicsFilter", + "SpatialLegendreFilter", + "SurfaceFilter", + "UniverseFilter", + "ZernikeFilter", + "ZernikeRadialFilter", + "filters", ] # Tally functions _dll.openmc_cell_filter_get_bins.argtypes = [ - c_int32, POINTER(POINTER(c_int32)), POINTER(c_int32)] + c_int32, + POINTER(POINTER(c_int32)), + POINTER(c_int32), +] _dll.openmc_cell_filter_get_bins.restype = c_int _dll.openmc_cell_filter_get_bins.errcheck = _error_handler _dll.openmc_energy_filter_get_bins.argtypes = [ - c_int32, POINTER(POINTER(c_double)), POINTER(c_size_t)] + c_int32, + POINTER(POINTER(c_double)), + POINTER(c_size_t), +] _dll.openmc_energy_filter_get_bins.restype = c_int _dll.openmc_energy_filter_get_bins.errcheck = _error_handler _dll.openmc_energy_filter_set_bins.argtypes = [c_int32, c_size_t, POINTER(c_double)] @@ -41,15 +76,25 @@ _dll.openmc_energyfunc_filter_set_data.restype = c_int _dll.openmc_energyfunc_filter_set_data.errcheck = _error_handler _dll.openmc_energyfunc_filter_set_data.argtypes = [ - c_int32, c_size_t, POINTER(c_double), POINTER(c_double)] + c_int32, + c_size_t, + POINTER(c_double), + POINTER(c_double), +] _dll.openmc_energyfunc_filter_get_energy.resttpe = c_int _dll.openmc_energyfunc_filter_get_energy.errcheck = _error_handler _dll.openmc_energyfunc_filter_get_energy.argtypes = [ - c_int32, POINTER(c_size_t), POINTER(POINTER(c_double))] + c_int32, + POINTER(c_size_t), + POINTER(POINTER(c_double)), +] _dll.openmc_energyfunc_filter_get_y.resttpe = c_int _dll.openmc_energyfunc_filter_get_y.errcheck = _error_handler _dll.openmc_energyfunc_filter_get_y.argtypes = [ - c_int32, POINTER(c_size_t), POINTER(POINTER(c_double))] + c_int32, + POINTER(c_size_t), + POINTER(POINTER(c_double)), +] _dll.openmc_energyfunc_filter_get_interpolation.resttpe = c_int _dll.openmc_energyfunc_filter_get_interpolation.errcheck = _error_handler _dll.openmc_energyfunc_filter_get_interpolation.argtypes = [c_int32, POINTER(c_int)] @@ -78,7 +123,10 @@ _dll.openmc_legendre_filter_set_order.restype = c_int _dll.openmc_legendre_filter_set_order.errcheck = _error_handler _dll.openmc_material_filter_get_bins.argtypes = [ - c_int32, POINTER(POINTER(c_int32)), POINTER(c_size_t)] + c_int32, + POINTER(POINTER(c_int32)), + POINTER(c_size_t), +] _dll.openmc_material_filter_get_bins.restype = c_int _dll.openmc_material_filter_get_bins.errcheck = _error_handler _dll.openmc_material_filter_set_bins.argtypes = [c_int32, c_size_t, POINTER(c_int32)] @@ -90,10 +138,10 @@ _dll.openmc_mesh_filter_set_mesh.argtypes = [c_int32, c_int32] _dll.openmc_mesh_filter_set_mesh.restype = c_int _dll.openmc_mesh_filter_set_mesh.errcheck = _error_handler -_dll.openmc_mesh_filter_get_translation.argtypes = [c_int32, POINTER(c_double*3)] +_dll.openmc_mesh_filter_get_translation.argtypes = [c_int32, POINTER(c_double * 3)] _dll.openmc_mesh_filter_get_translation.restype = c_int _dll.openmc_mesh_filter_get_translation.errcheck = _error_handler -_dll.openmc_mesh_filter_set_translation.argtypes = [c_int32, POINTER(c_double*3)] +_dll.openmc_mesh_filter_set_translation.argtypes = [c_int32, POINTER(c_double * 3)] _dll.openmc_mesh_filter_set_translation.restype = c_int _dll.openmc_mesh_filter_set_translation.errcheck = _error_handler _dll.openmc_meshborn_filter_get_mesh.argtypes = [c_int32, POINTER(c_int32)] @@ -102,10 +150,10 @@ _dll.openmc_meshborn_filter_set_mesh.argtypes = [c_int32, c_int32] _dll.openmc_meshborn_filter_set_mesh.restype = c_int _dll.openmc_meshborn_filter_set_mesh.errcheck = _error_handler -_dll.openmc_meshborn_filter_get_translation.argtypes = [c_int32, POINTER(c_double*3)] +_dll.openmc_meshborn_filter_get_translation.argtypes = [c_int32, POINTER(c_double * 3)] _dll.openmc_meshborn_filter_get_translation.restype = c_int _dll.openmc_meshborn_filter_get_translation.errcheck = _error_handler -_dll.openmc_meshborn_filter_set_translation.argtypes = [c_int32, POINTER(c_double*3)] +_dll.openmc_meshborn_filter_set_translation.argtypes = [c_int32, POINTER(c_double * 3)] _dll.openmc_meshborn_filter_set_translation.restype = c_int _dll.openmc_meshborn_filter_set_translation.errcheck = _error_handler _dll.openmc_meshsurface_filter_get_mesh.argtypes = [c_int32, POINTER(c_int32)] @@ -114,10 +162,16 @@ _dll.openmc_meshsurface_filter_set_mesh.argtypes = [c_int32, c_int32] _dll.openmc_meshsurface_filter_set_mesh.restype = c_int _dll.openmc_meshsurface_filter_set_mesh.errcheck = _error_handler -_dll.openmc_meshsurface_filter_get_translation.argtypes = [c_int32, POINTER(c_double*3)] +_dll.openmc_meshsurface_filter_get_translation.argtypes = [ + c_int32, + POINTER(c_double * 3), +] _dll.openmc_meshsurface_filter_get_translation.restype = c_int _dll.openmc_meshsurface_filter_get_translation.errcheck = _error_handler -_dll.openmc_meshsurface_filter_set_translation.argtypes = [c_int32, POINTER(c_double*3)] +_dll.openmc_meshsurface_filter_set_translation.argtypes = [ + c_int32, + POINTER(c_double * 3), +] _dll.openmc_meshsurface_filter_set_translation.restype = c_int _dll.openmc_meshsurface_filter_set_translation.errcheck = _error_handler _dll.openmc_new_filter.argtypes = [c_char_p, POINTER(c_int32)] @@ -143,6 +197,7 @@ _dll.openmc_zernike_filter_set_order.errcheck = _error_handler _dll.tally_filters_size.restype = c_size_t + class Filter(_FortranObjectWithID): __instances = WeakValueDictionary() @@ -155,8 +210,10 @@ def __new__(cls, obj=None, uid=None, new=True, index=None): uid = max(mapping, default=0) + 1 else: if uid in mapping: - raise AllocationError('A filter with ID={} has already ' - 'been allocated.'.format(uid)) + raise AllocationError( + "A filter with ID={} has already " + "been allocated.".format(uid) + ) # Set the filter type -- note that the filter_type attribute # only exists on subclasses! @@ -193,7 +250,7 @@ def n_bins(self): class EnergyFilter(Filter): - filter_type = 'energy' + filter_type = "energy" def __init__(self, bins=None, uid=None, new=True, index=None): super().__init__(uid, new, index) @@ -213,24 +270,23 @@ def bins(self, bins): energies = np.asarray(bins) energies_p = energies.ctypes.data_as(POINTER(c_double)) - _dll.openmc_energy_filter_set_bins( - self._index, len(energies), energies_p) + _dll.openmc_energy_filter_set_bins(self._index, len(energies), energies_p) class CollisionFilter(Filter): - filter_type = 'collision' + filter_type = "collision" class EnergyoutFilter(EnergyFilter): - filter_type = 'energyout' + filter_type = "energyout" class AzimuthalFilter(Filter): - filter_type = 'azimuthal' + filter_type = "azimuthal" class CellFilter(Filter): - filter_type = 'cell' + filter_type = "cell" @property def bins(self): @@ -241,27 +297,27 @@ def bins(self): class CellbornFilter(Filter): - filter_type = 'cellborn' + filter_type = "cellborn" class CellfromFilter(Filter): - filter_type = 'cellfrom' + filter_type = "cellfrom" class CellInstanceFilter(Filter): - filter_type = 'cellinstance' + filter_type = "cellinstance" class DelayedGroupFilter(Filter): - filter_type = 'delayedgroup' + filter_type = "delayedgroup" class DistribcellFilter(Filter): - filter_type = 'distribcell' + filter_type = "distribcell" class EnergyFunctionFilter(Filter): - filter_type = 'energyfunction' + filter_type = "energyfunction" def __new__(cls, energy=None, y=None, uid=None, new=True, index=None): return super().__new__(cls, uid=uid, new=new, index=index) @@ -291,7 +347,8 @@ def set_data(self, energy, y): y_p = y_array.ctypes.data_as(POINTER(c_double)) _dll.openmc_energyfunc_filter_set_data( - self._index, len(energy_array), energy_p, y_p) + self._index, len(energy_array), energy_p, y_p + ) @property def energy(self): @@ -316,11 +373,11 @@ def _get_attr(self, cfunc): array_p = POINTER(c_double)() n = c_size_t() cfunc(self._index, n, array_p) - return as_array(array_p, (n.value, )) + return as_array(array_p, (n.value,)) class LegendreFilter(Filter): - filter_type = 'legendre' + filter_type = "legendre" def __init__(self, order=None, uid=None, new=True, index=None): super().__init__(uid, new, index) @@ -339,7 +396,7 @@ def order(self, order): class MaterialFilter(Filter): - filter_type = 'material' + filter_type = "material" def __init__(self, bins=None, uid=None, new=True, index=None): super().__init__(uid, new, index) @@ -357,12 +414,12 @@ def bins(self): def bins(self, materials): # Get material indices as int32_t[] n = len(materials) - bins = (c_int32*n)(*(m._index for m in materials)) + bins = (c_int32 * n)(*(m._index for m in materials)) _dll.openmc_material_filter_set_bins(self._index, n, bins) class MaterialFromFilter(Filter): - filter_type = 'materialfrom' + filter_type = "materialfrom" class MeshFilter(Filter): @@ -394,7 +451,8 @@ class MeshFilter(Filter): 3-D coordinates of the translation vector """ - filter_type = 'mesh' + + filter_type = "mesh" def __init__(self, mesh=None, uid=None, new=True, index=None): super().__init__(uid, new, index) @@ -413,13 +471,15 @@ def mesh(self, mesh): @property def translation(self): - translation = (c_double*3)() + translation = (c_double * 3)() _dll.openmc_mesh_filter_get_translation(self._index, translation) return tuple(translation) @translation.setter def translation(self, translation): - _dll.openmc_mesh_filter_set_translation(self._index, (c_double*3)(*translation)) + _dll.openmc_mesh_filter_set_translation( + self._index, (c_double * 3)(*translation) + ) class MeshBornFilter(Filter): @@ -451,7 +511,8 @@ class MeshBornFilter(Filter): 3-D coordinates of the translation vector """ - filter_type = 'meshborn' + + filter_type = "meshborn" def __init__(self, mesh=None, uid=None, new=True, index=None): super().__init__(uid, new, index) @@ -470,13 +531,15 @@ def mesh(self, mesh): @property def translation(self): - translation = (c_double*3)() + translation = (c_double * 3)() _dll.openmc_meshborn_filter_get_translation(self._index, translation) return tuple(translation) @translation.setter def translation(self, translation): - _dll.openmc_meshborn_filter_set_translation(self._index, (c_double*3)(*translation)) + _dll.openmc_meshborn_filter_set_translation( + self._index, (c_double * 3)(*translation) + ) class MeshSurfaceFilter(Filter): @@ -508,7 +571,8 @@ class MeshSurfaceFilter(Filter): 3-D coordinates of the translation vector """ - filter_type = 'meshsurface' + + filter_type = "meshsurface" def __init__(self, mesh=None, uid=None, new=True, index=None): super().__init__(uid, new, index) @@ -527,40 +591,43 @@ def mesh(self, mesh): @property def translation(self): - translation = (c_double*3)() + translation = (c_double * 3)() _dll.openmc_meshsurface_filter_get_translation(self._index, translation) return tuple(translation) @translation.setter def translation(self, translation): - _dll.openmc_meshsurface_filter_set_translation(self._index, (c_double*3)(*translation)) + _dll.openmc_meshsurface_filter_set_translation( + self._index, (c_double * 3)(*translation) + ) class MuFilter(Filter): - filter_type = 'mu' + filter_type = "mu" class MuSurfaceFilter(Filter): - filter_type = 'musurface' + filter_type = "musurface" class ParticleFilter(Filter): - filter_type = 'particle' + filter_type = "particle" @property def bins(self): particle_i = np.zeros((self.n_bins,), dtype=c_int) _dll.openmc_particle_filter_get_bins( - self._index, particle_i.ctypes.data_as(POINTER(c_int))) + self._index, particle_i.ctypes.data_as(POINTER(c_int)) + ) return [ParticleType(i) for i in particle_i] class PolarFilter(Filter): - filter_type = 'polar' + filter_type = "polar" class SphericalHarmonicsFilter(Filter): - filter_type = 'sphericalharmonics' + filter_type = "sphericalharmonics" def __init__(self, order=None, uid=None, new=True, index=None): super().__init__(uid, new, index) @@ -579,7 +646,7 @@ def order(self, order): class SpatialLegendreFilter(Filter): - filter_type = 'spatiallegendre' + filter_type = "spatiallegendre" def __init__(self, order=None, uid=None, new=True, index=None): super().__init__(uid, new, index) @@ -598,15 +665,15 @@ def order(self, order): class SurfaceFilter(Filter): - filter_type = 'surface' + filter_type = "surface" class UniverseFilter(Filter): - filter_type = 'universe' + filter_type = "universe" class ZernikeFilter(Filter): - filter_type = 'zernike' + filter_type = "zernike" def __init__(self, order=None, uid=None, new=True, index=None): super().__init__(uid, new, index) @@ -625,36 +692,36 @@ def order(self, order): class ZernikeRadialFilter(ZernikeFilter): - filter_type = 'zernikeradial' + filter_type = "zernikeradial" _FILTER_TYPE_MAP = { - 'azimuthal': AzimuthalFilter, - 'cell': CellFilter, - 'cellborn': CellbornFilter, - 'cellfrom': CellfromFilter, - 'cellinstance': CellInstanceFilter, - 'delayedgroup': DelayedGroupFilter, - 'distribcell': DistribcellFilter, - 'energy': EnergyFilter, - 'energyout': EnergyoutFilter, - 'energyfunction': EnergyFunctionFilter, - 'legendre': LegendreFilter, - 'material': MaterialFilter, - 'materialfrom': MaterialFromFilter, - 'mesh': MeshFilter, - 'meshborn': MeshBornFilter, - 'meshsurface': MeshSurfaceFilter, - 'mu': MuFilter, - 'musurface': MuSurfaceFilter, - 'particle': ParticleFilter, - 'polar': PolarFilter, - 'sphericalharmonics': SphericalHarmonicsFilter, - 'spatiallegendre': SpatialLegendreFilter, - 'surface': SurfaceFilter, - 'universe': UniverseFilter, - 'zernike': ZernikeFilter, - 'zernikeradial': ZernikeRadialFilter + "azimuthal": AzimuthalFilter, + "cell": CellFilter, + "cellborn": CellbornFilter, + "cellfrom": CellfromFilter, + "cellinstance": CellInstanceFilter, + "delayedgroup": DelayedGroupFilter, + "distribcell": DistribcellFilter, + "energy": EnergyFilter, + "energyout": EnergyoutFilter, + "energyfunction": EnergyFunctionFilter, + "legendre": LegendreFilter, + "material": MaterialFilter, + "materialfrom": MaterialFromFilter, + "mesh": MeshFilter, + "meshborn": MeshBornFilter, + "meshsurface": MeshSurfaceFilter, + "mu": MuFilter, + "musurface": MuSurfaceFilter, + "particle": ParticleFilter, + "polar": PolarFilter, + "sphericalharmonics": SphericalHarmonicsFilter, + "spatiallegendre": SpatialLegendreFilter, + "surface": SurfaceFilter, + "universe": UniverseFilter, + "zernike": ZernikeFilter, + "zernikeradial": ZernikeRadialFilter, } @@ -685,4 +752,5 @@ def __len__(self): def __repr__(self): return repr(dict(self)) + filters = _FilterMapping() diff --git a/openmc/lib/material.py b/openmc/lib/material.py index 0ed8932da8f..37f1f95dead 100644 --- a/openmc/lib/material.py +++ b/openmc/lib/material.py @@ -11,7 +11,7 @@ from .error import _error_handler -__all__ = ['Material', 'materials'] +__all__ = ["Material", "materials"] # Material functions _dll.openmc_extend_materials.argtypes = [c_int32, POINTER(c_int32), POINTER(c_int32)] @@ -20,16 +20,18 @@ _dll.openmc_get_material_index.argtypes = [c_int32, POINTER(c_int32)] _dll.openmc_get_material_index.restype = c_int _dll.openmc_get_material_index.errcheck = _error_handler -_dll.openmc_material_add_nuclide.argtypes = [ - c_int32, c_char_p, c_double] +_dll.openmc_material_add_nuclide.argtypes = [c_int32, c_char_p, c_double] _dll.openmc_material_add_nuclide.restype = c_int _dll.openmc_material_add_nuclide.errcheck = _error_handler _dll.openmc_material_get_id.argtypes = [c_int32, POINTER(c_int32)] _dll.openmc_material_get_id.restype = c_int _dll.openmc_material_get_id.errcheck = _error_handler _dll.openmc_material_get_densities.argtypes = [ - c_int32, POINTER(POINTER(c_int)), POINTER(POINTER(c_double)), - POINTER(c_int)] + c_int32, + POINTER(POINTER(c_int)), + POINTER(POINTER(c_double)), + POINTER(c_int), +] _dll.openmc_material_get_densities.restype = c_int _dll.openmc_material_get_densities.errcheck = _error_handler _dll.openmc_material_get_density.argtypes = [c_int32, POINTER(c_double)] @@ -45,7 +47,11 @@ _dll.openmc_material_set_density.restype = c_int _dll.openmc_material_set_density.errcheck = _error_handler _dll.openmc_material_set_densities.argtypes = [ - c_int32, c_int, POINTER(c_char_p), POINTER(c_double)] + c_int32, + c_int, + POINTER(c_char_p), + POINTER(c_double), +] _dll.openmc_material_set_densities.restype = c_int _dll.openmc_material_set_densities.errcheck = _error_handler _dll.openmc_material_set_id.argtypes = [c_int32, c_int32] @@ -105,6 +111,7 @@ class Material(_FortranObjectWithID): Volume of the material in [cm^3] """ + __instances = WeakValueDictionary() def __new__(cls, uid=None, new=True, index=None): @@ -116,8 +123,10 @@ def __new__(cls, uid=None, new=True, index=None): uid = max(mapping, default=0) + 1 else: if uid in mapping: - raise AllocationError('A material with ID={} has already ' - 'been allocated.'.format(uid)) + raise AllocationError( + "A material with ID={} has already " + "been allocated.".format(uid) + ) index = c_int32() _dll.openmc_extend_materials(1, index, None) @@ -232,7 +241,7 @@ def add_nuclide(self, name, density): """ _dll.openmc_material_add_nuclide(self._index, name.encode(), density) - def get_density(self, units='atom/b-cm'): + def get_density(self, units="atom/b-cm"): """Get density of a material. Parameters @@ -246,16 +255,16 @@ def get_density(self, units='atom/b-cm'): Density in requested units """ - if units == 'atom/b-cm': + if units == "atom/b-cm": return self.densities.sum() - elif units == 'g/cm3': + elif units == "g/cm3": density = c_double() _dll.openmc_material_get_density(self._index, density) return density.value else: raise ValueError("Units must be 'atom/b-cm' or 'g/cm3'") - def set_density(self, density, units='atom/b-cm'): + def set_density(self, density, units="atom/b-cm"): """Set density of a material. Parameters @@ -310,4 +319,5 @@ def __len__(self): def __repr__(self): return repr(dict(self)) + materials = _MaterialMapping() diff --git a/openmc/lib/math.py b/openmc/lib/math.py index 8c62f241624..2f20e6be93d 100644 --- a/openmc/lib/math.py +++ b/openmc/lib/math.py @@ -14,7 +14,7 @@ def calc_zn(n, rho, phi): - """ Calculate the n-th order modified Zernike polynomial moment for a + """Calculate the n-th order modified Zernike polynomial moment for a given angle (rho, theta) location in the unit disk. The normalization of the polynomials is such that the integral of Z_pq*Z_pq over the unit disk is exactly pi @@ -39,7 +39,7 @@ def calc_zn(n, rho, phi): def calc_zn_rad(n, rho): - """ Calculate the even orders in n-th order modified Zernike polynomial + """Calculate the even orders in n-th order modified Zernike polynomial moment with no azimuthal dependency (m=0) for a given radial location in the unit disk. The normalization of the polynomials is such that the integral of Z_pq*Z_pq over the unit disk is exactly pi. diff --git a/openmc/lib/mesh.py b/openmc/lib/mesh.py index 16bec019863..40e79ea55a0 100644 --- a/openmc/lib/mesh.py +++ b/openmc/lib/mesh.py @@ -1,6 +1,15 @@ from collections.abc import Mapping, Sequence -from ctypes import (c_int, c_int32, c_char_p, c_double, POINTER, Structure, - create_string_buffer, c_uint64, c_size_t) +from ctypes import ( + c_int, + c_int32, + c_char_p, + c_double, + POINTER, + Structure, + create_string_buffer, + c_uint64, + c_size_t, +) from random import getrandbits import sys from weakref import WeakValueDictionary @@ -17,21 +26,27 @@ from ..bounding_box import BoundingBox __all__ = [ - 'Mesh', 'RegularMesh', 'RectilinearMesh', 'CylindricalMesh', - 'SphericalMesh', 'UnstructuredMesh', 'meshes' + "Mesh", + "RegularMesh", + "RectilinearMesh", + "CylindricalMesh", + "SphericalMesh", + "UnstructuredMesh", + "meshes", ] class _MaterialVolume(Structure): - _fields_ = [ - ("material", c_int32), - ("volume", c_double) - ] + _fields_ = [("material", c_int32), ("volume", c_double)] # Mesh functions -_dll.openmc_extend_meshes.argtypes = [c_int32, c_char_p, 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)] @@ -46,17 +61,27 @@ class _MaterialVolume(Structure): _dll.openmc_mesh_get_volumes.argtypes = [c_int32, POINTER(c_double)] _dll.openmc_mesh_get_volumes.restype = c_int _dll.openmc_mesh_get_volumes.errcheck = _error_handler -_dll.openmc_mesh_bounding_box.argtypes = [ - c_int32, POINTER(c_double), POINTER(c_double)] +_dll.openmc_mesh_bounding_box.argtypes = [c_int32, POINTER(c_double), POINTER(c_double)] _dll.openmc_mesh_bounding_box.restype = c_int _dll.openmc_mesh_bounding_box.errcheck = _error_handler _dll.openmc_mesh_material_volumes.argtypes = [ - c_int32, c_int, c_int, c_int, POINTER(_MaterialVolume), - POINTER(c_int), POINTER(c_uint64)] + c_int32, + c_int, + c_int, + c_int, + POINTER(_MaterialVolume), + POINTER(c_int), + POINTER(c_uint64), +] _dll.openmc_mesh_material_volumes.restype = c_int _dll.openmc_mesh_material_volumes.errcheck = _error_handler _dll.openmc_mesh_get_plot_bins.argtypes = [ - c_int32, _Position, _Position, c_int, POINTER(c_int), POINTER(c_int32) + c_int32, + _Position, + _Position, + c_int, + POINTER(c_int), + POINTER(c_int32), ] _dll.openmc_mesh_get_plot_bins.restype = c_int _dll.openmc_mesh_get_plot_bins.errcheck = _error_handler @@ -65,58 +90,107 @@ class _MaterialVolume(Structure): _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.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.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.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)] + 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.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)] + 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 -_dll.openmc_cylindrical_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_cylindrical_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_cylindrical_mesh_get_grid.restype = c_int _dll.openmc_cylindrical_mesh_get_grid.errcheck = _error_handler -_dll.openmc_cylindrical_mesh_set_grid.argtypes = [c_int32, POINTER(c_double), - c_int, POINTER(c_double), c_int, POINTER(c_double), c_int] +_dll.openmc_cylindrical_mesh_set_grid.argtypes = [ + c_int32, + POINTER(c_double), + c_int, + POINTER(c_double), + c_int, + POINTER(c_double), + c_int, +] _dll.openmc_cylindrical_mesh_set_grid.restype = c_int _dll.openmc_cylindrical_mesh_set_grid.errcheck = _error_handler -_dll.openmc_spherical_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_spherical_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_spherical_mesh_get_grid.restype = c_int _dll.openmc_spherical_mesh_get_grid.errcheck = _error_handler -_dll.openmc_spherical_mesh_set_grid.argtypes = [c_int32, POINTER(c_double), - c_int, POINTER(c_double), c_int, POINTER(c_double), c_int] +_dll.openmc_spherical_mesh_set_grid.argtypes = [ + c_int32, + POINTER(c_double), + c_int, + POINTER(c_double), + c_int, + POINTER(c_double), + c_int, +] _dll.openmc_spherical_mesh_set_grid.restype = c_int _dll.openmc_spherical_mesh_set_grid.errcheck = _error_handler class Mesh(_FortranObjectWithID): - """Base class to represent mesh objects + """Base class to represent mesh objects""" - """ __instances = WeakValueDictionary() def __new__(cls, uid=None, new=True, index=None): @@ -128,14 +202,15 @@ def __new__(cls, uid=None, new=True, index=None): uid = max(mapping, default=0) + 1 else: if uid in mapping: - raise AllocationError('A mesh with ID={} has already ' - 'been allocated.'.format(uid)) + 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) + _dll.openmc_extend_meshes(1, cls.mesh_type.encode(), index, None) index = index.value else: index = mapping[uid]._index @@ -169,7 +244,8 @@ def n_elements(self) -> int: def volumes(self) -> np.ndarray: volumes = np.empty((self.n_elements,)) _dll.openmc_mesh_get_volumes( - self._index, volumes.ctypes.data_as(POINTER(c_double))) + self._index, volumes.ctypes.data_as(POINTER(c_double)) + ) return volumes @property @@ -180,7 +256,7 @@ def bounding_box(self) -> BoundingBox: _dll.openmc_mesh_bounding_box( self._index, ll.ctypes.data_as(POINTER(c_double)), - ur.ctypes.data_as(POINTER(c_double)) + ur.ctypes.data_as(POINTER(c_double)), ) ll[ll == inf] = np.inf ur[ur == inf] = np.inf @@ -189,9 +265,7 @@ def bounding_box(self) -> BoundingBox: return BoundingBox(ll, ur) def material_volumes( - self, - n_samples: int = 10_000, - prn_seed: int | None = None + self, n_samples: int = 10_000, prn_seed: int | None = None ) -> list[list[tuple[Material, float]]]: """Determine volume of materials in each mesh element @@ -228,7 +302,8 @@ def material_volumes( while True: try: _dll.openmc_mesh_material_volumes( - self._index, n_samples, i_element, size, result, hits, prn_seed) + self._index, n_samples, i_element, size, result, hits, prn_seed + ) except AllocationError: # Increase size of result array and try again size *= 2 @@ -237,18 +312,17 @@ def material_volumes( # If no error, break out of loop break - volumes.append([ - (Material(index=r.material), r.volume) - for r in result[:hits.value] - ]) + volumes.append( + [(Material(index=r.material), r.volume) for r in result[: hits.value]] + ) return volumes def get_plot_bins( - self, - origin: Sequence[float], - width: Sequence[float], - basis: str, - pixels: Sequence[int] + self, + origin: Sequence[float], + width: Sequence[float], + basis: str, + pixels: Sequence[int], ) -> np.ndarray: """Get mesh bin indices for a rasterized plot. @@ -273,13 +347,17 @@ def get_plot_bins( """ origin = _Position(*origin) width = _Position(*width) - basis = {'xy': 1, 'xz': 2, 'yz': 3}[basis] - pixel_array = (c_int*2)(*pixels) - img_data = np.zeros((pixels[1], pixels[0]), dtype=np.dtype('int32')) + basis = {"xy": 1, "xz": 2, "yz": 3}[basis] + pixel_array = (c_int * 2)(*pixels) + img_data = np.zeros((pixels[1], pixels[0]), dtype=np.dtype("int32")) _dll.openmc_mesh_get_plot_bins( - self._index, origin, width, basis, pixel_array, - img_data.ctypes.data_as(POINTER(c_int32)) + self._index, + origin, + width, + basis, + pixel_array, + img_data.ctypes.data_as(POINTER(c_int32)), ) return img_data @@ -318,7 +396,8 @@ class RegularMesh(Mesh): Axis-aligned bounding box of the mesh """ - mesh_type = 'regular' + + mesh_type = "regular" def __init__(self, uid=None, new=True, index=None): super().__init__(uid, new, index) @@ -333,10 +412,9 @@ def dimension(self): @dimension.setter def dimension(self, dimension): n = len(dimension) - dimension = (c_int*n)(*dimension) + dimension = (c_int * n)(*dimension) _dll.openmc_regular_mesh_set_dimension(self._index, n, dimension) - @property def lower_left(self): return self._get_parameters()[0] @@ -358,20 +436,22 @@ def _get_parameters(self): return ( as_array(ll, (n.value,)), as_array(ur, (n.value,)), - as_array(w, (n.value,)) + as_array(w, (n.value,)), ) def set_parameters(self, lower_left=None, upper_right=None, width=None): if lower_left is not None: n = len(lower_left) - lower_left = (c_double*n)(*lower_left) + lower_left = (c_double * n)(*lower_left) if upper_right is not None: n = len(upper_right) - upper_right = (c_double*n)(*upper_right) + upper_right = (c_double * n)(*upper_right) if width is not None: n = len(width) - width = (c_double*n)(*width) - _dll.openmc_regular_mesh_set_params(self._index, n, lower_left, upper_right, width) + width = (c_double * n)(*width) + _dll.openmc_regular_mesh_set_params( + self._index, n, lower_left, upper_right, width + ) class RectilinearMesh(Mesh): @@ -406,7 +486,8 @@ class RectilinearMesh(Mesh): Axis-aligned bounding box of the mesh """ - mesh_type = 'rectilinear' + + mesh_type = "rectilinear" def __init__(self, uid=None, new=True, index=None): super().__init__(uid, new, index) @@ -435,8 +516,7 @@ def _get_parameters(self): 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) + _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,)) @@ -470,13 +550,14 @@ def set_grid(self, x_grid, y_grid, z_grid): """ nx = len(x_grid) - x_grid = (c_double*nx)(*x_grid) + x_grid = (c_double * nx)(*x_grid) ny = len(y_grid) - y_grid = (c_double*ny)(*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) + z_grid = (c_double * nz)(*z_grid) + _dll.openmc_rectilinear_mesh_set_grid( + self._index, x_grid, nx, y_grid, ny, z_grid, nz + ) class CylindricalMesh(Mesh): @@ -511,7 +592,8 @@ class CylindricalMesh(Mesh): Axis-aligned bounding box of the mesh """ - mesh_type = 'cylindrical' + + mesh_type = "cylindrical" def __init__(self, uid=None, new=True, index=None): super().__init__(uid, new, index) @@ -540,8 +622,7 @@ def _get_parameters(self): gz = POINTER(c_double)() nz = c_int() # Call C API to get grid parameters - _dll.openmc_cylindrical_mesh_get_grid(self._index, gx, nx, gy, ny, gz, - nz) + _dll.openmc_cylindrical_mesh_get_grid(self._index, gx, nx, gy, ny, gz, nz) # Convert grid parameters to Numpy arrays grid_x = as_array(gx, (nx.value,)) @@ -575,13 +656,14 @@ def set_grid(self, r_grid, phi_grid, z_grid): """ nr = len(r_grid) - r_grid = (c_double*nr)(*r_grid) + r_grid = (c_double * nr)(*r_grid) nphi = len(phi_grid) - phi_grid = (c_double*nphi)(*phi_grid) + phi_grid = (c_double * nphi)(*phi_grid) nz = len(z_grid) - z_grid = (c_double*nz)(*z_grid) - _dll.openmc_cylindrical_mesh_set_grid(self._index, r_grid, nr, phi_grid, - nphi, z_grid, nz) + z_grid = (c_double * nz)(*z_grid) + _dll.openmc_cylindrical_mesh_set_grid( + self._index, r_grid, nr, phi_grid, nphi, z_grid, nz + ) class SphericalMesh(Mesh): @@ -616,7 +698,8 @@ class SphericalMesh(Mesh): Axis-aligned bounding box of the mesh """ - mesh_type = 'spherical' + + mesh_type = "spherical" def __init__(self, uid=None, new=True, index=None): super().__init__(uid, new, index) @@ -645,8 +728,7 @@ def _get_parameters(self): gz = POINTER(c_double)() nz = c_int() # Call C API to get grid parameters - _dll.openmc_spherical_mesh_get_grid(self._index, gx, nx, gy, ny, gz, - nz) + _dll.openmc_spherical_mesh_get_grid(self._index, gx, nx, gy, ny, gz, nz) # Convert grid parameters to Numpy arrays grid_x = as_array(gx, (nx.value,)) @@ -680,13 +762,14 @@ def set_grid(self, r_grid, theta_grid, phi_grid): """ nr = len(r_grid) - r_grid = (c_double*nr)(*r_grid) + r_grid = (c_double * nr)(*r_grid) ntheta = len(theta_grid) - theta_grid = (c_double*ntheta)(*theta_grid) + theta_grid = (c_double * ntheta)(*theta_grid) nphi = len(phi_grid) - phi_grid = (c_double*nphi)(*phi_grid) - _dll.openmc_spherical_mesh_set_grid(self._index, r_grid, nr, theta_grid, - ntheta, phi_grid, nphi) + phi_grid = (c_double * nphi)(*phi_grid) + _dll.openmc_spherical_mesh_set_grid( + self._index, r_grid, nr, theta_grid, ntheta, phi_grid, nphi + ) class UnstructuredMesh(Mesh): @@ -694,11 +777,11 @@ class UnstructuredMesh(Mesh): _MESH_TYPE_MAP = { - 'regular': RegularMesh, - 'rectilinear': RectilinearMesh, - 'cylindrical': CylindricalMesh, - 'spherical': SphericalMesh, - 'unstructured': UnstructuredMesh + "regular": RegularMesh, + "rectilinear": RectilinearMesh, + "cylindrical": CylindricalMesh, + "spherical": SphericalMesh, + "unstructured": UnstructuredMesh, } @@ -719,7 +802,6 @@ def __getitem__(self, key): raise KeyError(str(e)) return _get_mesh(index.value) - def __iter__(self): for i in range(len(self)): yield _get_mesh(i).id @@ -730,4 +812,5 @@ def __len__(self): def __repr__(self): return repr(dict(self)) + meshes = _MeshMapping() diff --git a/openmc/lib/nuclide.py b/openmc/lib/nuclide.py index 8078882cf35..9257f25fc97 100644 --- a/openmc/lib/nuclide.py +++ b/openmc/lib/nuclide.py @@ -11,9 +11,9 @@ from .error import _error_handler -__all__ = ['Nuclide', 'nuclides', 'load_nuclide'] +__all__ = ["Nuclide", "nuclides", "load_nuclide"] -_array_1d_dble = ndpointer(dtype=np.double, ndim=1, flags='CONTIGUOUS') +_array_1d_dble = ndpointer(dtype=np.double, ndim=1, flags="CONTIGUOUS") # Nuclide functions _dll.openmc_get_nuclide_index.argtypes = [c_char_p, POINTER(c_int)] @@ -25,8 +25,15 @@ _dll.openmc_nuclide_name.argtypes = [c_int, POINTER(c_char_p)] _dll.openmc_nuclide_name.restype = c_int _dll.openmc_nuclide_name.errcheck = _error_handler -_dll.openmc_nuclide_collapse_rate.argtypes = [c_int, c_int, c_double, - _array_1d_dble, _array_1d_dble, c_int, POINTER(c_double)] +_dll.openmc_nuclide_collapse_rate.argtypes = [ + c_int, + c_int, + c_double, + _array_1d_dble, + _array_1d_dble, + c_int, + POINTER(c_double), +] _dll.openmc_nuclide_collapse_rate.restype = c_int _dll.openmc_nuclide_collapse_rate.errcheck = _error_handler _dll.nuclides_size.restype = c_size_t @@ -62,6 +69,7 @@ class Nuclide(_FortranObject): Name of the nuclide, e.g. 'U235' """ + __instances = WeakValueDictionary() def __new__(cls, *args): @@ -102,13 +110,15 @@ def collapse_rate(self, MT, temperature, energy, flux): energy = np.asarray(energy, dtype=float) flux = np.asarray(flux, dtype=float) xs = c_double() - _dll.openmc_nuclide_collapse_rate(self._index, MT, temperature, energy, - flux, len(flux), xs) + _dll.openmc_nuclide_collapse_rate( + self._index, MT, temperature, energy, flux, len(flux), xs + ) return xs.value class _NuclideMapping(Mapping): """Provide mapping from nuclide name to index in nuclides array.""" + def __getitem__(self, key): index = c_int() try: @@ -128,4 +138,5 @@ def __len__(self): def __repr__(self): return repr(dict(self)) + nuclides = _NuclideMapping() diff --git a/openmc/lib/plot.py b/openmc/lib/plot.py index d9863667641..5f44f78a516 100644 --- a/openmc/lib/plot.py +++ b/openmc/lib/plot.py @@ -1,5 +1,4 @@ -from ctypes import (c_bool, c_int, c_size_t, c_int32, - c_double, Structure, POINTER) +from ctypes import c_bool, c_int, c_size_t, c_int32, c_double, Structure, POINTER from . import _dll from .error import _error_handler @@ -19,9 +18,8 @@ class _Position(Structure): z : c_double Position's z value (default: 0.0) """ - _fields_ = [('x', c_double), - ('y', c_double), - ('z', c_double)] + + _fields_ = [("x", c_double), ("y", c_double), ("z", c_double)] def __getitem__(self, idx): if idx == 0: @@ -81,12 +79,15 @@ class _PlotBase(Structure): level : int The universe level for the plot (default: -1 -> all universes shown) """ - _fields_ = [('origin_', _Position), - ('width_', _Position), - ('basis_', c_int), - ('pixels_', 3*c_size_t), - ('color_overlaps_', c_bool), - ('level_', c_int)] + + _fields_ = [ + ("origin_", _Position), + ("width_", _Position), + ("basis_", c_int), + ("pixels_", 3 * c_size_t), + ("color_overlaps_", c_bool), + ("level_", c_int), + ] def __init__(self): self.level_ = -1 @@ -121,27 +122,27 @@ def height(self, height): @property def basis(self): if self.basis_ == 1: - return 'xy' + return "xy" elif self.basis_ == 2: - return 'xz' + return "xz" elif self.basis_ == 3: - return 'yz' + return "yz" raise ValueError(f"Plot basis {self.basis_} is invalid") @basis.setter def basis(self, basis): if isinstance(basis, str): - valid_bases = ('xy', 'xz', 'yz') + valid_bases = ("xy", "xz", "yz") basis = basis.lower() if basis not in valid_bases: raise ValueError(f"{basis} is not a valid plot basis.") - if basis == 'xy': + if basis == "xy": self.basis_ = 1 - elif basis == 'xz': + elif basis == "xz": self.basis_ = 2 - elif basis == 'yz': + elif basis == "yz": self.basis_ = 3 return @@ -195,18 +196,20 @@ def color_overlaps(self, val): self.color_overlaps_ = val def __repr__(self): - out_str = ["-----", - "Plot:", - "-----", - f"Origin: {self.origin}", - f"Width: {self.width}", - f"Height: {self.height}", - f"Basis: {self.basis}", - f"HRes: {self.h_res}", - f"VRes: {self.v_res}", - f"Color Overlaps: {self.color_overlaps}", - f"Level: {self.level}"] - return '\n'.join(out_str) + out_str = [ + "-----", + "Plot:", + "-----", + f"Origin: {self.origin}", + f"Width: {self.width}", + f"Height: {self.height}", + f"Basis: {self.basis}", + f"HRes: {self.h_res}", + f"VRes: {self.v_res}", + f"Color Overlaps: {self.color_overlaps}", + f"Level: {self.level}", + ] + return "\n".join(out_str) _dll.openmc_id_map.argtypes = [POINTER(_PlotBase), POINTER(c_int32)] @@ -232,8 +235,7 @@ def id_map(plot): contains, in order, cell IDs, cell instances, and material IDs. """ - img_data = np.zeros((plot.v_res, plot.h_res, 3), - dtype=np.dtype('int32')) + img_data = np.zeros((plot.v_res, plot.h_res, 3), dtype=np.dtype("int32")) _dll.openmc_id_map(plot, img_data.ctypes.data_as(POINTER(c_int32))) return img_data diff --git a/openmc/lib/settings.py b/openmc/lib/settings.py index 062670ef843..70d12daed09 100644 --- a/openmc/lib/settings.py +++ b/openmc/lib/settings.py @@ -4,11 +4,13 @@ from .core import _DLLGlobal from .error import _error_handler -_RUN_MODES = {1: 'fixed source', - 2: 'eigenvalue', - 3: 'plot', - 4: 'particle restart', - 5: 'volume'} +_RUN_MODES = { + 1: "fixed source", + 2: "eigenvalue", + 3: "plot", + 4: "particle restart", + 5: "volume", +} _dll.openmc_set_seed.argtypes = [c_int64] _dll.openmc_get_seed.restype = c_int64 @@ -22,24 +24,24 @@ class _Settings: # Attributes that are accessed through a descriptor - cmfd_run = _DLLGlobal(c_bool, 'cmfd_run') - entropy_on = _DLLGlobal(c_bool, 'entropy_on') - generations_per_batch = _DLLGlobal(c_int32, 'gen_per_batch') - inactive = _DLLGlobal(c_int32, 'n_inactive') - max_lost_particles = _DLLGlobal(c_int32, 'max_lost_particles') - need_depletion_rx = _DLLGlobal(c_bool, 'need_depletion_rx') - output_summary = _DLLGlobal(c_bool, 'output_summary') - particles = _DLLGlobal(c_int64, 'n_particles') - rel_max_lost_particles = _DLLGlobal(c_double, 'rel_max_lost_particles') - restart_run = _DLLGlobal(c_bool, 'restart_run') - run_CE = _DLLGlobal(c_bool, 'run_CE') - verbosity = _DLLGlobal(c_int, 'verbosity') - event_based = _DLLGlobal(c_bool, 'event_based') - weight_windows_on = _DLLGlobal(c_bool, 'weight_windows_on') + cmfd_run = _DLLGlobal(c_bool, "cmfd_run") + entropy_on = _DLLGlobal(c_bool, "entropy_on") + generations_per_batch = _DLLGlobal(c_int32, "gen_per_batch") + inactive = _DLLGlobal(c_int32, "n_inactive") + max_lost_particles = _DLLGlobal(c_int32, "max_lost_particles") + need_depletion_rx = _DLLGlobal(c_bool, "need_depletion_rx") + output_summary = _DLLGlobal(c_bool, "output_summary") + particles = _DLLGlobal(c_int64, "n_particles") + rel_max_lost_particles = _DLLGlobal(c_double, "rel_max_lost_particles") + restart_run = _DLLGlobal(c_bool, "restart_run") + run_CE = _DLLGlobal(c_bool, "run_CE") + verbosity = _DLLGlobal(c_int, "verbosity") + event_based = _DLLGlobal(c_bool, "event_based") + weight_windows_on = _DLLGlobal(c_bool, "weight_windows_on") @property def run_mode(self): - i = c_int.in_dll(_dll, 'run_mode').value + i = c_int.in_dll(_dll, "run_mode").value try: return _RUN_MODES[i] except KeyError: @@ -47,17 +49,17 @@ def run_mode(self): @run_mode.setter def run_mode(self, mode): - current_idx = c_int.in_dll(_dll, 'run_mode') + current_idx = c_int.in_dll(_dll, "run_mode") for idx, mode_value in _RUN_MODES.items(): if mode_value == mode: current_idx.value = idx break else: - raise ValueError(f'Invalid run mode: {mode}') + raise ValueError(f"Invalid run mode: {mode}") @property def path_statepoint(self): - path = c_char_p.in_dll(_dll, 'path_statepoint_c').value + path = c_char_p.in_dll(_dll, "path_statepoint_c").value return path.decode() @property diff --git a/openmc/lib/tally.py b/openmc/lib/tally.py index d0b34aedc26..31c3866485c 100644 --- a/openmc/lib/tally.py +++ b/openmc/lib/tally.py @@ -14,7 +14,7 @@ from .filter import _get_filter -__all__ = ['Tally', 'tallies', 'global_tallies', 'num_realizations'] +__all__ = ["Tally", "tallies", "global_tallies", "num_realizations"] # Tally functions _dll.openmc_extend_tallies.argtypes = [c_int32, POINTER(c_int32), POINTER(c_int32)] @@ -36,7 +36,10 @@ _dll.openmc_tally_get_id.restype = c_int _dll.openmc_tally_get_id.errcheck = _error_handler _dll.openmc_tally_get_filters.argtypes = [ - c_int32, POINTER(POINTER(c_int32)), POINTER(c_size_t)] + c_int32, + POINTER(POINTER(c_int32)), + POINTER(c_size_t), +] _dll.openmc_tally_get_filters.restype = c_int _dll.openmc_tally_get_filters.errcheck = _error_handler _dll.openmc_tally_get_multiply_density.argtypes = [c_int32, POINTER(c_bool)] @@ -46,11 +49,17 @@ _dll.openmc_tally_get_n_realizations.restype = c_int _dll.openmc_tally_get_n_realizations.errcheck = _error_handler _dll.openmc_tally_get_nuclides.argtypes = [ - c_int32, POINTER(POINTER(c_int)), POINTER(c_int)] + c_int32, + POINTER(POINTER(c_int)), + POINTER(c_int), +] _dll.openmc_tally_get_nuclides.restype = c_int _dll.openmc_tally_get_nuclides.errcheck = _error_handler _dll.openmc_tally_get_scores.argtypes = [ - c_int32, POINTER(POINTER(c_int)), POINTER(c_int)] + c_int32, + POINTER(POINTER(c_int)), + POINTER(c_int), +] _dll.openmc_tally_get_scores.restype = c_int _dll.openmc_tally_get_scores.errcheck = _error_handler _dll.openmc_tally_get_type.argtypes = [c_int32, POINTER(c_int32)] @@ -63,7 +72,10 @@ _dll.openmc_tally_reset.restype = c_int _dll.openmc_tally_reset.errcheck = _error_handler _dll.openmc_tally_results.argtypes = [ - c_int32, POINTER(POINTER(c_double)), POINTER(c_size_t*3)] + c_int32, + POINTER(POINTER(c_double)), + POINTER(c_size_t * 3), +] _dll.openmc_tally_results.restype = c_int _dll.openmc_tally_results.errcheck = _error_handler _dll.openmc_tally_set_active.argtypes = [c_int32, c_bool] @@ -100,18 +112,26 @@ _SCORES = { - -1: 'flux', -2: 'total', -3: 'scatter', -4: 'nu-scatter', - -5: 'absorption', -6: 'fission', -7: 'nu-fission', -8: 'kappa-fission', - -9: 'current', -10: 'events', -11: 'delayed-nu-fission', - -12: 'prompt-nu-fission', -13: 'inverse-velocity', -14: 'fission-q-prompt', - -15: 'fission-q-recoverable', -16: 'decay-rate', -17: 'pulse-height' -} -_ESTIMATORS = { - 0: 'analog', 1: 'tracklength', 2: 'collision' -} -_TALLY_TYPES = { - 0: 'volume', 1: 'mesh-surface', 2: 'surface', 3: 'pulse-height' + -1: "flux", + -2: "total", + -3: "scatter", + -4: "nu-scatter", + -5: "absorption", + -6: "fission", + -7: "nu-fission", + -8: "kappa-fission", + -9: "current", + -10: "events", + -11: "delayed-nu-fission", + -12: "prompt-nu-fission", + -13: "inverse-velocity", + -14: "fission-q-prompt", + -15: "fission-q-recoverable", + -16: "decay-rate", + -17: "pulse-height", } +_ESTIMATORS = {0: "analog", 1: "tracklength", 2: "collision"} +_TALLY_TYPES = {0: "volume", 1: "mesh-surface", 2: "surface", 3: "pulse-height"} def global_tallies(): @@ -143,14 +163,14 @@ def global_tallies(): stdev = np.empty_like(mean) stdev.fill(np.inf) if n > 1: - stdev[nonzero] = np.sqrt((sum_sq[nonzero]/n - mean[nonzero]**2)/(n - 1)) + stdev[nonzero] = np.sqrt((sum_sq[nonzero] / n - mean[nonzero] ** 2) / (n - 1)) return list(zip(mean, stdev)) def num_realizations(): """Number of realizations of global tallies.""" - return c_int32.in_dll(_dll, 'n_realizations').value + return c_int32.in_dll(_dll, "n_realizations").value class Tally(_FortranObjectWithID): @@ -196,6 +216,7 @@ class Tally(_FortranObjectWithID): Type of tally (volume, mesh_surface, surface) """ + __instances = WeakValueDictionary() def __new__(cls, uid=None, new=True, index=None): @@ -207,8 +228,10 @@ def __new__(cls, uid=None, new=True, index=None): uid = max(mapping, default=0) + 1 else: if uid in mapping: - raise AllocationError('A tally with ID={} has already ' - 'been allocated.'.format(uid)) + raise AllocationError( + "A tally with ID={} has already " + "been allocated.".format(uid) + ) index = c_int32() _dll.openmc_extend_tallies(1, index, None) @@ -276,7 +299,7 @@ def filters(self): def filters(self, filters): # Get filter indices as int32_t[] n = len(filters) - indices = (c_int32*n)(*(f._index for f in filters)) + indices = (c_int32 * n)(*(f._index for f in filters)) _dll.openmc_tally_set_filters(self._index, n, indices) @@ -302,7 +325,7 @@ def find_filter(self, filter_type): if isinstance(filter, filter_type): return filter - raise ValueError(f'No filter of type {filter_type} on tally {self.id}') + raise ValueError(f"No filter of type {filter_type} on tally {self.id}") @property def mean(self): @@ -318,8 +341,9 @@ def nuclides(self): nucs = POINTER(c_int)() n = c_int() _dll.openmc_tally_get_nuclides(self._index, nucs, n) - return [Nuclide(nucs[i]).name if nucs[i] >= 0 else 'total' - for i in range(n.value)] + return [ + Nuclide(nucs[i]).name if nucs[i] >= 0 else "total" for i in range(n.value) + ] @nuclides.setter def nuclides(self, nuclides): @@ -336,7 +360,7 @@ def num_realizations(self): @property def results(self): data = POINTER(c_double)() - shape = (c_size_t*3)() + shape = (c_size_t * 3)() _dll.openmc_tally_results(self._index, data, shape) return as_array(data, tuple(shape)) @@ -383,7 +407,8 @@ def std_dev(self): # Calculate sample standard deviation of the mean std_dev[nonzero] = np.sqrt( - (sum_sq[nonzero]/n - mean[nonzero]**2)/(n - 1)) + (sum_sq[nonzero] / n - mean[nonzero] ** 2) / (n - 1) + ) return std_dev @@ -428,7 +453,7 @@ def ci_width(self, alpha=0.05): half_width = self.std_dev.copy() n = self.num_realizations if n > 1: - half_width *= scipy.stats.t.ppf(1 - alpha/2, n - 1) + half_width *= scipy.stats.t.ppf(1 - alpha / 2, n - 1) return half_width @@ -456,4 +481,5 @@ def __delitem__(self, key): """Delete a tally from tally vector and remove the ID,index pair from tally""" _dll.openmc_remove_tally(self[key]._index) + tallies = _TallyMapping() diff --git a/openmc/lib/weight_windows.py b/openmc/lib/weight_windows.py index d92f019179f..e90de638f5f 100644 --- a/openmc/lib/weight_windows.py +++ b/openmc/lib/weight_windows.py @@ -15,11 +15,17 @@ from .mesh import meshes -__all__ = ['WeightWindows', 'weight_windows'] +__all__ = ["WeightWindows", "weight_windows"] -_dll.openmc_extend_weight_windows.argtypes = [c_int32, POINTER(c_int32), POINTER(c_int32)] +_dll.openmc_extend_weight_windows.argtypes = [ + c_int32, + POINTER(c_int32), + POINTER(c_int32), +] -_dll.openmc_weight_windows_update_magic.argtypes = 2*[c_int32] + [c_char_p] + 2*[c_double] +_dll.openmc_weight_windows_update_magic.argtypes = ( + 2 * [c_int32] + [c_char_p] + 2 * [c_double] +) _dll.openmc_weight_windows_update_magic.restype = c_int _dll.openmc_weight_windows_update_magic.errcheck = _error_handler @@ -45,11 +51,19 @@ _dll.openmc_weight_windows_get_mesh.restype = c_int _dll.openmc_weight_windows_get_mesh.errcheck = _error_handler -_dll.openmc_weight_windows_set_energy_bounds.argtypes = [c_int32, POINTER(c_double), c_size_t] +_dll.openmc_weight_windows_set_energy_bounds.argtypes = [ + c_int32, + POINTER(c_double), + c_size_t, +] _dll.openmc_weight_windows_set_energy_bounds.restype = c_int _dll.openmc_weight_windows_set_energy_bounds.errcheck = _error_handler -_dll.openmc_weight_windows_get_energy_bounds.argtypes = [c_int32, POINTER(POINTER(c_double)), POINTER(c_size_t)] +_dll.openmc_weight_windows_get_energy_bounds.argtypes = [ + c_int32, + POINTER(POINTER(c_double)), + POINTER(c_size_t), +] _dll.openmc_weight_windows_get_energy_bounds.restype = c_int _dll.openmc_weight_windows_get_energy_bounds.errcheck = _error_handler @@ -61,11 +75,21 @@ _dll.openmc_weight_windows_get_particle.restype = c_int _dll.openmc_weight_windows_get_particle.errcheck = _error_handler -_dll.openmc_weight_windows_set_bounds.argtypes = [c_int32, POINTER(c_double), POINTER(c_double), c_size_t] +_dll.openmc_weight_windows_set_bounds.argtypes = [ + c_int32, + POINTER(c_double), + POINTER(c_double), + c_size_t, +] _dll.openmc_weight_windows_set_bounds.restype = c_int _dll.openmc_weight_windows_set_bounds.errcheck = _error_handler -_dll.openmc_weight_windows_get_bounds.argtypes = [c_int32, POINTER(POINTER(c_double)), POINTER(POINTER(c_double)), POINTER(c_size_t)] +_dll.openmc_weight_windows_get_bounds.argtypes = [ + c_int32, + POINTER(POINTER(c_double)), + POINTER(POINTER(c_double)), + POINTER(c_size_t), +] _dll.openmc_weight_windows_get_bounds.restype = c_int _dll.openmc_weight_windows_get_bounds.errcheck = _error_handler @@ -77,7 +101,10 @@ _dll.openmc_weight_windows_set_survival_ratio.restype = c_int _dll.openmc_weight_windows_set_survival_ratio.errcheck = _error_handler -_dll.openmc_weight_windows_get_max_lower_bound_ratio.argtypes = [c_int32, POINTER(c_double)] +_dll.openmc_weight_windows_get_max_lower_bound_ratio.argtypes = [ + c_int32, + POINTER(c_double), +] _dll.openmc_weight_windows_get_max_lower_bound_ratio.restype = c_int _dll.openmc_weight_windows_get_max_lower_bound_ratio.errcheck = _error_handler @@ -134,6 +161,7 @@ class WeightWindows(_FortranObjectWithID): bounds : numpy.ndarray The weight window bounds """ + __instances = WeakValueDictionary() def __new__(cls, id=None, new=True, index=None): @@ -146,8 +174,10 @@ def __new__(cls, id=None, new=True, index=None): id = max(mapping, default=0) + 1 else: if id in mapping: - raise AllocationError(f'A weight windows object with ID={id} ' - 'has already been allocated.') + raise AllocationError( + f"A weight windows object with ID={id} " + "has already been allocated." + ) index = c_int32() _dll.openmc_extend_weight_windows(1, index, None) @@ -183,7 +213,8 @@ def mesh(self): @mesh.setter def mesh(self, mesh): _dll.openmc_weight_windows_set_mesh( - weight_windows[self.id]._index, meshes[mesh.id]._index) + weight_windows[self.id]._index, meshes[mesh.id]._index + ) @property def energy_bounds(self): @@ -197,7 +228,8 @@ def energy_bounds(self, e_bounds): e_bounds_arr = np.asarray(e_bounds, dtype=float) e_bounds_ptr = e_bounds_arr.ctypes.data_as(POINTER(c_double)) _dll.openmc_weight_windows_set_energy_bounds( - self._index, e_bounds_ptr, e_bounds_arr.size) + self._index, e_bounds_ptr, e_bounds_arr.size + ) @property def particle(self): @@ -273,7 +305,7 @@ def max_split(self): def max_split(self, max_split): _dll.openmc_weight_windows_set_max_split(self._index, max_split) - def update_magic(self, tally, value='mean', threshold=1.0, ratio=5.0): + def update_magic(self, tally, value="mean", threshold=1.0, ratio=5.0): """Update weight window values using the MAGIC method Reference: https://inis.iaea.org/search/48022314 @@ -290,11 +322,9 @@ def update_magic(self, tally, value='mean', threshold=1.0, ratio=5.0): Ratio of the lower to upper weight window bounds """ - _dll.openmc_weight_windows_update_magic(self._index, - tally._index, - c_char_p(value.encode()), - threshold, - ratio) + _dll.openmc_weight_windows_update_magic( + self._index, tally._index, c_char_p(value.encode()), threshold, ratio + ) @classmethod def from_tally(cls, tally, particle=ParticleType.NEUTRON): @@ -329,14 +359,18 @@ def from_tally(cls, tally, particle=ParticleType.NEUTRON): """ # do some checks on particle value if not isinstance(particle, (ParticleType, str)): - raise ValueError(f"Parameter 'particle' must be {ParticleType} or one of ('neutron', 'photon').") + raise ValueError( + f"Parameter 'particle' must be {ParticleType} or one of ('neutron', 'photon')." + ) # convert particle type if needed if isinstance(particle, str): particle = ParticleType.from_string(particle) if particle not in (ParticleType.NEUTRON, ParticleType.PHOTON): - raise ValueError('Weight windows can only be applied for neutrons or photons') + raise ValueError( + "Weight windows can only be applied for neutrons or photons" + ) try: particle_filter = tally.find_filter(ParticleFilter) @@ -345,8 +379,10 @@ def from_tally(cls, tally, particle=ParticleType.NEUTRON): # ensure that the tally won't filter out the specified particle if particle_filter is not None and particle not in particle_filter.bins: - raise ValueError(f'Specified tally for weight windows (Tally {tally.id})' - f' does not track the requested particle: "{particle}"') + raise ValueError( + f"Specified tally for weight windows (Tally {tally.id})" + f' does not track the requested particle: "{particle}"' + ) # tally must have a mesh filter mesh_filter = tally.find_filter(MeshFilter) @@ -392,4 +428,5 @@ def __repr__(self): def __delitem__(self): raise NotImplementedError("WeightWindows object remove not implemented") + weight_windows = _WeightWindowsMapping() diff --git a/openmc/macroscopic.py b/openmc/macroscopic.py index 2a5a22752a7..ff5e0e58763 100644 --- a/openmc/macroscopic.py +++ b/openmc/macroscopic.py @@ -17,7 +17,7 @@ class Macroscopic(str): """ def __new__(cls, name): - check_type('name', name, str) + check_type("name", name, str) return super().__new__(cls, name) @property diff --git a/openmc/material.py b/openmc/material.py index a6401216adb..407927ce6db 100644 --- a/openmc/material.py +++ b/openmc/material.py @@ -24,14 +24,13 @@ # Units for density supported by OpenMC -DENSITY_UNITS = ('g/cm3', 'g/cc', 'kg/m3', 'atom/b-cm', 'atom/cm3', 'sum', - 'macro') +DENSITY_UNITS = ("g/cm3", "g/cc", "kg/m3", "atom/b-cm", "atom/cm3", "sum", "macro") # Smallest normalized floating point number _SMALLEST_NORMAL = sys.float_info.min -NuclideTuple = namedtuple('NuclideTuple', ['name', 'percent', 'percent_type']) +NuclideTuple = namedtuple("NuclideTuple", ["name", "percent", "percent_type"]) class Material(IDManagerMixin): @@ -107,13 +106,13 @@ class Material(IDManagerMixin): next_id = 1 used_ids = set() - def __init__(self, material_id=None, name='', temperature=None): + def __init__(self, material_id=None, name="", temperature=None): # Initialize class attributes self.id = material_id self.name = name self.temperature = temperature self._density = None - self._density_units = 'sum' + self._density_units = "sum" self._depletable = False self._paths = None self._num_instances = None @@ -133,34 +132,34 @@ def __init__(self, material_id=None, name='', temperature=None): self._sab = [] def __repr__(self) -> str: - string = 'Material\n' - string += '{: <16}=\t{}\n'.format('\tID', self._id) - string += '{: <16}=\t{}\n'.format('\tName', self._name) - string += '{: <16}=\t{}\n'.format('\tTemperature', self._temperature) + string = "Material\n" + string += "{: <16}=\t{}\n".format("\tID", self._id) + string += "{: <16}=\t{}\n".format("\tName", self._name) + string += "{: <16}=\t{}\n".format("\tTemperature", self._temperature) - string += '{: <16}=\t{}'.format('\tDensity', self._density) - string += f' [{self._density_units}]\n' + string += "{: <16}=\t{}".format("\tDensity", self._density) + string += f" [{self._density_units}]\n" - string += '{: <16}=\t{} [cm^3]\n'.format('\tVolume', self._volume) - string += '{: <16}=\t{}\n'.format('\tDepletable', self._depletable) + string += "{: <16}=\t{} [cm^3]\n".format("\tVolume", self._volume) + string += "{: <16}=\t{}\n".format("\tDepletable", self._depletable) - string += '{: <16}\n'.format('\tS(a,b) Tables') + string += "{: <16}\n".format("\tS(a,b) Tables") if self._ncrystal_cfg: - string += '{: <16}=\t{}\n'.format('\tNCrystal conf', self._ncrystal_cfg) + string += "{: <16}=\t{}\n".format("\tNCrystal conf", self._ncrystal_cfg) for sab in self._sab: - string += '{: <16}=\t{}\n'.format('\tS(a,b)', sab) + string += "{: <16}=\t{}\n".format("\tS(a,b)", sab) - string += '{: <16}\n'.format('\tNuclides') + string += "{: <16}\n".format("\tNuclides") for nuclide, percent, percent_type in self._nuclides: - string += '{: <16}'.format('\t{}'.format(nuclide)) - string += f'=\t{percent: <12} [{percent_type}]\n' + string += "{: <16}".format("\t{}".format(nuclide)) + string += f"=\t{percent: <12} [{percent_type}]\n" if self._macroscopic is not None: - string += '{: <16}\n'.format('\tMacroscopic Data') - string += '{: <16}'.format('\t{}'.format(self._macroscopic)) + string += "{: <16}\n".format("\tMacroscopic Data") + string += "{: <16}".format("\t{}".format(self._macroscopic)) return string @@ -171,11 +170,10 @@ def name(self) -> str | None: @name.setter def name(self, name: str | None): if name is not None: - cv.check_type(f'name for Material ID="{self._id}"', - name, str) + cv.check_type(f'name for Material ID="{self._id}"', name, str) self._name = name else: - self._name = '' + self._name = "" @property def temperature(self) -> float | None: @@ -183,8 +181,9 @@ def temperature(self) -> float | None: @temperature.setter def temperature(self, temperature: Real | None): - cv.check_type(f'Temperature for Material ID="{self._id}"', - temperature, (Real, type(None))) + cv.check_type( + f'Temperature for Material ID="{self._id}"', temperature, (Real, type(None)) + ) self._temperature = temperature @property @@ -201,23 +200,25 @@ def depletable(self) -> bool: @depletable.setter def depletable(self, depletable: bool): - cv.check_type(f'Depletable flag for Material ID="{self._id}"', - depletable, bool) + cv.check_type(f'Depletable flag for Material ID="{self._id}"', depletable, bool) self._depletable = depletable @property def paths(self) -> list[str]: if self._paths is None: - raise ValueError('Material instance paths have not been determined. ' - 'Call the Geometry.determine_paths() method.') + raise ValueError( + "Material instance paths have not been determined. " + "Call the Geometry.determine_paths() method." + ) return self._paths @property def num_instances(self) -> int: if self._num_instances is None: raise ValueError( - 'Number of material instances have not been determined. Call ' - 'the Geometry.determine_paths() method.') + "Number of material instances have not been determined. Call " + "the Geometry.determine_paths() method." + ) return self._num_instances @property @@ -230,18 +231,17 @@ def isotropic(self) -> list[str]: @isotropic.setter def isotropic(self, isotropic: Iterable[str]): - cv.check_iterable_type('Isotropic scattering nuclides', isotropic, - str) + cv.check_iterable_type("Isotropic scattering nuclides", isotropic, str) self._isotropic = list(isotropic) @property def average_molar_mass(self) -> float: # Using the sum of specified atomic or weight amounts as a basis, sum # the mass and moles of the material - mass = 0. - moles = 0. + mass = 0.0 + moles = 0.0 for nuc in self.nuclides: - if nuc.percent_type == 'ao': + if nuc.percent_type == "ao": mass += nuc.percent * openmc.data.atomic_mass(nuc.name) moles += nuc.percent else: @@ -258,7 +258,7 @@ def volume(self) -> float | None: @volume.setter def volume(self, volume: Real): if volume is not None: - cv.check_type('material volume', volume, Real) + cv.check_type("material volume", volume, Real) self._volume = volume @property @@ -273,24 +273,30 @@ def fissionable_mass(self) -> float: for nuc, atoms_per_bcm in self.get_nuclide_atom_densities().items(): Z = openmc.data.zam(nuc)[0] if Z >= 90: - density += 1e24 * atoms_per_bcm * openmc.data.atomic_mass(nuc) \ - / openmc.data.AVOGADRO - return density*self.volume + density += ( + 1e24 + * atoms_per_bcm + * openmc.data.atomic_mass(nuc) + / openmc.data.AVOGADRO + ) + return density * self.volume @property def decay_photon_energy(self) -> Univariate | None: warnings.warn( "The 'decay_photon_energy' property has been replaced by the " "get_decay_photon_energy() method and will be removed in a future " - "version.", FutureWarning) + "version.", + FutureWarning, + ) return self.get_decay_photon_energy(0.0) def get_decay_photon_energy( - self, - clip_tolerance: float = 1e-6, - units: str = 'Bq', - volume: float | None = None - ) -> Univariate | None: + self, + clip_tolerance: float = 1e-6, + units: str = "Bq", + volume: float | None = None, + ) -> Univariate | None: r"""Return energy distribution of decay photons from unstable nuclides. .. versionadded:: 0.14.0 @@ -312,14 +318,14 @@ def get_decay_photon_energy( the total intensity of the photon source in the requested units. """ - cv.check_value('units', units, {'Bq', 'Bq/g', 'Bq/cm3'}) - if units == 'Bq': + cv.check_value("units", units, {"Bq", "Bq/g", "Bq/cm3"}) + if units == "Bq": multiplier = volume if volume is not None else self.volume if multiplier is None: raise ValueError("volume must be specified if units='Bq'") - elif units == 'Bq/cm3': + elif units == "Bq/cm3": multiplier = 1 - elif units == 'Bq/g': + elif units == "Bq/g": multiplier = 1.0 / self.get_mass_density() dists = [] @@ -361,39 +367,39 @@ def from_hdf5(cls, group: h5py.Group) -> Material: Material instance """ - mat_id = int(group.name.split('/')[-1].lstrip('material ')) + mat_id = int(group.name.split("/")[-1].lstrip("material ")) - name = group['name'][()].decode() if 'name' in group else '' - density = group['atom_density'][()] - if 'nuclide_densities' in group: - nuc_densities = group['nuclide_densities'][()] + name = group["name"][()].decode() if "name" in group else "" + density = group["atom_density"][()] + if "nuclide_densities" in group: + nuc_densities = group["nuclide_densities"][()] # Create the Material material = cls(mat_id, name) - material.depletable = bool(group.attrs['depletable']) - if 'volume' in group.attrs: - material.volume = group.attrs['volume'] + material.depletable = bool(group.attrs["depletable"]) + if "volume" in group.attrs: + material.volume = group.attrs["volume"] if "temperature" in group.attrs: material.temperature = group.attrs["temperature"] # Read the names of the S(a,b) tables for this Material and add them - if 'sab_names' in group: - sab_tables = group['sab_names'][()] + if "sab_names" in group: + sab_tables = group["sab_names"][()] for sab_table in sab_tables: name = sab_table.decode() material.add_s_alpha_beta(name) # Set the Material's density to atom/b-cm as used by OpenMC - material.set_density(density=density, units='atom/b-cm') + material.set_density(density=density, units="atom/b-cm") - if 'nuclides' in group: - nuclides = group['nuclides'][()] + if "nuclides" in group: + nuclides = group["nuclides"][()] # Add all nuclides to the Material for fullname, density in zip(nuclides, nuc_densities): name = fullname.decode().strip() - material.add_nuclide(name, percent=density, percent_type='ao') - if 'macroscopics' in group: - macroscopics = group['macroscopics'][()] + material.add_nuclide(name, percent=density, percent_type="ao") + if "macroscopics" in group: + macroscopics = group["macroscopics"][()] # Add all macroscopics to the Material for fullname in macroscopics: name = fullname.decode().strip() @@ -427,23 +433,25 @@ def from_ncrystal(cls, cfg, **kwargs) -> Material: """ import NCrystal + nc_mat = NCrystal.createInfo(cfg) def openmc_natabund(Z): - #nc_mat.getFlattenedComposition might need natural abundancies. - #This call-back function is used so NCrystal can flatten composition - #using OpenMC's natural abundancies. In practice this function will - #only get invoked in the unlikely case where a material is specified - #by referring both to natural elements and specific isotopes of the - #same element. + # nc_mat.getFlattenedComposition might need natural abundancies. + # This call-back function is used so NCrystal can flatten composition + # using OpenMC's natural abundancies. In practice this function will + # only get invoked in the unlikely case where a material is specified + # by referring both to natural elements and specific isotopes of the + # same element. elem_name = openmc.data.ATOMIC_SYMBOL[Z] return [ - (int(iso_name[len(elem_name):]), abund) + (int(iso_name[len(elem_name) :]), abund) for iso_name, abund in openmc.data.isotopes(elem_name) ] flat_compos = nc_mat.getFlattenedComposition( - preferNaturalElements=True, naturalAbundProvider=openmc_natabund) + preferNaturalElements=True, naturalAbundProvider=openmc_natabund + ) # Create the Material material = cls(temperature=nc_mat.getTemperature(), **kwargs) @@ -452,11 +460,11 @@ def openmc_natabund(Z): elemname = openmc.data.ATOMIC_SYMBOL[Z] for A, frac in A_vals: if A: - material.add_nuclide(f'{elemname}{A}', frac) + material.add_nuclide(f"{elemname}{A}", frac) else: material.add_element(elemname, frac) - material.set_density('g/cm3', nc_mat.getDensity()) + material.set_density("g/cm3", nc_mat.getDensity()) material._ncrystal_cfg = NCrystal.normaliseCfg(cfg) return material @@ -470,15 +478,16 @@ def add_volume_information(self, volume_calc): Results from a stochastic volume calculation """ - if volume_calc.domain_type == 'material': + if volume_calc.domain_type == "material": if self.id in volume_calc.volumes: self._volume = volume_calc.volumes[self.id].n self._atoms = volume_calc.atoms[self.id] else: - raise ValueError('No volume information found for material ID={}.' - .format(self.id)) + raise ValueError( + "No volume information found for material ID={}.".format(self.id) + ) else: - raise ValueError(f'No volume information found for material ID={self.id}.') + raise ValueError(f"No volume information found for material ID={self.id}.") def set_density(self, units: str, density: float | None = None): """Set the density of the material @@ -493,26 +502,29 @@ def set_density(self, units: str, density: float | None = None): """ - cv.check_value('density units', units, DENSITY_UNITS) + cv.check_value("density units", units, DENSITY_UNITS) self._density_units = units - if units == 'sum': + if units == "sum": if density is not None: - msg = 'Density "{}" for Material ID="{}" is ignored ' \ - 'because the unit is "sum"'.format(density, self.id) + msg = ( + 'Density "{}" for Material ID="{}" is ignored ' + 'because the unit is "sum"'.format(density, self.id) + ) warnings.warn(msg) else: if density is None: - msg = 'Unable to set the density for Material ID="{}" ' \ - 'because a density value must be given when not using ' \ - '"sum" unit'.format(self.id) + msg = ( + 'Unable to set the density for Material ID="{}" ' + "because a density value must be given when not using " + '"sum" unit'.format(self.id) + ) raise ValueError(msg) - cv.check_type(f'the density for Material ID="{self.id}"', - density, Real) + cv.check_type(f'the density for Material ID="{self.id}"', density, Real) self._density = density - def add_nuclide(self, nuclide: str, percent: float, percent_type: str = 'ao'): + def add_nuclide(self, nuclide: str, percent: float, percent_type: str = "ao"): """Add a nuclide to the material Parameters @@ -525,14 +537,16 @@ def add_nuclide(self, nuclide: str, percent: float, percent_type: str = 'ao'): 'ao' for atom percent and 'wo' for weight percent """ - cv.check_type('nuclide', nuclide, str) - cv.check_type('percent', percent, Real) - cv.check_value('percent type', percent_type, {'ao', 'wo'}) - cv.check_greater_than('percent', percent, 0, equality=True) + cv.check_type("nuclide", nuclide, str) + cv.check_type("percent", percent, Real) + cv.check_value("percent type", percent_type, {"ao", "wo"}) + cv.check_greater_than("percent", percent, 0, equality=True) if self._macroscopic is not None: - msg = 'Unable to add a Nuclide to Material ID="{}" as a ' \ - 'macroscopic data-set has already been added'.format(self._id) + msg = ( + 'Unable to add a Nuclide to Material ID="{}" as a ' + "macroscopic data-set has already been added".format(self._id) + ) raise ValueError(msg) if self._ncrystal_cfg is not None: @@ -550,8 +564,8 @@ def add_nuclide(self, nuclide: str, percent: float, percent_type: str = 'ao'): self._nuclides.append(NuclideTuple(nuclide, percent, percent_type)) - def add_components(self, components: dict, percent_type: str = 'ao'): - """ Add multiple elements or nuclides to a material + def add_components(self, components: dict, percent_type: str = "ao"): + """Add multiple elements or nuclides to a material .. versionadded:: 0.13.1 @@ -579,17 +593,19 @@ def add_components(self, components: dict, percent_type: str = 'ao'): """ for component, params in components.items(): - cv.check_type('component', component, str) + cv.check_type("component", component, str) if isinstance(params, Real): - params = {'percent': params} + params = {"percent": params} else: - cv.check_type('params', params, dict) - if 'percent' not in params: - raise ValueError("An entry in the dictionary does not have " - "a required key: 'percent'") + cv.check_type("params", params, dict) + if "percent" not in params: + raise ValueError( + "An entry in the dictionary does not have " + "a required key: 'percent'" + ) - params['percent_type'] = percent_type + params["percent_type"] = percent_type # check if nuclide if not component.isalpha(): @@ -606,7 +622,7 @@ def remove_nuclide(self, nuclide: str): Nuclide to remove """ - cv.check_type('nuclide', nuclide, str) + cv.check_type("nuclide", nuclide, str) # If the Material contains the Nuclide, delete it for nuc in reversed(self.nuclides): @@ -624,11 +640,11 @@ def remove_element(self, element): Element to remove """ - cv.check_type('element', element, str) + cv.check_type("element", element, str) # If the Material contains the element, delete it for nuc in reversed(self.nuclides): - element_name = re.split(r'\d+', nuc.name)[0] + element_name = re.split(r"\d+", nuc.name)[0] if element_name == element: self.nuclides.remove(nuc) @@ -647,23 +663,29 @@ def add_macroscopic(self, macroscopic: str): # Ensure no nuclides, elements, or sab are added since these would be # incompatible with macroscopics if self._nuclides or self._sab: - msg = 'Unable to add a Macroscopic data set to Material ID="{}" ' \ - 'with a macroscopic value "{}" as an incompatible data ' \ - 'member (i.e., nuclide or S(a,b) table) ' \ - 'has already been added'.format(self._id, macroscopic) + msg = ( + 'Unable to add a Macroscopic data set to Material ID="{}" ' + 'with a macroscopic value "{}" as an incompatible data ' + "member (i.e., nuclide or S(a,b) table) " + "has already been added".format(self._id, macroscopic) + ) raise ValueError(msg) if not isinstance(macroscopic, str): - msg = 'Unable to add a Macroscopic to Material ID="{}" with a ' \ - 'non-string value "{}"'.format(self._id, macroscopic) + msg = ( + 'Unable to add a Macroscopic to Material ID="{}" with a ' + 'non-string value "{}"'.format(self._id, macroscopic) + ) raise ValueError(msg) if self._macroscopic is None: self._macroscopic = macroscopic else: - msg = 'Unable to add a Macroscopic to Material ID="{}". ' \ - 'Only one Macroscopic allowed per ' \ - 'Material.'.format(self._id) + msg = ( + 'Unable to add a Macroscopic to Material ID="{}". ' + "Only one Macroscopic allowed per " + "Material.".format(self._id) + ) raise ValueError(msg) # Generally speaking, the density for a macroscopic object will @@ -672,7 +694,7 @@ def add_macroscopic(self, macroscopic: str): # Of course, if the user has already set a value of density, # then we will not override it. if self._density is None: - self.set_density('macro', 1.0) + self.set_density("macro", 1.0) def remove_macroscopic(self, macroscopic: str): """Remove a macroscopic from the material @@ -685,19 +707,26 @@ def remove_macroscopic(self, macroscopic: str): """ if not isinstance(macroscopic, str): - msg = 'Unable to remove a Macroscopic "{}" in Material ID="{}" ' \ - 'since it is not a string'.format(self._id, macroscopic) + msg = ( + 'Unable to remove a Macroscopic "{}" in Material ID="{}" ' + "since it is not a string".format(self._id, macroscopic) + ) raise ValueError(msg) # If the Material contains the Macroscopic, delete it if macroscopic == self._macroscopic: self._macroscopic = None - def add_element(self, element: str, percent: float, percent_type: str = 'ao', - enrichment: float | None = None, - enrichment_target: str | None = None, - enrichment_type: str | None = None, - cross_sections: str | None = None): + def add_element( + self, + element: str, + percent: float, + percent_type: str = "ao", + enrichment: float | None = None, + enrichment_target: str | None = None, + enrichment_type: str | None = None, + cross_sections: str | None = None, + ): """Add a natural element to the material Parameters @@ -735,15 +764,17 @@ def add_element(self, element: str, percent: float, percent_type: str = 'ao', """ - cv.check_type('nuclide', element, str) - cv.check_type('percent', percent, Real) - cv.check_greater_than('percent', percent, 0, equality=True) - cv.check_value('percent type', percent_type, {'ao', 'wo'}) + cv.check_type("nuclide", element, str) + cv.check_type("percent", percent, Real) + cv.check_greater_than("percent", percent, 0, equality=True) + cv.check_value("percent type", percent_type, {"ao", "wo"}) # Make sure element name is just that if not element.isalpha(): - raise ValueError("Element name should be given by the " - "element's symbol or name, e.g., 'Zr', 'zirconium'") + raise ValueError( + "Element name should be given by the " + "element's symbol or name, e.g., 'Zr', 'zirconium'" + ) if self._ncrystal_cfg is not None: raise ValueError("Cannot add elements to NCrystal material") @@ -768,49 +799,65 @@ def add_element(self, element: str, percent: float, percent_type: str = 'ao', raise ValueError(msg) if self._macroscopic is not None: - msg = 'Unable to add an Element to Material ID="{}" as a ' \ - 'macroscopic data-set has already been added'.format(self._id) + msg = ( + 'Unable to add an Element to Material ID="{}" as a ' + "macroscopic data-set has already been added".format(self._id) + ) raise ValueError(msg) if enrichment is not None and enrichment_target is None: if not isinstance(enrichment, Real): - msg = 'Unable to add an Element to Material ID="{}" with a ' \ - 'non-floating point enrichment value "{}"'\ - .format(self._id, enrichment) + msg = ( + 'Unable to add an Element to Material ID="{}" with a ' + 'non-floating point enrichment value "{}"'.format( + self._id, enrichment + ) + ) raise ValueError(msg) - elif element != 'U': - msg = 'Unable to use enrichment for element {} which is not ' \ - 'uranium for Material ID="{}"'.format(element, self._id) + elif element != "U": + msg = ( + "Unable to use enrichment for element {} which is not " + 'uranium for Material ID="{}"'.format(element, self._id) + ) raise ValueError(msg) # Check that the enrichment is in the valid range - cv.check_less_than('enrichment', enrichment, 100./1.008) - cv.check_greater_than('enrichment', enrichment, 0., equality=True) + cv.check_less_than("enrichment", enrichment, 100.0 / 1.008) + cv.check_greater_than("enrichment", enrichment, 0.0, equality=True) if enrichment > 5.0: - msg = 'A uranium enrichment of {} was given for Material ID='\ - '"{}". OpenMC assumes the U234/U235 mass ratio is '\ - 'constant at 0.008, which is only valid at low ' \ - 'enrichments. Consider setting the isotopic ' \ - 'composition manually for enrichments over 5%.'.\ - format(enrichment, self._id) + msg = ( + "A uranium enrichment of {} was given for Material ID=" + '"{}". OpenMC assumes the U234/U235 mass ratio is ' + "constant at 0.008, which is only valid at low " + "enrichments. Consider setting the isotopic " + "composition manually for enrichments over 5%.".format( + enrichment, self._id + ) + ) warnings.warn(msg) # Add naturally-occuring isotopes element = openmc.Element(element) - for nuclide in element.expand(percent, - percent_type, - enrichment, - enrichment_target, - enrichment_type, - cross_sections): + for nuclide in element.expand( + percent, + percent_type, + enrichment, + enrichment_target, + enrichment_type, + cross_sections, + ): self.add_nuclide(*nuclide) - def add_elements_from_formula(self, formula: str, percent_type: str = 'ao', - enrichment: float | None = None, - enrichment_target: str | None = None, - enrichment_type: str | None = None): + def add_elements_from_formula( + self, + formula: str, + percent_type: str = "ao", + enrichment: float | None = None, + enrichment_target: str | None = None, + enrichment_type: str | None = None, + ): """Add a elements from a chemical formula to the material. .. versionadded:: 0.12 @@ -842,11 +889,13 @@ def add_elements_from_formula(self, formula: str, percent_type: str = 'ao', natural composition is added to the material. """ - cv.check_type('formula', formula, str) + cv.check_type("formula", formula, str) - if '.' in formula: - msg = 'Non-integer multiplier values are not accepted. The ' \ - 'input formula {} contains a "." character.'.format(formula) + if "." in formula: + msg = ( + "Non-integer multiplier values are not accepted. The " + 'input formula {} contains a "." character.'.format(formula) + ) raise ValueError(msg) # Tokenizes the formula and check validity of tokens @@ -855,28 +904,33 @@ def add_elements_from_formula(self, formula: str, percent_type: str = 'ao', for token in row: if token.isalpha(): if token == "n" or token not in openmc.data.ATOMIC_NUMBER: - msg = f'Formula entry {token} not an element symbol.' - raise ValueError(msg) - elif token not in ['(', ')', ''] and not token.isdigit(): - msg = 'Formula must be made from a sequence of ' \ - 'element symbols, integers, and brackets. ' \ - '{} is not an allowable entry.'.format(token) + msg = f"Formula entry {token} not an element symbol." raise ValueError(msg) + elif token not in ["(", ")", ""] and not token.isdigit(): + msg = ( + "Formula must be made from a sequence of " + "element symbols, integers, and brackets. " + "{} is not an allowable entry.".format(token) + ) + raise ValueError(msg) # Checks that the number of opening and closing brackets are equal - if formula.count('(') != formula.count(')'): - msg = 'Number of opening and closing brackets is not equal ' \ - 'in the input formula {}.'.format(formula) + if formula.count("(") != formula.count(")"): + msg = ( + "Number of opening and closing brackets is not equal " + "in the input formula {}.".format(formula) + ) raise ValueError(msg) # Checks that every part of the original formula has been tokenized for row in tokens: for token in row: - formula = formula.replace(token, '', 1) + formula = formula.replace(token, "", 1) if len(formula) != 0: - msg = 'Part of formula was not successfully parsed as an ' \ - 'element symbol, bracket or integer. {} was not parsed.' \ - .format(formula) + msg = ( + "Part of formula was not successfully parsed as an " + "element symbol, bracket or integer. {} was not parsed.".format(formula) + ) raise ValueError(msg) # Works through the tokens building a stack @@ -898,10 +952,20 @@ def add_elements_from_formula(self, formula: str, percent_type: str = 'ao', # Adds each element and percent to the material for element, percent in zip(elements, norm_percents): - if enrichment_target is not None and element == re.sub(r'\d+$', '', enrichment_target): - self.add_element(element, percent, percent_type, enrichment, - enrichment_target, enrichment_type) - elif enrichment is not None and enrichment_target is None and element == 'U': + if enrichment_target is not None and element == re.sub( + r"\d+$", "", enrichment_target + ): + self.add_element( + element, + percent, + percent_type, + enrichment, + enrichment_target, + enrichment_type, + ) + elif ( + enrichment is not None and enrichment_target is None and element == "U" + ): self.add_element(element, percent, percent_type, enrichment) else: self.add_element(element, percent, percent_type) @@ -922,18 +986,22 @@ def add_s_alpha_beta(self, name: str, fraction: float = 1.0): """ if self._macroscopic is not None: - msg = 'Unable to add an S(a,b) table to Material ID="{}" as a ' \ - 'macroscopic data-set has already been added'.format(self._id) + msg = ( + 'Unable to add an S(a,b) table to Material ID="{}" as a ' + "macroscopic data-set has already been added".format(self._id) + ) raise ValueError(msg) if not isinstance(name, str): - msg = 'Unable to add an S(a,b) table to Material ID="{}" with a ' \ - 'non-string table name "{}"'.format(self._id, name) + msg = ( + 'Unable to add an S(a,b) table to Material ID="{}" with a ' + 'non-string table name "{}"'.format(self._id, name) + ) raise ValueError(msg) - cv.check_type('S(a,b) fraction', fraction, Real) - cv.check_greater_than('S(a,b) fraction', fraction, 0.0, True) - cv.check_less_than('S(a,b) fraction', fraction, 1.0, True) + cv.check_type("S(a,b) fraction", fraction, Real) + cv.check_greater_than("S(a,b) fraction", fraction, 0.0, True) + cv.check_less_than("S(a,b) fraction", fraction, 1.0, True) self._sab.append((name, fraction)) def make_isotropic_in_lab(self): @@ -951,7 +1019,7 @@ def get_elements(self) -> list[str]: """ - return sorted({re.split(r'(\d+)', i)[0] for i in self.get_nuclides()}) + return sorted({re.split(r"(\d+)", i)[0] for i in self.get_nuclides()}) def get_nuclides(self, element: str | None = None) -> list[str]: """Returns a list of all nuclides in the material, if the element @@ -973,7 +1041,7 @@ def get_nuclides(self, element: str | None = None) -> list[str]: matching_nuclides = [] if element: for nuclide in self._nuclides: - if re.split(r'(\d+)', nuclide.name)[0] == element: + if re.split(r"(\d+)", nuclide.name)[0] == element: if nuclide.name not in matching_nuclides: matching_nuclides.append(nuclide.name) else: @@ -1001,7 +1069,9 @@ def get_nuclide_densities(self) -> dict[str, tuple]: return nuclides - def get_nuclide_atom_densities(self, nuclide: str | None = None) -> dict[str, float]: + def get_nuclide_atom_densities( + self, nuclide: str | None = None + ) -> dict[str, float]: """Returns one or all nuclides in the material and their atomic densities in units of atom/b-cm @@ -1026,19 +1096,19 @@ def get_nuclide_atom_densities(self, nuclide: str | None = None) -> dict[str, fl """ sum_density = False - if self.density_units == 'sum': + if self.density_units == "sum": sum_density = True - density = 0. - elif self.density_units == 'macro': + density = 0.0 + elif self.density_units == "macro": density = self.density - elif self.density_units == 'g/cc' or self.density_units == 'g/cm3': + elif self.density_units == "g/cc" or self.density_units == "g/cm3": density = -self.density - elif self.density_units == 'kg/m3': + elif self.density_units == "kg/m3": density = -0.001 * self.density - elif self.density_units == 'atom/b-cm': + elif self.density_units == "atom/b-cm": density = self.density - elif self.density_units == 'atom/cm3' or self.density_units == 'atom/cc': - density = 1.e-24 * self.density + elif self.density_units == "atom/cm3" or self.density_units == "atom/cc": + density = 1.0e-24 * self.density # For ease of processing split out nuc, nuc_density, # and nuc_density_type into separate arrays @@ -1058,15 +1128,16 @@ def get_nuclide_atom_densities(self, nuclide: str | None = None) -> dict[str, fl if sum_density: density = np.sum(nuc_densities) - percent_in_atom = np.all(nuc_density_types == 'ao') - density_in_atom = density > 0. - sum_percent = 0. + percent_in_atom = np.all(nuc_density_types == "ao") + density_in_atom = density > 0.0 + sum_percent = 0.0 # Convert the weight amounts to atomic amounts if not percent_in_atom: for n, nuc in enumerate(nucs): - nuc_densities[n] *= self.average_molar_mass / \ - openmc.data.atomic_mass(nuc) + nuc_densities[n] *= self.average_molar_mass / openmc.data.atomic_mass( + nuc + ) # Now that we have the atomic amounts, lets finish calculating densities sum_percent = np.sum(nuc_densities) @@ -1074,8 +1145,9 @@ def get_nuclide_atom_densities(self, nuclide: str | None = None) -> dict[str, fl # Convert the mass density to an atom density if not density_in_atom: - density = -density / self.average_molar_mass * 1.e-24 \ - * openmc.data.AVOGADRO + density = ( + -density / self.average_molar_mass * 1.0e-24 * openmc.data.AVOGADRO + ) nuc_densities = density * nuc_densities @@ -1086,7 +1158,9 @@ def get_nuclide_atom_densities(self, nuclide: str | None = None) -> dict[str, fl return nuclides - def get_element_atom_densities(self, element: str | None = None) -> dict[str, float]: + def get_element_atom_densities( + self, element: str | None = None + ) -> dict[str, float]: """Returns one or all elements in the material and their atomic densities in units of atom/b-cm @@ -1123,13 +1197,16 @@ def get_element_atom_densities(self, element: str | None = None) -> dict[str, fl # If specific element was requested, make sure it is present if element is not None and element not in densities: - raise ValueError(f'Element {element} not found in material.') + raise ValueError(f"Element {element} not found in material.") return densities - - def get_activity(self, units: str = 'Bq/cm3', by_nuclide: bool = False, - volume: float | None = None) -> dict[str, float] | float: + def get_activity( + self, + units: str = "Bq/cm3", + by_nuclide: bool = False, + volume: float | None = None, + ) -> dict[str, float] | float: """Returns the activity of the material or for each nuclide in the material in units of [Bq], [Bq/g] or [Bq/cm3]. @@ -1158,14 +1235,14 @@ def get_activity(self, units: str = 'Bq/cm3', by_nuclide: bool = False, of the material is returned as a float. """ - cv.check_value('units', units, {'Bq', 'Bq/g', 'Bq/cm3'}) - cv.check_type('by_nuclide', by_nuclide, bool) + cv.check_value("units", units, {"Bq", "Bq/g", "Bq/cm3"}) + cv.check_type("by_nuclide", by_nuclide, bool) - if units == 'Bq': + if units == "Bq": multiplier = volume if volume is not None else self.volume - elif units == 'Bq/cm3': + elif units == "Bq/cm3": multiplier = 1 - elif units == 'Bq/g': + elif units == "Bq/g": multiplier = 1.0 / self.get_mass_density() activity = {} @@ -1175,8 +1252,9 @@ def get_activity(self, units: str = 'Bq/cm3', by_nuclide: bool = False, return activity if by_nuclide else sum(activity.values()) - def get_decay_heat(self, units: str = 'W', by_nuclide: bool = False, - volume: float | None = None) -> dict[str, float] | float: + def get_decay_heat( + self, units: str = "W", by_nuclide: bool = False, volume: float | None = None + ) -> dict[str, float] | float: """Returns the decay heat of the material or for each nuclide in the material in units of [W], [W/g] or [W/cm3]. @@ -1205,14 +1283,14 @@ def get_decay_heat(self, units: str = 'W', by_nuclide: bool = False, of the material is returned as a float. """ - cv.check_value('units', units, {'W', 'W/g', 'W/cm3'}) - cv.check_type('by_nuclide', by_nuclide, bool) + cv.check_value("units", units, {"W", "W/g", "W/cm3"}) + cv.check_type("by_nuclide", by_nuclide, bool) - if units == 'W': + if units == "W": multiplier = volume if volume is not None else self.volume - elif units == 'W/cm3': + elif units == "W/cm3": multiplier = 1 - elif units == 'W/g': + elif units == "W/g": multiplier = 1.0 / self.get_mass_density() decayheat = {} @@ -1220,7 +1298,9 @@ def get_decay_heat(self, units: str = 'W', by_nuclide: bool = False, decay_erg = openmc.data.decay_energy(nuclide) inv_seconds = openmc.data.decay_constant(nuclide) decay_erg *= openmc.data.JOULE_PER_EV - decayheat[nuclide] = inv_seconds * decay_erg * 1e24 * atoms_per_bcm * multiplier + decayheat[nuclide] = ( + inv_seconds * decay_erg * 1e24 * atoms_per_bcm * multiplier + ) return decayheat if by_nuclide else sum(decayheat.values()) @@ -1269,13 +1349,21 @@ def get_mass_density(self, nuclide: str | None = None) -> float: """ mass_density = 0.0 - for nuc, atoms_per_bcm in self.get_nuclide_atom_densities(nuclide=nuclide).items(): - density_i = 1e24 * atoms_per_bcm * openmc.data.atomic_mass(nuc) \ - / openmc.data.AVOGADRO + for nuc, atoms_per_bcm in self.get_nuclide_atom_densities( + nuclide=nuclide + ).items(): + density_i = ( + 1e24 + * atoms_per_bcm + * openmc.data.atomic_mass(nuc) + / openmc.data.AVOGADRO + ) mass_density += density_i return mass_density - def get_mass(self, nuclide: str | None = None, volume: float | None = None) -> float: + def get_mass( + self, nuclide: str | None = None, volume: float | None = None + ) -> float: """Return mass of one or all nuclides. Note that this method requires that the :attr:`Material.volume` has @@ -1303,7 +1391,7 @@ def get_mass(self, nuclide: str | None = None, volume: float | None = None) -> f volume = self.volume if volume is None: raise ValueError("Volume must be set in order to determine mass.") - return volume*self.get_mass_density(nuclide) + return volume * self.get_mass_density(nuclide) def clone(self, memo: dict | None = None) -> Material: """Create a copy of this material with a new unique ID. @@ -1354,7 +1442,7 @@ def _get_nuclide_xml(self, nuclide: NuclideTuple) -> ET.Element: if abs(val) < _SMALLEST_NORMAL: val = 0.0 - if nuclide.percent_type == 'ao': + if nuclide.percent_type == "ao": xml_element.set("ao", str(val)) else: xml_element.set("wo", str(val)) @@ -1368,20 +1456,27 @@ def _get_macroscopic_xml(self, macroscopic: str) -> ET.Element: return xml_element def _get_nuclides_xml( - self, nuclides: Iterable[NuclideTuple], - nuclides_to_ignore: Iterable[str] | None = None)-> list[ET.Element]: + self, + nuclides: Iterable[NuclideTuple], + nuclides_to_ignore: Iterable[str] | None = None, + ) -> list[ET.Element]: xml_elements = [] # Remove any nuclides to ignore from the XML export if nuclides_to_ignore: - nuclides = [nuclide for nuclide in nuclides if nuclide.name not in nuclides_to_ignore] + nuclides = [ + nuclide + for nuclide in nuclides + if nuclide.name not in nuclides_to_ignore + ] xml_elements = [self._get_nuclide_xml(nuclide) for nuclide in nuclides] return xml_elements def to_xml_element( - self, nuclides_to_ignore: Iterable[str] | None = None) -> ET.Element: + self, nuclides_to_ignore: Iterable[str] | None = None + ) -> ET.Element: """Return XML representation of the material Parameters @@ -1413,7 +1508,9 @@ def to_xml_element( if self._sab: raise ValueError("NCrystal materials are not compatible with S(a,b).") if self._macroscopic is not None: - raise ValueError("NCrystal materials are not compatible with macroscopic cross sections.") + raise ValueError( + "NCrystal materials are not compatible with macroscopic cross sections." + ) element.set("cfg", str(self._ncrystal_cfg)) @@ -1422,18 +1519,19 @@ def to_xml_element( element.set("temperature", str(self.temperature)) # Create density XML subelement - if self._density is not None or self._density_units == 'sum': + if self._density is not None or self._density_units == "sum": subelement = ET.SubElement(element, "density") - if self._density_units != 'sum': + if self._density_units != "sum": subelement.set("value", str(self._density)) subelement.set("units", self._density_units) else: - raise ValueError(f'Density has not been set for material {self.id}!') + raise ValueError(f"Density has not been set for material {self.id}!") if self._macroscopic is None: # Create nuclide XML subelements - subelements = self._get_nuclides_xml(self._nuclides, - nuclides_to_ignore=nuclides_to_ignore) + subelements = self._get_nuclides_xml( + self._nuclides, nuclides_to_ignore=nuclides_to_ignore + ) for subelement in subelements: element.append(subelement) else: @@ -1450,13 +1548,18 @@ def to_xml_element( if self._isotropic: subelement = ET.SubElement(element, "isotropic") - subelement.text = ' '.join(self._isotropic) + subelement.text = " ".join(self._isotropic) return element @classmethod - def mix_materials(cls, materials, fracs: Iterable[float], - percent_type: str = 'ao', name: str | None = None) -> Material: + def mix_materials( + cls, + materials, + fracs: Iterable[float], + percent_type: str = "ao", + name: str | None = None, + ) -> Material: """Mix materials together based on atom, weight, or volume fractions .. versionadded:: 0.12 @@ -1483,43 +1586,49 @@ def mix_materials(cls, materials, fracs: Iterable[float], """ - cv.check_type('materials', materials, Iterable, Material) - cv.check_type('fracs', fracs, Iterable, Real) - cv.check_value('percent type', percent_type, {'ao', 'wo', 'vo'}) + cv.check_type("materials", materials, Iterable, Material) + cv.check_type("fracs", fracs, Iterable, Real) + cv.check_value("percent type", percent_type, {"ao", "wo", "vo"}) fracs = np.asarray(fracs) - void_frac = 1. - np.sum(fracs) + void_frac = 1.0 - np.sum(fracs) # Warn that fractions don't add to 1, set remainder to void, or raise # an error if percent_type isn't 'vo' - if not np.isclose(void_frac, 0.): - if percent_type in ('ao', 'wo'): - msg = ('A non-zero void fraction is not acceptable for ' - 'percent_type: {}'.format(percent_type)) + if not np.isclose(void_frac, 0.0): + if percent_type in ("ao", "wo"): + msg = ( + "A non-zero void fraction is not acceptable for " + "percent_type: {}".format(percent_type) + ) raise ValueError(msg) else: - msg = ('Warning: sum of fractions do not add to 1, void ' - 'fraction set to {}'.format(void_frac)) + msg = ( + "Warning: sum of fractions do not add to 1, void " + "fraction set to {}".format(void_frac) + ) warnings.warn(msg) # Calculate appropriate weights which are how many cc's of each # material are found in 1cc of the composite material amms = np.asarray([mat.average_molar_mass for mat in materials]) mass_dens = np.asarray([mat.get_mass_density() for mat in materials]) - if percent_type == 'ao': + if percent_type == "ao": wgts = fracs * amms / mass_dens wgts /= np.sum(wgts) - elif percent_type == 'wo': + elif percent_type == "wo": wgts = fracs / mass_dens wgts /= np.sum(wgts) - elif percent_type == 'vo': + elif percent_type == "vo": wgts = fracs # If any of the involved materials contain S(a,b) tables raise an error sab_names = set(sab[0] for mat in materials for sab in mat._sab) if sab_names: - msg = ('Currently we do not support mixing materials containing ' - 'S(a,b) tables') + msg = ( + "Currently we do not support mixing materials containing " + "S(a,b) tables" + ) raise NotImplementedError(msg) # Add nuclide densities weighted by appropriate fractions @@ -1527,25 +1636,25 @@ def mix_materials(cls, materials, fracs: Iterable[float], mass_per_cc = defaultdict(float) for mat, wgt in zip(materials, wgts): for nuc, atoms_per_bcm in mat.get_nuclide_atom_densities().items(): - nuc_per_cc = wgt*1.e24*atoms_per_bcm + nuc_per_cc = wgt * 1.0e24 * atoms_per_bcm nuclides_per_cc[nuc] += nuc_per_cc - mass_per_cc[nuc] += nuc_per_cc*openmc.data.atomic_mass(nuc) / \ - openmc.data.AVOGADRO + mass_per_cc[nuc] += ( + nuc_per_cc * openmc.data.atomic_mass(nuc) / openmc.data.AVOGADRO + ) # Create the new material with the desired name if name is None: - name = '-'.join([f'{m.name}({f})' for m, f in - zip(materials, fracs)]) + name = "-".join([f"{m.name}({f})" for m, f in zip(materials, fracs)]) new_mat = cls(name=name) # Compute atom fractions of nuclides and add them to the new material tot_nuclides_per_cc = np.sum([dens for dens in nuclides_per_cc.values()]) for nuc, atom_dens in nuclides_per_cc.items(): - new_mat.add_nuclide(nuc, atom_dens/tot_nuclides_per_cc, 'ao') + new_mat.add_nuclide(nuc, atom_dens / tot_nuclides_per_cc, "ao") # Compute mass density for the new material and set it new_density = np.sum([dens for dens in mass_per_cc.values()]) - new_mat.set_density('g/cm3', new_density) + new_mat.set_density("g/cm3", new_density) # If any of the involved materials is depletable, the new material is # depletable @@ -1568,46 +1677,46 @@ def from_xml_element(cls, elem: ET.Element) -> Material: Material generated from XML element """ - mat_id = int(elem.get('id')) + mat_id = int(elem.get("id")) # Add NCrystal material from cfg string if "cfg" in elem.attrib: cfg = elem.get("cfg") return Material.from_ncrystal(cfg, material_id=mat_id) mat = cls(mat_id) - mat.name = elem.get('name') + mat.name = elem.get("name") if "temperature" in elem.attrib: mat.temperature = float(elem.get("temperature")) - if 'volume' in elem.attrib: - mat.volume = float(elem.get('volume')) - mat.depletable = bool(elem.get('depletable')) + if "volume" in elem.attrib: + mat.volume = float(elem.get("volume")) + mat.depletable = bool(elem.get("depletable")) # Get each nuclide - for nuclide in elem.findall('nuclide'): - name = nuclide.attrib['name'] - if 'ao' in nuclide.attrib: - mat.add_nuclide(name, float(nuclide.attrib['ao'])) - elif 'wo' in nuclide.attrib: - mat.add_nuclide(name, float(nuclide.attrib['wo']), 'wo') + for nuclide in elem.findall("nuclide"): + name = nuclide.attrib["name"] + if "ao" in nuclide.attrib: + mat.add_nuclide(name, float(nuclide.attrib["ao"])) + elif "wo" in nuclide.attrib: + mat.add_nuclide(name, float(nuclide.attrib["wo"]), "wo") # Get each S(a,b) table - for sab in elem.findall('sab'): - fraction = float(sab.get('fraction', 1.0)) - mat.add_s_alpha_beta(sab.get('name'), fraction) + for sab in elem.findall("sab"): + fraction = float(sab.get("fraction", 1.0)) + mat.add_s_alpha_beta(sab.get("name"), fraction) # Get total material density - density = elem.find('density') - units = density.get('units') - if units == 'sum': + density = elem.find("density") + units = density.get("units") + if units == "sum": mat.set_density(units) else: - value = float(density.get('value')) + value = float(density.get("value")) mat.set_density(units, value) # Check for isotropic scattering nuclides - isotropic = elem.find('isotropic') + isotropic = elem.find("isotropic") if isotropic is not None: mat.isotropic = isotropic.text.split() @@ -1646,7 +1755,7 @@ class Materials(cv.CheckedList): """ def __init__(self, materials=None): - super().__init__(Material, 'materials collection') + super().__init__(Material, "materials collection") self._cross_sections = None if materials is not None: @@ -1689,8 +1798,15 @@ def make_isotropic_in_lab(self): for material in self: material.make_isotropic_in_lab() - def _write_xml(self, file, header=True, level=0, spaces_per_level=2, - trailing_indent=True, nuclides_to_ignore=None): + def _write_xml( + self, + file, + header=True, + level=0, + spaces_per_level=2, + trailing_indent=True, + nuclides_to_ignore=None, + ): """Writes XML content of the materials to an open file handle. Parameters @@ -1709,41 +1825,44 @@ def _write_xml(self, file, header=True, level=0, spaces_per_level=2, Nuclides to ignore when exporting to XML. """ - indentation = level*spaces_per_level*' ' + indentation = level * spaces_per_level * " " # Write the header and the opening tag for the root element. if header: file.write("\n") - file.write(indentation+'\n') + file.write(indentation + "\n") # Write the element. if self.cross_sections is not None: - element = ET.Element('cross_sections') + element = ET.Element("cross_sections") element.text = str(self.cross_sections) - clean_indentation(element, level=level+1) - element.tail = element.tail.strip(' ') - file.write((level+1)*spaces_per_level*' ') + clean_indentation(element, level=level + 1) + element.tail = element.tail.strip(" ") + file.write((level + 1) * spaces_per_level * " ") reorder_attributes(element) # TODO: Remove when support is Python 3.8+ file.write(ET.tostring(element, encoding="unicode")) # Write the elements. for material in sorted(self, key=lambda x: x.id): element = material.to_xml_element(nuclides_to_ignore=nuclides_to_ignore) - clean_indentation(element, level=level+1) - element.tail = element.tail.strip(' ') - file.write((level+1)*spaces_per_level*' ') + clean_indentation(element, level=level + 1) + element.tail = element.tail.strip(" ") + file.write((level + 1) * spaces_per_level * " ") reorder_attributes(element) # TODO: Remove when support is Python 3.8+ file.write(ET.tostring(element, encoding="unicode")) # Write the closing tag for the root element. - file.write(indentation+'\n') + file.write(indentation + "\n") # Write a trailing indentation for the next element # at this level if needed if trailing_indent: file.write(indentation) - def export_to_xml(self, path: PathLike = 'materials.xml', - nuclides_to_ignore: Iterable[str] | None = None): + def export_to_xml( + self, + path: PathLike = "materials.xml", + nuclides_to_ignore: Iterable[str] | None = None, + ): """Export material collection to an XML file. Parameters @@ -1757,13 +1876,12 @@ def export_to_xml(self, path: PathLike = 'materials.xml', # Check if path is a directory p = Path(path) if p.is_dir(): - p /= 'materials.xml' + p /= "materials.xml" # Write materials to the file one-at-a-time. This significantly reduces # memory demand over allocating a complete ElementTree and writing it in # one go. - with open(str(p), 'w', encoding='utf-8', - errors='xmlcharrefreplace') as fh: + with open(str(p), "w", encoding="utf-8", errors="xmlcharrefreplace") as fh: self._write_xml(fh, nuclides_to_ignore=nuclides_to_ignore) @classmethod @@ -1783,18 +1901,18 @@ def from_xml_element(cls, elem) -> Materials: """ # Generate each material materials = cls() - for material in elem.findall('material'): + for material in elem.findall("material"): materials.append(Material.from_xml_element(material)) # Check for cross sections settings - xs = elem.find('cross_sections') + xs = elem.find("cross_sections") if xs is not None: materials.cross_sections = xs.text return materials @classmethod - def from_xml(cls, path: PathLike = 'materials.xml') -> Materials: + def from_xml(cls, path: PathLike = "materials.xml") -> Materials: """Generate materials collection from XML file Parameters diff --git a/openmc/mesh.py b/openmc/mesh.py index ef5c6c78410..9fefbc8c81f 100644 --- a/openmc/mesh.py +++ b/openmc/mesh.py @@ -46,7 +46,7 @@ class MeshBase(IDManagerMixin, ABC): next_id = 1 used_ids = set() - def __init__(self, mesh_id: int | None = None, name: str = ''): + def __init__(self, mesh_id: int | None = None, name: str = ""): # Initialize Mesh class attributes self.id = mesh_id self.name = name @@ -61,7 +61,7 @@ def name(self, name: str): cv.check_type(f'name for mesh ID="{self._id}"', name, str) self._name = name else: - self._name = '' + self._name = "" @property def bounding_box(self) -> openmc.BoundingBox: @@ -73,16 +73,16 @@ def indices(self): pass def __repr__(self): - string = type(self).__name__ + '\n' - string += '{0: <16}{1}{2}\n'.format('\tID', '=\t', self._id) - string += '{0: <16}{1}{2}\n'.format('\tName', '=\t', self._name) + string = type(self).__name__ + "\n" + string += "{0: <16}{1}{2}\n".format("\tID", "=\t", self._id) + string += "{0: <16}{1}{2}\n".format("\tName", "=\t", self._name) return string def _volume_dim_check(self): - if self.n_dimension != 3 or \ - any([d == 0 for d in self.dimension]): - raise RuntimeError(f'Mesh {self.id} is not 3D. ' - 'Volumes cannot be provided.') + if self.n_dimension != 3 or any([d == 0 for d in self.dimension]): + raise RuntimeError( + f"Mesh {self.id} is not 3D. " "Volumes cannot be provided." + ) @classmethod def from_hdf5(cls, group: h5py.Group): @@ -100,16 +100,16 @@ def from_hdf5(cls, group: h5py.Group): """ - mesh_type = group['type'][()].decode() - if mesh_type == 'regular': + mesh_type = group["type"][()].decode() + if mesh_type == "regular": return RegularMesh.from_hdf5(group) - elif mesh_type == 'rectilinear': + elif mesh_type == "rectilinear": return RectilinearMesh.from_hdf5(group) - elif mesh_type == 'cylindrical': + elif mesh_type == "cylindrical": return CylindricalMesh.from_hdf5(group) - elif mesh_type == 'spherical': + elif mesh_type == "spherical": return SphericalMesh.from_hdf5(group) - elif mesh_type == 'unstructured': + elif mesh_type == "unstructured": return UnstructuredMesh.from_hdf5(group) else: raise ValueError('Unrecognized mesh type: "' + mesh_type + '"') @@ -129,28 +129,28 @@ def from_xml_element(cls, elem: ET.Element): an openmc mesh object """ - mesh_type = get_text(elem, 'type') + mesh_type = get_text(elem, "type") - if mesh_type == 'regular' or mesh_type is None: + if mesh_type == "regular" or mesh_type is None: return RegularMesh.from_xml_element(elem) - elif mesh_type == 'rectilinear': + elif mesh_type == "rectilinear": return RectilinearMesh.from_xml_element(elem) - elif mesh_type == 'cylindrical': + elif mesh_type == "cylindrical": return CylindricalMesh.from_xml_element(elem) - elif mesh_type == 'spherical': + elif mesh_type == "spherical": return SphericalMesh.from_xml_element(elem) - elif mesh_type == 'unstructured': + elif mesh_type == "unstructured": return UnstructuredMesh.from_xml_element(elem) else: raise ValueError(f'Unrecognized mesh type "{mesh_type}" found.') def get_homogenized_materials( - self, - model: openmc.Model, - n_samples: int = 10_000, - prn_seed: int | None = None, - include_void: bool = True, - **kwargs + self, + model: openmc.Model, + n_samples: int = 10_000, + prn_seed: int | None = None, + include_void: bool = True, + **kwargs, ) -> list[openmc.Material]: """Generate homogenized materials over each element in a mesh. @@ -185,7 +185,7 @@ def get_homogenized_materials( original_tallies = model.tallies new_tally = openmc.Tally() new_tally.filters = [openmc.MeshFilter(self)] - new_tally.scores = ['flux'] + new_tally.scores = ["flux"] model.tallies = [new_tally] # Export model to XML @@ -216,9 +216,9 @@ def get_homogenized_materials( for cell in model.geometry.get_all_cells().values(): if isinstance(cell.fill, openmc.DAGMCUniverse): names = cell.fill.material_names - materials.update({ - mat.id: mat for mat in model.materials if mat.name in names - }) + materials.update( + {mat.id: mat for mat in model.materials if mat.name in names} + ) homogenized_materials = [] for mat_volume_list in mat_volume_by_element: @@ -243,9 +243,7 @@ def get_homogenized_materials( # Get list of materials and mix 'em up! mats = [materials[uid] for uid in material_ids] - homogenized_mat = openmc.Material.mix_materials( - mats, volume_fracs, 'vo' - ) + homogenized_mat = openmc.Material.mix_materials(mats, volume_fracs, "vo") homogenized_mat.volume = total_volume homogenized_materials.append(homogenized_mat) @@ -309,9 +307,9 @@ def vertices(self): @staticmethod def _generate_vertices(i_grid, j_grid, k_grid): """Returns an array with shape (i_grid.size, j_grid.size, k_grid.size, 3) - containing the corner vertices of mesh elements. + containing the corner vertices of mesh elements. """ - return np.stack(np.meshgrid(i_grid, j_grid, k_grid, indexing='ij'), axis=-1) + return np.stack(np.meshgrid(i_grid, j_grid, k_grid, indexing="ij"), axis=-1) @staticmethod def _generate_edge_midpoints(grids): @@ -353,8 +351,7 @@ def _generate_edge_midpoints(grids): @property def midpoint_vertices(self): - """Create vertices that lie on the midpoint of element edges - """ + """Create vertices that lie on the midpoint of element edges""" # generate edge midpoints needed for curvilinear element definition midpoint_vertices = self._generate_edge_midpoints(self._grids) @@ -380,19 +377,21 @@ def centroids(self): # this line ensures that the vertices aren't adjusted by the origin or # converted to the Cartesian system for cylindrical and spherical meshes vertices = StructuredMesh.vertices.fget(self) - s0 = (slice(0, -1),)*ndim + (slice(None),) - s1 = (slice(1, None),)*ndim + (slice(None),) + s0 = (slice(0, -1),) * ndim + (slice(None),) + s1 = (slice(1, None),) * ndim + (slice(None),) return (vertices[s0] + vertices[s1]) / 2 @property def num_mesh_cells(self): return np.prod(self.dimension) - def write_data_to_vtk(self, - filename: PathLike, - datasets: dict | None = None, - volume_normalization: bool = True, - curvilinear: bool = False): + def write_data_to_vtk( + self, + filename: PathLike, + datasets: dict | None = None, + volume_normalization: bool = True, + curvilinear: bool = False, + ): """Creates a VTK object of the mesh Parameters @@ -449,9 +448,7 @@ def write_data_to_vtk(self, dataset_array = vtk.vtkDoubleArray() dataset_array.SetName(label) - dataset_array.SetArray(nps.numpy_to_vtk(dataset), - dataset.size, - True) + dataset_array.SetArray(nps.numpy_to_vtk(dataset), dataset.size, True) vtk_grid.GetCellData().AddArray(dataset_array) writer.SetFileName(str(filename)) @@ -472,7 +469,9 @@ def _create_vtk_structured_grid(self): from vtk.util import numpy_support as nps vtkPts = vtk.vtkPoints() - vtkPts.SetData(nps.numpy_to_vtk(np.swapaxes(self.vertices, 0, 2).reshape(-1, 3), deep=True)) + vtkPts.SetData( + nps.numpy_to_vtk(np.swapaxes(self.vertices, 0, 2).reshape(-1, 3), deep=True) + ) vtk_grid = vtk.vtkStructuredGrid() vtk_grid.SetPoints(vtkPts) vtk_grid.SetDimensions(*[dim + 1 for dim in self.dimension]) @@ -504,7 +503,7 @@ def _create_vtk_unstructured_grid(self): # create a locator to assist with duplicate points locator = vtk.vtkPointLocator() locator.SetDataSet(vtk_grid) - locator.AutomaticOn() # autmoatically adds points to locator + locator.AutomaticOn() # autmoatically adds points to locator locator.InitPointInsertion(vtkPts, vtkPts.GetBounds()) locator.BuildLocator() @@ -556,7 +555,9 @@ def _insert_point(pnt): for n, (di, dj, dk) in enumerate(_HEX_VERTEX_CONN): # compute flat index into the point ID list based on i, j, k # of the vertex - flat_idx = np.ravel_multi_index((i+di, j+dj, k+dk), n_pnts, order='F') + flat_idx = np.ravel_multi_index( + (i + di, j + dj, k + dk), n_pnts, order="F" + ) # set corner vertices hex.GetPointIds().SetId(n, point_ids[flat_idx]) @@ -567,9 +568,9 @@ def _insert_point(pnt): flat_idx = corner_vertices.shape[0] + sum(n_midpoint_vertices[:dim]) # generate a flat index into the table of point IDs midpoint_shape = midpoint_vertices[dim].shape[:-1] - flat_idx += np.ravel_multi_index((i+di, j+dj, k+dk), - midpoint_shape, - order='F') + flat_idx += np.ravel_multi_index( + (i + di, j + dj, k + dk), midpoint_shape, order="F" + ) # set hex midpoint connectivity hex.GetPointIds().SetId(_N_HEX_VERTICES + n, point_ids[flat_idx]) @@ -599,7 +600,7 @@ def _check_vtk_datasets(self, datasets: dict): else: if len(dataset) == self.num_mesh_cells: raise ValueError(errmsg) - cv.check_type('data label', label, str) + cv.check_type("data label", label, str) class RegularMesh(StructuredMesh): @@ -639,7 +640,7 @@ class RegularMesh(StructuredMesh): """ - def __init__(self, mesh_id: int | None = None, name: str = ''): + def __init__(self, mesh_id: int | None = None, name: str = ""): super().__init__(mesh_id, name) self._dimension = None @@ -653,8 +654,8 @@ def dimension(self): @dimension.setter def dimension(self, dimension: Iterable[int]): - cv.check_type('mesh dimension', dimension, Iterable, Integral) - cv.check_length('mesh dimension', dimension, 1, 3) + cv.check_type("mesh dimension", dimension, Iterable, Integral) + cv.check_length("mesh dimension", dimension, 1, 3) self._dimension = dimension @property @@ -670,11 +671,13 @@ def lower_left(self): @lower_left.setter def lower_left(self, lower_left: Iterable[Real]): - cv.check_type('mesh lower_left', lower_left, Iterable, Real) - cv.check_length('mesh lower_left', lower_left, 1, 3) + cv.check_type("mesh lower_left", lower_left, Iterable, Real) + cv.check_length("mesh lower_left", lower_left, 1, 3) self._lower_left = lower_left - if self.upper_right is not None and any(np.isclose(self.upper_right, lower_left)): + if self.upper_right is not None and any( + np.isclose(self.upper_right, lower_left) + ): raise ValueError("Mesh cannot have zero thickness in any dimension") @property @@ -690,15 +693,17 @@ def upper_right(self): @upper_right.setter def upper_right(self, upper_right: Iterable[Real]): - cv.check_type('mesh upper_right', upper_right, Iterable, Real) - cv.check_length('mesh upper_right', upper_right, 1, 3) + cv.check_type("mesh upper_right", upper_right, Iterable, Real) + cv.check_length("mesh upper_right", upper_right, 1, 3) self._upper_right = upper_right if self._width is not None: self._width = None warnings.warn("Unsetting width attribute.") - if self.lower_left is not None and any(np.isclose(self.lower_left, upper_right)): + if self.lower_left is not None and any( + np.isclose(self.lower_left, upper_right) + ): raise ValueError("Mesh cannot have zero thickness in any dimension") @property @@ -709,13 +714,13 @@ def width(self): if self._lower_left is not None and self._dimension is not None: us = self._upper_right ls = self._lower_left - dims = self._dimension + dims = self._dimension return [(u - l) / d for u, l, d in zip(us, ls, dims)] @width.setter def width(self, width: Iterable[Real]): - cv.check_type('mesh width', width, Iterable, Real) - cv.check_length('mesh width', width, 1, 3) + cv.check_type("mesh width", width, Iterable, Real) + cv.check_length("mesh width", width, 1, 3) self._width = width if self._upper_right is not None: @@ -744,17 +749,17 @@ def indices(self): ndim = len(self._dimension) if ndim == 3: nx, ny, nz = self.dimension - return ((x, y, z) - for z in range(1, nz + 1) - for y in range(1, ny + 1) - for x in range(1, nx + 1)) + return ( + (x, y, z) + for z in range(1, nz + 1) + for y in range(1, ny + 1) + for x in range(1, nx + 1) + ) elif ndim == 2: nx, ny = self.dimension - return ((x, y) - for y in range(1, ny + 1) - for x in range(1, nx + 1)) + return ((x, y) for y in range(1, ny + 1) for x in range(1, nx + 1)) else: - nx, = self.dimension + (nx,) = self.dimension return ((x,) for x in range(1, nx + 1)) @property @@ -776,32 +781,32 @@ def _grids(self): yarr = np.linspace(y0, y1, ny + 1) return (xarr, yarr) else: - nx, = self.dimension - x0, = self.lower_left - x1, = self.upper_right + (nx,) = self.dimension + (x0,) = self.lower_left + (x1,) = self.upper_right return (np.linspace(x0, x1, nx + 1),) def __repr__(self): string = super().__repr__() - string += '{0: <16}{1}{2}\n'.format('\tDimensions', '=\t', self.n_dimension) - string += '{0: <16}{1}{2}\n'.format('\tVoxels', '=\t', self._dimension) - string += '{0: <16}{1}{2}\n'.format('\tLower left', '=\t', self._lower_left) - string += '{0: <16}{1}{2}\n'.format('\tUpper Right', '=\t', self.upper_right) - string += '{0: <16}{1}{2}\n'.format('\tWidth', '=\t', self.width) + string += "{0: <16}{1}{2}\n".format("\tDimensions", "=\t", self.n_dimension) + string += "{0: <16}{1}{2}\n".format("\tVoxels", "=\t", self._dimension) + string += "{0: <16}{1}{2}\n".format("\tLower left", "=\t", self._lower_left) + string += "{0: <16}{1}{2}\n".format("\tUpper Right", "=\t", self.upper_right) + string += "{0: <16}{1}{2}\n".format("\tWidth", "=\t", self.width) return string @classmethod def from_hdf5(cls, group: h5py.Group): - mesh_id = int(group.name.split('/')[-1].lstrip('mesh ')) + mesh_id = int(group.name.split("/")[-1].lstrip("mesh ")) # Read and assign mesh properties mesh = cls(mesh_id) - mesh.dimension = group['dimension'][()] - mesh.lower_left = group['lower_left'][()] - if 'width' in group: - mesh.width = group['width'][()] - elif 'upper_right' in group: - mesh.upper_right = group['upper_right'][()] + mesh.dimension = group["dimension"][()] + mesh.lower_left = group["lower_left"][()] + if "width" in group: + mesh.width = group["width"][()] + elif "upper_right" in group: + mesh.upper_right = group["upper_right"][()] else: raise IOError('Invalid mesh: must have one of "upper_right" or "width"') @@ -810,10 +815,10 @@ def from_hdf5(cls, group: h5py.Group): @classmethod def from_rect_lattice( cls, - lattice: 'openmc.RectLattice', + lattice: "openmc.RectLattice", division: int = 1, mesh_id: int | None = None, - name: str = '' + name: str = "", ): """Create mesh from an existing rectangular lattice @@ -835,25 +840,25 @@ def from_rect_lattice( RegularMesh instance """ - cv.check_type('rectangular lattice', lattice, openmc.RectLattice) + cv.check_type("rectangular lattice", lattice, openmc.RectLattice) shape = np.array(lattice.shape) - width = lattice.pitch*shape + width = lattice.pitch * shape mesh = cls(mesh_id=mesh_id, name=name) mesh.lower_left = lattice.lower_left mesh.upper_right = lattice.lower_left + width - mesh.dimension = shape*division + mesh.dimension = shape * division return mesh @classmethod def from_domain( cls, - domain: 'openmc.Cell' | 'openmc.Region' | 'openmc.Universe' | 'openmc.Geometry', + domain: "openmc.Cell" | "openmc.Region" | "openmc.Universe" | "openmc.Geometry", dimension: Sequence[int] = (10, 10, 10), mesh_id: int | None = None, - name: str = '' + name: str = "", ): """Create mesh from an existing openmc cell, region, universe or geometry by making use of the objects bounding box property. @@ -905,17 +910,17 @@ def to_xml_element(self): if self._dimension is not None: subelement = ET.SubElement(element, "dimension") - subelement.text = ' '.join(map(str, self._dimension)) + subelement.text = " ".join(map(str, self._dimension)) subelement = ET.SubElement(element, "lower_left") - subelement.text = ' '.join(map(str, self._lower_left)) + subelement.text = " ".join(map(str, self._lower_left)) if self._upper_right is not None: subelement = ET.SubElement(element, "upper_right") - subelement.text = ' '.join(map(str, self._upper_right)) + subelement.text = " ".join(map(str, self._upper_right)) if self._width is not None: subelement = ET.SubElement(element, "width") - subelement.text = ' '.join(map(str, self._width)) + subelement.text = " ".join(map(str, self._width)) return element @@ -934,26 +939,26 @@ def from_xml_element(cls, elem: ET.Element): Mesh generated from XML element """ - mesh_id = int(get_text(elem, 'id')) + mesh_id = int(get_text(elem, "id")) mesh = cls(mesh_id=mesh_id) - mesh_type = get_text(elem, 'type') + mesh_type = get_text(elem, "type") if mesh_type is not None: mesh.type = mesh_type - dimension = get_text(elem, 'dimension') + dimension = get_text(elem, "dimension") if dimension is not None: mesh.dimension = [int(x) for x in dimension.split()] - lower_left = get_text(elem, 'lower_left') + lower_left = get_text(elem, "lower_left") if lower_left is not None: mesh.lower_left = [float(x) for x in lower_left.split()] - upper_right = get_text(elem, 'upper_right') + upper_right = get_text(elem, "upper_right") if upper_right is not None: mesh.upper_right = [float(x) for x in upper_right.split()] - width = get_text(elem, 'width') + width = get_text(elem, "width") if width is not None: mesh.width = [float(x) for x in width.split()] @@ -988,23 +993,29 @@ def build_cells(self, bc: str | None = None): """ if bc is None: - bc = ['reflective'] * 6 + bc = ["reflective"] * 6 if len(bc) not in (4, 6): - raise ValueError('Boundary condition must be of length 4 or 6') + raise ValueError("Boundary condition must be of length 4 or 6") for entry in bc: - cv.check_value('bc', entry, _BOUNDARY_TYPES) + cv.check_value("bc", entry, _BOUNDARY_TYPES) n_dim = self.n_dimension # Build the cell which will contain the lattice - xplanes = [openmc.XPlane(self.lower_left[0], boundary_type=bc[0]), - openmc.XPlane(self.upper_right[0], boundary_type=bc[1])] + xplanes = [ + openmc.XPlane(self.lower_left[0], boundary_type=bc[0]), + openmc.XPlane(self.upper_right[0], boundary_type=bc[1]), + ] if n_dim == 1: - yplanes = [openmc.YPlane(-1e10, boundary_type='reflective'), - openmc.YPlane(1e10, boundary_type='reflective')] + yplanes = [ + openmc.YPlane(-1e10, boundary_type="reflective"), + openmc.YPlane(1e10, boundary_type="reflective"), + ] else: - yplanes = [openmc.YPlane(self.lower_left[1], boundary_type=bc[2]), - openmc.YPlane(self.upper_right[1], boundary_type=bc[3])] + yplanes = [ + openmc.YPlane(self.lower_left[1], boundary_type=bc[2]), + openmc.YPlane(self.upper_right[1], boundary_type=bc[3]), + ] if n_dim <= 2: # Would prefer to have the z ranges be the max supported float, but @@ -1014,15 +1025,21 @@ def build_cells(self, bc: str | None = None): # inconsistency between what numpy uses as the max float and what # Fortran expects for a real(8), so this avoids code complication # and achieves the same goal. - zplanes = [openmc.ZPlane(-1e10, boundary_type='reflective'), - openmc.ZPlane(1e10, boundary_type='reflective')] + zplanes = [ + openmc.ZPlane(-1e10, boundary_type="reflective"), + openmc.ZPlane(1e10, boundary_type="reflective"), + ] else: - zplanes = [openmc.ZPlane(self.lower_left[2], boundary_type=bc[4]), - openmc.ZPlane(self.upper_right[2], boundary_type=bc[5])] + zplanes = [ + openmc.ZPlane(self.lower_left[2], boundary_type=bc[4]), + openmc.ZPlane(self.upper_right[2], boundary_type=bc[5]), + ] root_cell = openmc.Cell() - root_cell.region = ((+xplanes[0] & -xplanes[1]) & - (+yplanes[0] & -yplanes[1]) & - (+zplanes[0] & -zplanes[1])) + root_cell.region = ( + (+xplanes[0] & -xplanes[1]) + & (+yplanes[0] & -yplanes[1]) + & (+zplanes[0] & -zplanes[1]) + ) # Build the universes which will be used for each of the (i,j,k) # locations within the mesh. @@ -1042,16 +1059,14 @@ def build_cells(self, bc: str | None = None): if n_dim == 1: universe_array = np.array([universes]) elif n_dim == 2: - universe_array = np.empty(self.dimension[::-1], - dtype=openmc.Universe) + universe_array = np.empty(self.dimension[::-1], dtype=openmc.Universe) i = 0 for y in range(self.dimension[1] - 1, -1, -1): for x in range(self.dimension[0]): universe_array[y][x] = universes[i] i += 1 else: - universe_array = np.empty(self.dimension[::-1], - dtype=openmc.Universe) + universe_array = np.empty(self.dimension[::-1], dtype=openmc.Universe) i = 0 for z in range(self.dimension[2]): for y in range(self.dimension[1] - 1, -1, -1): @@ -1063,20 +1078,16 @@ def build_cells(self, bc: str | None = None): if self.width is not None: lattice.pitch = self.width else: - dx = ((self.upper_right[0] - self.lower_left[0]) / - self.dimension[0]) + dx = (self.upper_right[0] - self.lower_left[0]) / self.dimension[0] if n_dim == 1: lattice.pitch = [dx] elif n_dim == 2: - dy = ((self.upper_right[1] - self.lower_left[1]) / - self.dimension[1]) + dy = (self.upper_right[1] - self.lower_left[1]) / self.dimension[1] lattice.pitch = [dx, dy] else: - dy = ((self.upper_right[1] - self.lower_left[1]) / - self.dimension[1]) - dz = ((self.upper_right[2] - self.lower_left[2]) / - self.dimension[2]) + dy = (self.upper_right[1] - self.lower_left[1]) / self.dimension[1] + dz = (self.upper_right[2] - self.lower_left[2]) / self.dimension[2] lattice.pitch = [dx, dy, dz] # Fill Cell with the Lattice @@ -1086,8 +1097,10 @@ def build_cells(self, bc: str | None = None): def Mesh(*args, **kwargs): - warnings.warn("Mesh has been renamed RegularMesh. Future versions of " - "OpenMC will not accept the name Mesh.") + warnings.warn( + "Mesh has been renamed RegularMesh. Future versions of " + "OpenMC will not accept the name Mesh." + ) return RegularMesh(*args, **kwargs) @@ -1126,7 +1139,7 @@ class RectilinearMesh(StructuredMesh): """ - def __init__(self, mesh_id: int = None, name: str = ''): + def __init__(self, mesh_id: int = None, name: str = ""): super().__init__(mesh_id, name) self._x_grid = None @@ -1135,9 +1148,7 @@ def __init__(self, mesh_id: int = None, name: str = ''): @property def dimension(self): - return (len(self.x_grid) - 1, - len(self.y_grid) - 1, - len(self.z_grid) - 1) + return (len(self.x_grid) - 1, len(self.y_grid) - 1, len(self.z_grid) - 1) @property def n_dimension(self): @@ -1149,7 +1160,7 @@ def x_grid(self): @x_grid.setter def x_grid(self, grid): - cv.check_type('mesh x_grid', grid, Iterable, Real) + cv.check_type("mesh x_grid", grid, Iterable, Real) self._x_grid = np.asarray(grid, dtype=float) @property @@ -1158,7 +1169,7 @@ def y_grid(self): @y_grid.setter def y_grid(self, grid): - cv.check_type('mesh y_grid', grid, Iterable, Real) + cv.check_type("mesh y_grid", grid, Iterable, Real) self._y_grid = np.asarray(grid, dtype=float) @property @@ -1167,7 +1178,7 @@ def z_grid(self): @z_grid.setter def z_grid(self, grid): - cv.check_type('mesh z_grid', grid, Iterable, Real) + cv.check_type("mesh z_grid", grid, Iterable, Real) self._z_grid = np.asarray(grid, dtype=float) @property @@ -1208,41 +1219,43 @@ def indices(self): nx = len(self.x_grid) - 1 ny = len(self.y_grid) - 1 nz = len(self.z_grid) - 1 - return ((x, y, z) - for z in range(1, nz + 1) - for y in range(1, ny + 1) - for x in range(1, nx + 1)) + return ( + (x, y, z) + for z in range(1, nz + 1) + for y in range(1, ny + 1) + for x in range(1, nx + 1) + ) def __repr__(self): - fmt = '{0: <16}{1}{2}\n' + fmt = "{0: <16}{1}{2}\n" string = super().__repr__() - string += fmt.format('\tDimensions', '=\t', self.n_dimension) + string += fmt.format("\tDimensions", "=\t", self.n_dimension) x_grid_str = str(self._x_grid) if self._x_grid is None else len(self._x_grid) - string += fmt.format('\tN X pnts:', '=\t', x_grid_str) + string += fmt.format("\tN X pnts:", "=\t", x_grid_str) if self._x_grid is not None: - string += fmt.format('\tX Min:', '=\t', self._x_grid[0]) - string += fmt.format('\tX Max:', '=\t', self._x_grid[-1]) + string += fmt.format("\tX Min:", "=\t", self._x_grid[0]) + string += fmt.format("\tX Max:", "=\t", self._x_grid[-1]) y_grid_str = str(self._y_grid) if self._y_grid is None else len(self._y_grid) - string += fmt.format('\tN Y pnts:', '=\t', y_grid_str) + string += fmt.format("\tN Y pnts:", "=\t", y_grid_str) if self._y_grid is not None: - string += fmt.format('\tY Min:', '=\t', self._y_grid[0]) - string += fmt.format('\tY Max:', '=\t', self._y_grid[-1]) + string += fmt.format("\tY Min:", "=\t", self._y_grid[0]) + string += fmt.format("\tY Max:", "=\t", self._y_grid[-1]) z_grid_str = str(self._z_grid) if self._z_grid is None else len(self._z_grid) - string += fmt.format('\tN Z pnts:', '=\t', z_grid_str) + string += fmt.format("\tN Z pnts:", "=\t", z_grid_str) if self._z_grid is not None: - string += fmt.format('\tZ Min:', '=\t', self._z_grid[0]) - string += fmt.format('\tZ Max:', '=\t', self._z_grid[-1]) + string += fmt.format("\tZ Min:", "=\t", self._z_grid[0]) + string += fmt.format("\tZ Max:", "=\t", self._z_grid[-1]) return string @classmethod def from_hdf5(cls, group: h5py.Group): - mesh_id = int(group.name.split('/')[-1].lstrip('mesh ')) + mesh_id = int(group.name.split("/")[-1].lstrip("mesh ")) # Read and assign mesh properties mesh = cls(mesh_id=mesh_id) - mesh.x_grid = group['x_grid'][()] - mesh.y_grid = group['y_grid'][()] - mesh.z_grid = group['z_grid'][()] + mesh.x_grid = group["x_grid"][()] + mesh.y_grid = group["y_grid"][()] + mesh.z_grid = group["z_grid"][()] return mesh @@ -1261,11 +1274,11 @@ def from_xml_element(cls, elem: ET.Element): Rectilinear mesh object """ - mesh_id = int(get_text(elem, 'id')) + mesh_id = int(get_text(elem, "id")) mesh = cls(mesh_id=mesh_id) - mesh.x_grid = [float(x) for x in get_text(elem, 'x_grid').split()] - mesh.y_grid = [float(y) for y in get_text(elem, 'y_grid').split()] - mesh.z_grid = [float(z) for z in get_text(elem, 'z_grid').split()] + mesh.x_grid = [float(x) for x in get_text(elem, "x_grid").split()] + mesh.y_grid = [float(y) for y in get_text(elem, "y_grid").split()] + mesh.z_grid = [float(z) for z in get_text(elem, "z_grid").split()] return mesh @@ -1284,13 +1297,13 @@ def to_xml_element(self): element.set("type", "rectilinear") subelement = ET.SubElement(element, "x_grid") - subelement.text = ' '.join(map(str, self.x_grid)) + subelement.text = " ".join(map(str, self.x_grid)) subelement = ET.SubElement(element, "y_grid") - subelement.text = ' '.join(map(str, self.y_grid)) + subelement.text = " ".join(map(str, self.y_grid)) subelement = ET.SubElement(element, "z_grid") - subelement.text = ' '.join(map(str, self.z_grid)) + subelement.text = " ".join(map(str, self.z_grid)) return element @@ -1358,10 +1371,10 @@ def __init__( self, r_grid: Sequence[float], z_grid: Sequence[float], - phi_grid: Sequence[float] = (0, 2*pi), - origin: Sequence[float] = (0., 0., 0.), + phi_grid: Sequence[float] = (0, 2 * pi), + origin: Sequence[float] = (0.0, 0.0, 0.0), mesh_id: int | None = None, - name: str = '', + name: str = "", ): super().__init__(mesh_id, name) @@ -1372,9 +1385,7 @@ def __init__( @property def dimension(self): - return (len(self.r_grid) - 1, - len(self.phi_grid) - 1, - len(self.z_grid) - 1) + return (len(self.r_grid) - 1, len(self.phi_grid) - 1, len(self.z_grid) - 1) @property def n_dimension(self): @@ -1386,7 +1397,7 @@ def origin(self): @origin.setter def origin(self, coords): - cv.check_type('mesh origin', coords, Iterable, Real) + cv.check_type("mesh origin", coords, Iterable, Real) cv.check_length("mesh origin", coords, 3) self._origin = np.asarray(coords) @@ -1396,9 +1407,9 @@ def r_grid(self): @r_grid.setter def r_grid(self, grid): - cv.check_type('mesh r_grid', grid, Iterable, Real) - cv.check_length('mesh r_grid', grid, 2) - cv.check_increasing('mesh r_grid', grid) + cv.check_type("mesh r_grid", grid, Iterable, Real) + cv.check_length("mesh r_grid", grid, 2) + cv.check_increasing("mesh r_grid", grid) self._r_grid = np.asarray(grid, dtype=float) @property @@ -1407,11 +1418,11 @@ def phi_grid(self): @phi_grid.setter def phi_grid(self, grid): - cv.check_type('mesh phi_grid', grid, Iterable, Real) - cv.check_length('mesh phi_grid', grid, 2) - cv.check_increasing('mesh phi_grid', grid) + cv.check_type("mesh phi_grid", grid, Iterable, Real) + cv.check_length("mesh phi_grid", grid, 2) + cv.check_increasing("mesh phi_grid", grid) grid = np.asarray(grid, dtype=float) - if np.any((grid < 0.0) | (grid > 2*pi)): + if np.any((grid < 0.0) | (grid > 2 * pi)): raise ValueError("phi_grid values must be in [0, 2Ļ€].") self._phi_grid = grid @@ -1421,9 +1432,9 @@ def z_grid(self): @z_grid.setter def z_grid(self, grid): - cv.check_type('mesh z_grid', grid, Iterable, Real) - cv.check_length('mesh z_grid', grid, 2) - cv.check_increasing('mesh z_grid', grid) + cv.check_type("mesh z_grid", grid, Iterable, Real) + cv.check_length("mesh z_grid", grid, 2) + cv.check_increasing("mesh z_grid", grid) self._z_grid = np.asarray(grid, dtype=float) @property @@ -1435,53 +1446,58 @@ def indices(self): nr, np, nz = self.dimension np = len(self.phi_grid) - 1 nz = len(self.z_grid) - 1 - return ((r, p, z) - for z in range(1, nz + 1) - for p in range(1, np + 1) - for r in range(1, nr + 1)) + return ( + (r, p, z) + for z in range(1, nz + 1) + for p in range(1, np + 1) + for r in range(1, nr + 1) + ) @property def lower_left(self): - return np.array(( - self.origin[0] - self.r_grid[-1], - self.origin[1] - self.r_grid[-1], - self.origin[2] + self.z_grid[0] - )) + return np.array( + ( + self.origin[0] - self.r_grid[-1], + self.origin[1] - self.r_grid[-1], + self.origin[2] + self.z_grid[0], + ) + ) @property def upper_right(self): - return np.array(( - self.origin[0] + self.r_grid[-1], - self.origin[1] + self.r_grid[-1], - self.origin[2] + self.z_grid[-1] - )) + return np.array( + ( + self.origin[0] + self.r_grid[-1], + self.origin[1] + self.r_grid[-1], + self.origin[2] + self.z_grid[-1], + ) + ) def __repr__(self): - fmt = '{0: <16}{1}{2}\n' + fmt = "{0: <16}{1}{2}\n" string = super().__repr__() - string += fmt.format('\tDimensions', '=\t', self.n_dimension) - string += fmt.format('\tOrigin', '=\t', self.origin) + string += fmt.format("\tDimensions", "=\t", self.n_dimension) + string += fmt.format("\tOrigin", "=\t", self.origin) r_grid_str = str(self._r_grid) if self._r_grid is None else len(self._r_grid) - string += fmt.format('\tN R pnts:', '=\t', r_grid_str) + string += fmt.format("\tN R pnts:", "=\t", r_grid_str) if self._r_grid is not None: - string += fmt.format('\tR Min:', '=\t', self._r_grid[0]) - string += fmt.format('\tR Max:', '=\t', self._r_grid[-1]) - phi_grid_str = str(self._phi_grid) if self._phi_grid is None else len(self._phi_grid) - string += fmt.format('\tN Phi pnts:', '=\t', phi_grid_str) + string += fmt.format("\tR Min:", "=\t", self._r_grid[0]) + string += fmt.format("\tR Max:", "=\t", self._r_grid[-1]) + phi_grid_str = ( + str(self._phi_grid) if self._phi_grid is None else len(self._phi_grid) + ) + string += fmt.format("\tN Phi pnts:", "=\t", phi_grid_str) if self._phi_grid is not None: - string += fmt.format('\tPhi Min:', '=\t', self._phi_grid[0]) - string += fmt.format('\tPhi Max:', '=\t', self._phi_grid[-1]) + string += fmt.format("\tPhi Min:", "=\t", self._phi_grid[0]) + string += fmt.format("\tPhi Max:", "=\t", self._phi_grid[-1]) z_grid_str = str(self._z_grid) if self._z_grid is None else len(self._z_grid) - string += fmt.format('\tN Z pnts:', '=\t', z_grid_str) + string += fmt.format("\tN Z pnts:", "=\t", z_grid_str) if self._z_grid is not None: - string += fmt.format('\tZ Min:', '=\t', self._z_grid[0]) - string += fmt.format('\tZ Max:', '=\t', self._z_grid[-1]) + string += fmt.format("\tZ Min:", "=\t", self._z_grid[0]) + string += fmt.format("\tZ Max:", "=\t", self._z_grid[-1]) return string - def get_indices_at_coords( - self, - coords: Sequence[float] - ) -> tuple[int, int, int]: + def get_indices_at_coords(self, coords: Sequence[float]) -> tuple[int, int, int]: """Finds the index of the mesh voxel at the specified x,y,z coordinates. .. versionadded:: 0.15.0 @@ -1497,13 +1513,18 @@ def get_indices_at_coords( The r, phi, z indices """ - r_value_from_origin = sqrt((coords[0]-self.origin[0])**2 + (coords[1]-self.origin[1])**2) + r_value_from_origin = sqrt( + (coords[0] - self.origin[0]) ** 2 + (coords[1] - self.origin[1]) ** 2 + ) - if r_value_from_origin < self.r_grid[0] or r_value_from_origin > self.r_grid[-1]: + if ( + r_value_from_origin < self.r_grid[0] + or r_value_from_origin > self.r_grid[-1] + ): raise ValueError( - f'The specified x, y ({coords[0]}, {coords[1]}) combine to give an r value of ' - f'{r_value_from_origin} from the origin of {self.origin}.which ' - f'is outside the origin absolute r grid values {self.r_grid}.' + f"The specified x, y ({coords[0]}, {coords[1]}) combine to give an r value of " + f"{r_value_from_origin} from the origin of {self.origin}.which " + f"is outside the origin absolute r grid values {self.r_grid}." ) r_index = np.searchsorted(self.r_grid, r_value_from_origin) - 1 @@ -1512,8 +1533,8 @@ def get_indices_at_coords( if coords[2] < z_grid_values[0] or coords[2] > z_grid_values[-1]: raise ValueError( - f'The specified z value ({coords[2]}) from the z origin of ' - f'{self.origin[-1]} is outside of the absolute z grid range {z_grid_values}.' + f"The specified z value ({coords[2]}) from the z origin of " + f"{self.origin[-1]} is outside of the absolute z grid range {z_grid_values}." ) z_index = np.argmax(z_grid_values > coords[2]) - 1 @@ -1533,8 +1554,8 @@ def get_indices_at_coords( if phi_value < phi_grid_values[0] or phi_value > phi_grid_values[-1]: raise ValueError( - f'The phi value ({phi_value}) resulting from the specified x, y ' - f'values is outside of the absolute phi grid range {phi_grid_values}.' + f"The phi value ({phi_value}) resulting from the specified x, y " + f"values is outside of the absolute phi grid range {phi_grid_values}." ) phi_index = np.argmax(phi_grid_values > phi_value) - 1 @@ -1542,28 +1563,28 @@ def get_indices_at_coords( @classmethod def from_hdf5(cls, group: h5py.Group): - mesh_id = int(group.name.split('/')[-1].lstrip('mesh ')) + mesh_id = int(group.name.split("/")[-1].lstrip("mesh ")) # Read and assign mesh properties mesh = cls( mesh_id=mesh_id, - r_grid = group['r_grid'][()], - phi_grid = group['phi_grid'][()], - z_grid = group['z_grid'][()], + r_grid=group["r_grid"][()], + phi_grid=group["phi_grid"][()], + z_grid=group["z_grid"][()], ) - if 'origin' in group: - mesh.origin = group['origin'][()] + if "origin" in group: + mesh.origin = group["origin"][()] return mesh @classmethod def from_domain( cls, - domain: 'openmc.Cell' | 'openmc.Region' | 'openmc.Universe' | 'openmc.Geometry', + domain: "openmc.Cell" | "openmc.Region" | "openmc.Universe" | "openmc.Geometry", dimension: Sequence[int] = (10, 10, 10), mesh_id: int | None = None, - phi_grid_bounds: Sequence[float] = (0.0, 2*pi), - name: str = '' + phi_grid_bounds: Sequence[float] = (0.0, 2 * pi), + name: str = "", ): """Creates a regular CylindricalMesh from an existing openmc domain. @@ -1606,21 +1627,11 @@ def from_domain( cached_bb[1][1], ] ) - r_grid = np.linspace( - 0, - max_bounding_box_radius, - num=dimension[0]+1 - ) + r_grid = np.linspace(0, max_bounding_box_radius, num=dimension[0] + 1) phi_grid = np.linspace( - phi_grid_bounds[0], - phi_grid_bounds[1], - num=dimension[1]+1 - ) - z_grid = np.linspace( - cached_bb[0][2], - cached_bb[1][2], - num=dimension[2]+1 + phi_grid_bounds[0], phi_grid_bounds[1], num=dimension[1] + 1 ) + z_grid = np.linspace(cached_bb[0][2], cached_bb[1][2], num=dimension[2] + 1) origin = (cached_bb.center[0], cached_bb.center[1], z_grid[0]) # make z-grid relative to the origin @@ -1632,7 +1643,7 @@ def from_domain( phi_grid=phi_grid, mesh_id=mesh_id, name=name, - origin=origin + origin=origin, ) return mesh @@ -1652,16 +1663,16 @@ def to_xml_element(self): element.set("type", "cylindrical") subelement = ET.SubElement(element, "r_grid") - subelement.text = ' '.join(map(str, self.r_grid)) + subelement.text = " ".join(map(str, self.r_grid)) subelement = ET.SubElement(element, "phi_grid") - subelement.text = ' '.join(map(str, self.phi_grid)) + subelement.text = " ".join(map(str, self.phi_grid)) subelement = ET.SubElement(element, "z_grid") - subelement.text = ' '.join(map(str, self.z_grid)) + subelement.text = " ".join(map(str, self.z_grid)) subelement = ET.SubElement(element, "origin") - subelement.text = ' '.join(map(str, self.origin)) + subelement.text = " ".join(map(str, self.origin)) return element @@ -1681,12 +1692,15 @@ def from_xml_element(cls, elem: ET.Element): """ - mesh_id = int(get_text(elem, 'id')) + mesh_id = int(get_text(elem, "id")) mesh = cls( - r_grid = [float(x) for x in get_text(elem, "r_grid").split()], - phi_grid = [float(x) for x in get_text(elem, "phi_grid").split()], - z_grid = [float(x) for x in get_text(elem, "z_grid").split()], - origin = [float(x) for x in get_text(elem, "origin", default=[0., 0., 0.]).split()], + r_grid=[float(x) for x in get_text(elem, "r_grid").split()], + phi_grid=[float(x) for x in get_text(elem, "phi_grid").split()], + z_grid=[float(x) for x in get_text(elem, "z_grid").split()], + origin=[ + float(x) + for x in get_text(elem, "origin", default=[0.0, 0.0, 0.0]).split() + ], mesh_id=mesh_id, ) @@ -1703,7 +1717,7 @@ def volumes(self): """ self._volume_dim_check() - V_r = np.diff(np.asarray(self.r_grid)**2 / 2) + V_r = np.diff(np.asarray(self.r_grid) ** 2 / 2) V_p = np.diff(self.phi_grid) V_z = np.diff(self.z_grid) @@ -1711,24 +1725,26 @@ def volumes(self): @property def vertices(self): - warnings.warn('Cartesian coordinates are returned from this property as of version 0.14.0') + warnings.warn( + "Cartesian coordinates are returned from this property as of version 0.14.0" + ) return self._convert_to_cartesian(self.vertices_cylindrical, self.origin) @property def vertices_cylindrical(self): - """Returns vertices of the mesh in cylindrical coordinates. - """ + """Returns vertices of the mesh in cylindrical coordinates.""" return super().vertices @property def centroids(self): - warnings.warn('Cartesian coordinates are returned from this property as of version 0.14.0') + warnings.warn( + "Cartesian coordinates are returned from this property as of version 0.14.0" + ) return self._convert_to_cartesian(self.centroids_cylindrical, self.origin) @property def centroids_cylindrical(self): - """Returns centroids of the mesh in cylindrical coordinates. - """ + """Returns centroids of the mesh in cylindrical coordinates.""" return super().centroids @staticmethod @@ -1807,11 +1823,11 @@ class SphericalMesh(StructuredMesh): def __init__( self, r_grid: Sequence[float], - phi_grid: Sequence[float] = (0, 2*pi), + phi_grid: Sequence[float] = (0, 2 * pi), theta_grid: Sequence[float] = (0, pi), - origin: Sequence[float] = (0., 0., 0.), + origin: Sequence[float] = (0.0, 0.0, 0.0), mesh_id: int | None = None, - name: str = '', + name: str = "", ): super().__init__(mesh_id, name) @@ -1822,9 +1838,7 @@ def __init__( @property def dimension(self): - return (len(self.r_grid) - 1, - len(self.theta_grid) - 1, - len(self.phi_grid) - 1) + return (len(self.r_grid) - 1, len(self.theta_grid) - 1, len(self.phi_grid) - 1) @property def n_dimension(self): @@ -1836,7 +1850,7 @@ def origin(self): @origin.setter def origin(self, coords): - cv.check_type('mesh origin', coords, Iterable, Real) + cv.check_type("mesh origin", coords, Iterable, Real) cv.check_length("mesh origin", coords, 3) self._origin = np.asarray(coords, dtype=float) @@ -1846,9 +1860,9 @@ def r_grid(self): @r_grid.setter def r_grid(self, grid): - cv.check_type('mesh r_grid', grid, Iterable, Real) - cv.check_length('mesh r_grid', grid, 2) - cv.check_increasing('mesh r_grid', grid) + cv.check_type("mesh r_grid", grid, Iterable, Real) + cv.check_length("mesh r_grid", grid, 2) + cv.check_increasing("mesh r_grid", grid) self._r_grid = np.asarray(grid, dtype=float) @property @@ -1857,9 +1871,9 @@ def theta_grid(self): @theta_grid.setter def theta_grid(self, grid): - cv.check_type('mesh theta_grid', grid, Iterable, Real) - cv.check_length('mesh theta_grid', grid, 2) - cv.check_increasing('mesh theta_grid', grid) + cv.check_type("mesh theta_grid", grid, Iterable, Real) + cv.check_length("mesh theta_grid", grid, 2) + cv.check_increasing("mesh theta_grid", grid) grid = np.asarray(grid, dtype=float) if np.any((grid < 0.0) | (grid > pi)): raise ValueError("theta_grid values must be in [0, Ļ€].") @@ -1871,11 +1885,11 @@ def phi_grid(self): @phi_grid.setter def phi_grid(self, grid): - cv.check_type('mesh phi_grid', grid, Iterable, Real) - cv.check_length('mesh phi_grid', grid, 2) - cv.check_increasing('mesh phi_grid', grid) + cv.check_type("mesh phi_grid", grid, Iterable, Real) + cv.check_length("mesh phi_grid", grid, 2) + cv.check_increasing("mesh phi_grid", grid) grid = np.asarray(grid, dtype=float) - if np.any((grid < 0.0) | (grid > 2*pi)): + if np.any((grid < 0.0) | (grid > 2 * pi)): raise ValueError("phi_grid values must be in [0, 2Ļ€].") self._phi_grid = grid @@ -1888,10 +1902,12 @@ def indices(self): nr, nt, np = self.dimension nt = len(self.theta_grid) - 1 np = len(self.phi_grid) - 1 - return ((r, t, p) - for p in range(1, np + 1) - for t in range(1, nt + 1) - for r in range(1, nr + 1)) + return ( + (r, t, p) + for p in range(1, np + 1) + for t in range(1, nt + 1) + for r in range(1, nr + 1) + ) @property def lower_left(self): @@ -1904,40 +1920,44 @@ def upper_right(self): return np.array((self.origin[0] + r, self.origin[1] + r, self.origin[2] + r)) def __repr__(self): - fmt = '{0: <16}{1}{2}\n' + fmt = "{0: <16}{1}{2}\n" string = super().__repr__() - string += fmt.format('\tDimensions', '=\t', self.n_dimension) - string += fmt.format('\tOrigin', '=\t', self.origin) + string += fmt.format("\tDimensions", "=\t", self.n_dimension) + string += fmt.format("\tOrigin", "=\t", self.origin) r_grid_str = str(self._r_grid) if self._r_grid is None else len(self._r_grid) - string += fmt.format('\tN R pnts:', '=\t', r_grid_str) + string += fmt.format("\tN R pnts:", "=\t", r_grid_str) if self._r_grid is not None: - string += fmt.format('\tR Min:', '=\t', self._r_grid[0]) - string += fmt.format('\tR Max:', '=\t', self._r_grid[-1]) - theta_grid_str = str(self._theta_grid) if self._theta_grid is None else len(self._theta_grid) - string += fmt.format('\tN Theta pnts:', '=\t', theta_grid_str) + string += fmt.format("\tR Min:", "=\t", self._r_grid[0]) + string += fmt.format("\tR Max:", "=\t", self._r_grid[-1]) + theta_grid_str = ( + str(self._theta_grid) if self._theta_grid is None else len(self._theta_grid) + ) + string += fmt.format("\tN Theta pnts:", "=\t", theta_grid_str) if self._theta_grid is not None: - string += fmt.format('\tTheta Min:', '=\t', self._theta_grid[0]) - string += fmt.format('\tTheta Max:', '=\t', self._theta_grid[-1]) - phi_grid_str = str(self._phi_grid) if self._phi_grid is None else len(self._phi_grid) - string += fmt.format('\tN Phi pnts:', '=\t', phi_grid_str) + string += fmt.format("\tTheta Min:", "=\t", self._theta_grid[0]) + string += fmt.format("\tTheta Max:", "=\t", self._theta_grid[-1]) + phi_grid_str = ( + str(self._phi_grid) if self._phi_grid is None else len(self._phi_grid) + ) + string += fmt.format("\tN Phi pnts:", "=\t", phi_grid_str) if self._phi_grid is not None: - string += fmt.format('\tPhi Min:', '=\t', self._phi_grid[0]) - string += fmt.format('\tPhi Max:', '=\t', self._phi_grid[-1]) + string += fmt.format("\tPhi Min:", "=\t", self._phi_grid[0]) + string += fmt.format("\tPhi Max:", "=\t", self._phi_grid[-1]) return string @classmethod def from_hdf5(cls, group: h5py.Group): - mesh_id = int(group.name.split('/')[-1].lstrip('mesh ')) + mesh_id = int(group.name.split("/")[-1].lstrip("mesh ")) # Read and assign mesh properties mesh = cls( - r_grid = group['r_grid'][()], - theta_grid = group['theta_grid'][()], - phi_grid = group['phi_grid'][()], + r_grid=group["r_grid"][()], + theta_grid=group["theta_grid"][()], + phi_grid=group["phi_grid"][()], mesh_id=mesh_id, ) - if 'origin' in group: - mesh.origin = group['origin'][()] + if "origin" in group: + mesh.origin = group["origin"][()] return mesh @@ -1956,16 +1976,16 @@ def to_xml_element(self): element.set("type", "spherical") subelement = ET.SubElement(element, "r_grid") - subelement.text = ' '.join(map(str, self.r_grid)) + subelement.text = " ".join(map(str, self.r_grid)) subelement = ET.SubElement(element, "theta_grid") - subelement.text = ' '.join(map(str, self.theta_grid)) + subelement.text = " ".join(map(str, self.theta_grid)) subelement = ET.SubElement(element, "phi_grid") - subelement.text = ' '.join(map(str, self.phi_grid)) + subelement.text = " ".join(map(str, self.phi_grid)) subelement = ET.SubElement(element, "origin") - subelement.text = ' '.join(map(str, self.origin)) + subelement.text = " ".join(map(str, self.origin)) return element @@ -1984,13 +2004,16 @@ def from_xml_element(cls, elem: ET.Element): Spherical mesh object """ - mesh_id = int(get_text(elem, 'id')) + mesh_id = int(get_text(elem, "id")) mesh = cls( mesh_id=mesh_id, - r_grid = [float(x) for x in get_text(elem, "r_grid").split()], - theta_grid = [float(x) for x in get_text(elem, "theta_grid").split()], - phi_grid = [float(x) for x in get_text(elem, "phi_grid").split()], - origin = [float(x) for x in get_text(elem, "origin", default=[0., 0., 0.]).split()], + r_grid=[float(x) for x in get_text(elem, "r_grid").split()], + theta_grid=[float(x) for x in get_text(elem, "theta_grid").split()], + phi_grid=[float(x) for x in get_text(elem, "phi_grid").split()], + origin=[ + float(x) + for x in get_text(elem, "origin", default=[0.0, 0.0, 0.0]).split() + ], ) return mesh @@ -2006,7 +2029,7 @@ def volumes(self): """ self._volume_dim_check() - V_r = np.diff(np.asarray(self.r_grid)**3 / 3) + V_r = np.diff(np.asarray(self.r_grid) ** 3 / 3) V_t = np.diff(-np.cos(self.theta_grid)) V_p = np.diff(self.phi_grid) @@ -2014,27 +2037,28 @@ def volumes(self): @property def vertices(self): - warnings.warn('Cartesian coordinates are returned from this property as of version 0.14.0') + warnings.warn( + "Cartesian coordinates are returned from this property as of version 0.14.0" + ) return self._convert_to_cartesian(self.vertices_spherical, self.origin) @property def vertices_spherical(self): - """Returns vertices of the mesh in cylindrical coordinates. - """ + """Returns vertices of the mesh in cylindrical coordinates.""" return super().vertices @property def centroids(self): - warnings.warn('Cartesian coordinates are returned from this property as of version 0.14.0') + warnings.warn( + "Cartesian coordinates are returned from this property as of version 0.14.0" + ) return self._convert_to_cartesian(self.centroids_spherical, self.origin) @property def centroids_spherical(self): - """Returns centroids of the mesh in cylindrical coordinates. - """ + """Returns centroids of the mesh in cylindrical coordinates.""" return super().centroids - @staticmethod def _convert_to_cartesian(arr, origin: Sequence[float]): """Converts an array with r, theta, phi values in the last dimension (shape (..., 3)) @@ -2054,10 +2078,13 @@ def require_statepoint_data(func): @wraps(func) def wrapper(self: UnstructuredMesh, *args, **kwargs): if not self._has_statepoint_data: - raise AttributeError(f'The "{func.__name__}" property requires ' - 'information about this mesh to be loaded ' - 'from a statepoint file.') + raise AttributeError( + f'The "{func.__name__}" property requires ' + "information about this mesh to be loaded " + "from a statepoint file." + ) return func(self, *args, **kwargs) + return wrapper @@ -2138,9 +2165,15 @@ class UnstructuredMesh(MeshBase): _LINEAR_TET = 0 _LINEAR_HEX = 1 - def __init__(self, filename: PathLike, library: str, mesh_id: int | None = None, - name: str = '', length_multiplier: float = 1.0, - options: str | None = None): + def __init__( + self, + filename: PathLike, + library: str, + mesh_id: int | None = None, + name: str = "", + length_multiplier: float = 1.0, + options: str | None = None, + ): super().__init__(mesh_id, name) self.filename = filename self._volumes = None @@ -2159,7 +2192,7 @@ def filename(self): @filename.setter def filename(self, filename): - cv.check_type('Unstructured Mesh filename', filename, PathLike) + cv.check_type("Unstructured Mesh filename", filename, PathLike) self._filename = input_path(filename) @property @@ -2168,7 +2201,7 @@ def library(self): @library.setter def library(self, lib: str): - cv.check_value('Unstructured mesh library', lib, ('moab', 'libmesh')) + cv.check_value("Unstructured mesh library", lib, ("moab", "libmesh")) self._library = lib @property @@ -2177,7 +2210,7 @@ def options(self) -> str | None: @options.setter def options(self, options: str | None): - cv.check_type('options', options, (str, type(None))) + cv.check_type("options", options, (str, type(None))) self._options = options @property @@ -2247,13 +2280,15 @@ def centroids(self): @require_statepoint_data def n_elements(self): if self._n_elements is None: - raise RuntimeError("No information about this mesh has " - "been loaded from a statepoint file.") + raise RuntimeError( + "No information about this mesh has " + "been loaded from a statepoint file." + ) return self._n_elements @n_elements.setter def n_elements(self, val: int): - cv.check_type('Number of elements', val, Integral) + cv.check_type("Number of elements", val, Integral) self._n_elements = val @property @@ -2262,9 +2297,7 @@ def length_multiplier(self): @length_multiplier.setter def length_multiplier(self, length_multiplier): - cv.check_type("Unstructured mesh length multiplier", - length_multiplier, - Real) + cv.check_type("Unstructured mesh length multiplier", length_multiplier, Real) self._length_multiplier = length_multiplier @property @@ -2286,13 +2319,14 @@ def has_statepoint_data(self) -> bool: def __repr__(self): string = super().__repr__() - string += '{: <16}=\t{}\n'.format('\tFilename', self.filename) - string += '{: <16}=\t{}\n'.format('\tMesh Library', self.library) + string += "{: <16}=\t{}\n".format("\tFilename", self.filename) + string += "{: <16}=\t{}\n".format("\tMesh Library", self.library) if self.length_multiplier != 1.0: - string += '{: <16}=\t{}\n'.format('\tLength multiplier', - self.length_multiplier) + string += "{: <16}=\t{}\n".format( + "\tLength multiplier", self.length_multiplier + ) if self.options is not None: - string += '{: <16}=\t{}\n'.format('\tOptions', self.options) + string += "{: <16}=\t{}\n".format("\tOptions", self.options) return string @property @@ -2346,15 +2380,16 @@ def write_vtk_mesh(self, **kwargs): warnings.warn( "The 'UnstructuredMesh.write_vtk_mesh' method has been renamed " "to 'write_data_to_vtk' and will be removed in a future version " - " of OpenMC.", FutureWarning + " of OpenMC.", + FutureWarning, ) self.write_data_to_vtk(**kwargs) def write_data_to_vtk( - self, - filename: PathLike | None = None, - datasets: dict | None = None, - volume_normalization: bool = True + self, + filename: PathLike | None = None, + datasets: dict | None = None, + volume_normalization: bool = True, ): """Map data to unstructured VTK mesh elements. @@ -2374,11 +2409,12 @@ def write_data_to_vtk( from vtk.util import numpy_support as nps if self.connectivity is None or self.vertices is None: - raise RuntimeError('This mesh has not been ' - 'loaded from a statepoint file.') + raise RuntimeError( + "This mesh has not been " "loaded from a statepoint file." + ) if filename is None: - filename = f'mesh_{self.id}.vtk' + filename = f"mesh_{self.id}.vtk" writer = vtk.vtkUnstructuredGridWriter() @@ -2399,7 +2435,7 @@ def write_data_to_vtk( elif elem_type == self._UNSUPPORTED_ELEM: n_skipped += 1 else: - raise RuntimeError(f'Invalid element type {elem_type} found') + raise RuntimeError(f"Invalid element type {elem_type} found") for i, c in enumerate(conn): if c == -1: break @@ -2408,23 +2444,29 @@ def write_data_to_vtk( grid.InsertNextCell(elem.GetCellType(), elem.GetPointIds()) if n_skipped > 0: - warnings.warn(f'{n_skipped} elements were not written because ' - 'they are not of type linear tet/hex') + warnings.warn( + f"{n_skipped} elements were not written because " + "they are not of type linear tet/hex" + ) # check that datasets are the correct size datasets_out = [] if datasets is not None: for name, data in datasets.items(): if data.shape != self.dimension: - raise ValueError(f'Cannot apply dataset "{name}" with ' - f'shape {data.shape} to mesh {self.id} ' - f'with dimensions {self.dimension}') + raise ValueError( + f'Cannot apply dataset "{name}" with ' + f"shape {data.shape} to mesh {self.id} " + f"with dimensions {self.dimension}" + ) if volume_normalization: for name, data in datasets.items(): if np.issubdtype(data.dtype, np.integer): - warnings.warn(f'Integer data set "{name}" will ' - 'not be volume-normalized.') + warnings.warn( + f'Integer data set "{name}" will ' + "not be volume-normalized." + ) continue data /= self.volumes @@ -2445,28 +2487,28 @@ def write_data_to_vtk( @classmethod def from_hdf5(cls, group: h5py.Group): - mesh_id = int(group.name.split('/')[-1].lstrip('mesh ')) - filename = group['filename'][()].decode() - library = group['library'][()].decode() - if 'options' in group.attrs: - options = group.attrs['options'].decode() + mesh_id = int(group.name.split("/")[-1].lstrip("mesh ")) + filename = group["filename"][()].decode() + library = group["library"][()].decode() + if "options" in group.attrs: + options = group.attrs["options"].decode() else: options = None mesh = cls(filename=filename, library=library, mesh_id=mesh_id, options=options) mesh._has_statepoint_data = True - vol_data = group['volumes'][()] + vol_data = group["volumes"][()] mesh.volumes = np.reshape(vol_data, (vol_data.shape[0],)) mesh.n_elements = mesh.volumes.size - vertices = group['vertices'][()] + vertices = group["vertices"][()] mesh._vertices = vertices.reshape((-1, 3)) - connectivity = group['connectivity'][()] + connectivity = group["connectivity"][()] mesh._connectivity = connectivity.reshape((-1, 8)) - mesh._element_types = group['element_types'][()] + mesh._element_types = group["element_types"][()] - if 'length_multiplier' in group: - mesh.length_multiplier = group['length_multiplier'][()] + if "length_multiplier" in group: + mesh.length_multiplier = group["length_multiplier"][()] return mesh @@ -2485,7 +2527,7 @@ def to_xml_element(self): element.set("type", "unstructured") element.set("library", self._library) if self.options is not None: - element.set('options', self.options) + element.set("options", self.options) subelement = ET.SubElement(element, "filename") subelement.text = str(self.filename) @@ -2508,13 +2550,13 @@ def from_xml_element(cls, elem: ET.Element): openmc.UnstructuredMesh UnstructuredMesh generated from an XML element """ - mesh_id = int(get_text(elem, 'id')) - filename = get_text(elem, 'filename') - library = get_text(elem, 'library') - length_multiplier = float(get_text(elem, 'length_multiplier', 1.0)) - options = elem.get('options') + mesh_id = int(get_text(elem, "id")) + filename = get_text(elem, "filename") + library = get_text(elem, "library") + length_multiplier = float(get_text(elem, "length_multiplier", 1.0)) + options = elem.get("options") - return cls(filename, library, mesh_id, '', length_multiplier, options) + return cls(filename, library, mesh_id, "", length_multiplier, options) def _read_meshes(elem): @@ -2532,7 +2574,7 @@ def _read_meshes(elem): instanaces as values """ out = {} - for mesh_elem in elem.findall('mesh'): + for mesh_elem in elem.findall("mesh"): mesh = MeshBase.from_xml_element(mesh_elem) out[mesh.id] = mesh @@ -2541,30 +2583,15 @@ def _read_meshes(elem): # hexahedron element connectivity # lower-k connectivity offsets -_HEX_VERTEX_CONN = ((0, 0, 0), - (1, 0, 0), - (1, 1, 0), - (0, 1, 0)) +_HEX_VERTEX_CONN = ((0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0)) # upper-k connectivity offsets -_HEX_VERTEX_CONN += ((0, 0, 1), - (1, 0, 1), - (1, 1, 1), - (0, 1, 1)) +_HEX_VERTEX_CONN += ((0, 0, 1), (1, 0, 1), (1, 1, 1), (0, 1, 1)) _N_HEX_VERTICES = 8 # lower-k connectivity offsets -_HEX_MIDPOINT_CONN = ((0, (0, 0, 0)), - (1, (1, 0, 0)), - (0, (0, 1, 0)), - (1, (0, 0, 0))) +_HEX_MIDPOINT_CONN = ((0, (0, 0, 0)), (1, (1, 0, 0)), (0, (0, 1, 0)), (1, (0, 0, 0))) # upper-k connectivity offsets -_HEX_MIDPOINT_CONN += ((0, (0, 0, 1)), - (1, (1, 0, 1)), - (0, (0, 1, 1)), - (1, (0, 0, 1))) +_HEX_MIDPOINT_CONN += ((0, (0, 0, 1)), (1, (1, 0, 1)), (0, (0, 1, 1)), (1, (0, 0, 1))) # mid-plane k connectivity -_HEX_MIDPOINT_CONN += ((2, (0, 0, 0)), - (2, (1, 0, 0)), - (2, (1, 1, 0)), - (2, (0, 1, 0))) +_HEX_MIDPOINT_CONN += ((2, (0, 0, 0)), (2, (1, 0, 0)), (2, (1, 1, 0)), (2, (0, 1, 0))) diff --git a/openmc/mgxs/__init__.py b/openmc/mgxs/__init__.py index 4f12a8fb42c..77eef1cc1e3 100644 --- a/openmc/mgxs/__init__.py +++ b/openmc/mgxs/__init__.py @@ -67,973 +67,5564 @@ Proceedings of International Conference on Physics of Reactors PHYSOR2020. """ -GROUP_STRUCTURES['CASMO-2'] = np.array([ - 0., 6.25e-1, 2.e7]) -GROUP_STRUCTURES['CASMO-4'] = np.array([ - 0., 6.25e-1, 5.53e3, 8.21e5, 2.e7]) -GROUP_STRUCTURES['CASMO-8'] = np.array([ - 0., 5.8e-2, 1.4e-1, 2.8e-1, 6.25e-1, 4., 5.53e3, 8.21e5, 2.e7]) -GROUP_STRUCTURES['CASMO-16'] = np.array([ - 0., 3.e-2, 5.8e-2, 1.4e-1, 2.8e-1, 3.5e-1, 6.25e-1, 8.5e-1, - 9.72e-1, 1.02, 1.097, 1.15, 1.3, 4., 5.53e3, 8.21e5, 2.e7]) -GROUP_STRUCTURES['CASMO-25'] = np.array([ - 0., 3.e-2, 5.8e-2, 1.4e-1, 2.8e-1, 3.5e-1, 6.25e-1, 9.72e-1, 1.02, 1.097, - 1.15, 1.855, 4., 9.877, 1.5968e1, 1.4873e2, 5.53e3, 9.118e3, 1.11e5, 5.e5, - 8.21e5, 1.353e6, 2.231e6, 3.679e6, 6.0655e6, 2.e7]) -GROUP_STRUCTURES['CASMO-40'] = np.array([ - 0., 1.5e-2, 3.e-2, 4.2e-2, 5.8e-2, 8.e-2, 1.e-1, 1.4e-1, - 1.8e-1, 2.2e-1, 2.8e-1, 3.5e-1, 6.25e-1, 8.5e-1, 9.5e-1, - 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['SCALE-44'] = np.array([1e-5, 3.e-3, 7.5e-3, 1.e-2, 2.53e-2, - 3.e-2, 4.e-2, 5e-2, 7.e-2, 1.e-1, 1.5e-1, 2.e-1, 2.25e-1, 2.5e-1, 2.75e-1, - 3.25e-1, 3.5e-1, 3.75e-1, 4.e-1, 6.25e-1, 1., 1.77, 3., 4.75, 6., 8.1, - 1.e1, 3.e1, 1.e2, 5.5e2, 3.e3, 1.7e4, 2.5e4, 1.e5, 4.e5, 9.e5, 1.4e6, - 1.85e6, 2.354e6, 2.479e6, 3.e6, 4.8e6, 6.434e6, 8.1873e6, 2.e7]) -GROUP_STRUCTURES['MPACT-51'] = np.array([ - 0., 1.e-2, 3.e-2, 4.e-2, 6.e-2, 8.e-2, 1.e-1, 1.5e-1, 2.e-1, 2.75e-1, - 3.5e-1, 5.e-1, 6.25e-1, 7.5e-1, 9.25e-1, 9.75e-1, 1.010, 1.080, 1.130, - 1.175, 1.250, 1.450, 1.860, 2.470, 3.730, 4.700, 5.000, 5.400, 6.250, - 7.150, 8.100, 1.19e+1, 1.44e+1, 3.e+1, 4.83e+1, 7.6e+1, 1.43e+2, 3.05e+2, - 9.5e+2, 2.25e+3, 9.5e+3, 2.e+4, 5.e+4, 7.3e+4, 2.e+5, 4.92e+5, 8.2e+5, - 1.356e+6, 2.354e+6, 4.304e+6, 6.434e+6, 2.e+7]) -GROUP_STRUCTURES['MPACT-60'] = np.array([ - 0., 1.e-2, 3.e-2, 4.e-2, 6.e-2, 8.e-2, 1.e-1, 1.5e-1, 2.e-1, 2.75e-1, - 3.5e-1, 5.e-1, 6.25e-1, 7.5e-1, 9.25e-1, 9.75e-1, 1.01, 1.08, 1.13, - 1.175, 1.25, 1.45, 1.86, 2.47, 3.73, 4.7, 5., 5.4, 6.25, 7.15, 8.1, - 1.19e+1, 1.44e+1, 3.e+1, 4.83e+1, 7.6e+1, 1.43e+2, 2.095e+2, 3.05e+2, - 6.7e+2, 9.5e+2, 1.55e+3, 2.25e+3, 3.9e+3, 9.5e+3, 1.3e+4, 2.e+4, 3.e+4, - 5.e+4, 7.3e+4, 1.283e+5, 2.e+5, 3.3e+5, 4.92e+5, 6.7e+5, 8.2e+5, 1.356e+6, - 2.354e+6, 4.304e+6, 6.434e+6, 2.e+7]) -GROUP_STRUCTURES['MPACT-69'] = np.array([ - 0., 1.e-2, 3.e-2, 4.e-2, 6.e-2, 8.e-2, 9.e-2, 1.e-1, 1.25e-1, 1.5e-1, - 1.75e-1, 2.e-1, 2.25e-1, 2.5e-1, 2.75e-1, 3.e-1, 3.25e-1, 3.5e-1, 3.75e-1, - 4.e-1, 4.5e-1, 5.e-1, 5.5e-1, 6.e-1, 6.25e-1, 6.5e-1, 7.5e-1, 8.5e-1, - 9.25e-1, 9.75e-1, 1.01, 1.08, 1.13, 1.175, 1.25, 1.45, 1.86, 2.47, 3., - 3.73, 4.7, 5., 5.4, 6.25, 7.15, 8.1, 1.e+1, 1.19e+1, 1.44e+1, 3.e+1, - 4.83e+1, 7.6e+1, 1.43e+2, 3.05e+2, 5.5e+2, 9.5e+2, 2.25e+3, 3.9e+3, 9.5e+3, - 2.e+4, 5.e+4, 7.3e+4, 2.e+5, 4.92e+5, 8.2e+5, 1.356e+6, 2.354e+6, 4.304e+6, - 6.434e+6, 2.e+7]) -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, - 2.5e-1, 2.8e-1, 3.e-1, 3.2e-1, 3.5e-1, 4.e-1, 5.e-1, 6.25e-1, - 7.8e-1, 8.5e-1, 9.1e-1, 9.5e-1, 9.72e-1, 9.96e-1, 1.02, 1.045, - 1.071, 1.097, 1.123, 1.15, 1.3, 1.5, 1.855, 2.1, 2.6, 3.3, 4., - 9.877, 1.5968e1, 2.77e1, 4.8052e1, 7.5501e1, 1.4873e2, - 3.6726e2, 9.069e2, 1.4251e3, 2.2395e3, 3.5191e3, 5.53e3, - 9.118e3, 1.503e4, 2.478e4, 4.085e4, 6.734e4, 1.11e5, 1.83e5, - 3.025e5, 5.e5, 8.21e5, 1.353e6, 2.231e6, 3.679e6, 6.0655e6, 2.e7]) -GROUP_STRUCTURES['XMAS-172'] = np.array([ - 1.00001e-05, 3.00000e-03, 5.00000e-03, 6.90000e-03, 1.00000e-02, - 1.50000e-02, 2.00000e-02, 2.50000e-02, 3.00000e-02, 3.50000e-02, - 4.20000e-02, 5.00000e-02, 5.80000e-02, 6.70000e-02, 7.70000e-02, - 8.00000e-02, 9.50000e-02, 1.00001e-01, 1.15000e-01, 1.34000e-01, - 1.40000e-01, 1.60000e-01, 1.80000e-01, 1.89000e-01, 2.20000e-01, - 2.48000e-01, 2.80000e-01, 3.00000e-01, 3.14500e-01, 3.20000e-01, - 3.50000e-01, 3.91000e-01, 4.00000e-01, 4.33000e-01, 4.85000e-01, - 5.00000e-01, 5.40000e-01, 6.25000e-01, 7.05000e-01, 7.80000e-01, - 7.90000e-01, 8.50000e-01, 8.60000e-01, 9.10000e-01, 9.30000e-01, - 9.50000e-01, 9.72000e-01, 9.86000e-01, 9.96000e-01, 1.02000e+00, - 1.03500e+00, 1.04500e+00, 1.07100e+00, 1.09700e+00, 1.11000e+00, - 1.12535e+00, 1.15000e+00, 1.17000e+00, 1.23500e+00, 1.30000e+00, - 1.33750e+00, 1.37000e+00, 1.44498e+00, 1.47500e+00, 1.50000e+00, - 1.59000e+00, 1.67000e+00, 1.75500e+00, 1.84000e+00, 1.93000e+00, - 2.02000e+00, 2.10000e+00, 2.13000e+00, 2.36000e+00, 2.55000e+00, - 2.60000e+00, 2.72000e+00, 2.76792e+00, 3.30000e+00, 3.38075e+00, - 4.00000e+00, 4.12925e+00, 5.04348e+00, 5.34643e+00, 6.16012e+00, - 7.52398e+00, 8.31529e+00, 9.18981e+00, 9.90555e+00, 1.12245e+01, - 1.37096e+01, 1.59283e+01, 1.94548e+01, 2.26033e+01, 2.49805e+01, - 2.76077e+01, 3.05113e+01, 3.37201e+01, 3.72665e+01, 4.01690e+01, - 4.55174e+01, 4.82516e+01, 5.15780e+01, 5.55951e+01, 6.79041e+01, - 7.56736e+01, 9.16609e+01, 1.36742e+02, 1.48625e+02, 2.03995e+02, - 3.04325e+02, 3.71703e+02, 4.53999e+02, 6.77287e+02, 7.48518e+02, - 9.14242e+02, 1.01039e+03, 1.23410e+03, 1.43382e+03, 1.50733e+03, - 2.03468e+03, 2.24867e+03, 3.35463e+03, 3.52662e+03, 5.00451e+03, - 5.53084e+03, 7.46586e+03, 9.11882e+03, 1.11378e+04, 1.50344e+04, - 1.66156e+04, 2.47875e+04, 2.73944e+04, 2.92830e+04, 3.69786e+04, - 4.08677e+04, 5.51656e+04, 6.73795e+04, 8.22975e+04, 1.11090e+05, - 1.22773e+05, 1.83156e+05, 2.47235e+05, 2.73237e+05, 3.01974e+05, - 4.07622e+05, 4.50492e+05, 4.97871e+05, 5.50232e+05, 6.08101e+05, - 8.20850e+05, 9.07180e+05, 1.00259e+06, 1.10803e+06, 1.22456e+06, - 1.35335e+06, 1.65299e+06, 2.01897e+06, 2.23130e+06, 2.46597e+06, - 3.01194e+06, 3.67879e+06, 4.49329e+06, 5.48812e+06, 6.06531e+06, - 6.70320e+06, 8.18731e+06, 1.00000e+07, 1.16183e+07, 1.38403e+07, - 1.49182e+07, 1.73325e+07, 1.96403e+07]) -GROUP_STRUCTURES['VITAMIN-J-175'] = np.array([ - 1.0000e-5, 1.0000e-1, 4.1399e-1, 5.3158e-1, 6.8256e-1, - 8.7643e-1, 1.1253, 1.4450, 1.8554, 2.3824, 3.0590, - 3.9279, 5.0435, 6.4759, 8.3153, 1.0677e1, 1.3710e1, - 1.7604e1, 2.2603e1, 2.9023e1, 3.7266e1, 4.7851e1, 6.1442e1, - 7.8893e1, 1.0130e2, 1.3007e2, 1.6702e2, 2.1445e2, 2.7536e2, - 3.5358e2, 4.5400e2, 5.8295e2, 7.4852e2, 9.6112e2, 1.2341e3, - 1.5846e3, 2.0347e3, 2.2487e3, 2.4852e3, 2.6126e3, 2.7465e3, - 3.0354e3, 3.3546e3, 3.7074e3, 4.3074e3, 5.5308e3, 7.1017e3, - 9.1188e3, 1.0595e4, 1.1709e4, 1.5034e4, 1.9304e4, 2.1875e4, - 2.3579e4, 2.4176e4, 2.4788e4, 2.6058e4, 2.7000e4, 2.8501e4, - 3.1828e4, 3.4307e4, 4.0868e4, 4.6309e4, 5.2475e4, 5.6562e4, - 6.7380e4, 7.2024e4, 7.9499e4, 8.2503e4, 8.6517e4, 9.8036e4, - 1.1109e5, 1.1679e5, 1.2277e5, 1.2907e5, 1.3569e5, 1.4264e5, - 1.4996e5, 1.5764e5, 1.6573e5, 1.7422e5, 1.8316e5, 1.9255e5, - 2.0242e5, 2.1280e5, 2.2371e5, 2.3518e5, 2.4724e5, 2.7324e5, - 2.8725e5, 2.9452e5, 2.9721e5, 2.9849e5, 3.0197e5, 3.3373e5, - 3.6883e5, 3.8774e5, 4.0762e5, 4.5049e5, 4.9787e5, 5.2340e5, - 5.5023e5, 5.7844e5, 6.0810e5, 6.3928e5, 6.7206e5, 7.0651e5, - 7.4274e5, 7.8082e5, 8.2085e5, 8.6294e5, 9.0718e5, 9.6167e5, - 1.0026e6, 1.1080e6, 1.1648e6, 1.2246e6, 1.2874e6, 1.3534e6, - 1.4227e6, 1.4957e6, 1.5724e6, 1.6530e6, 1.7377e6, 1.8268e6, - 1.9205e6, 2.0190e6, 2.1225e6, 2.2313e6, 2.3069e6, 2.3457e6, - 2.3653e6, 2.3851e6, 2.4660e6, 2.5924e6, 2.7253e6, 2.8650e6, - 3.0119e6, 3.1664e6, 3.3287e6, 3.6788e6, 4.0657e6, 4.4933e6, - 4.7237e6, 4.9658e6, 5.2205e6, 5.4881e6, 5.7695e6, 6.0653e6, - 6.3763e6, 6.5924e6, 6.7032e6, 7.0469e6, 7.4082e6, 7.7880e6, - 8.1873e6, 8.6071e6, 9.0484e6, 9.5123e6, 1.0000e7, 1.0513e7, - 1.1052e7, 1.1618e7, 1.2214e7, 1.2523e7, 1.2840e7, 1.3499e7, - 1.3840e7, 1.4191e7, 1.4550e7, 1.4918e7, 1.5683e7, 1.6487e7, - 1.6905e7, 1.7332e7, 1.9640e7]) -GROUP_STRUCTURES['SCALE-252'] = np.array([ - 0., 1.e-4, 5.e-4, 7.5e-4, 1.e-3, 1.2e-3, 1.5e-3, 2.e-3, 2.5e-3, 3.e-3, - 4.e-3, 5.e-3, 7.5e-3, 1.e-2, 2.53e-2, 3.e-2, 4.e-2, 5.e-2, 6.e-2, 7.e-2, - 8.e-2, 9.e-2, 1.e-1, 1.25e-1, 1.5e-1, 1.75e-1, 2.e-1, 2.25e-1, 2.5e-1, - 2.75e-1, 3.e-1, 3.25e-1, 3.5e-1, 3.75e-1, 4.e-1, 4.5e-1, 5.e-1, 5.5e-1, - 6.e-1, 6.25e-1, 6.5e-1, 7.e-1, 7.5e-1, 8.e-1, 8.5e-1, 9.e-1, 9.25e-1, - 9.5e-1, 9.75e-1, 1., 1.01, 1.02, 1.03, 1.04, 1.05, 1.06, 1.07, 1.08, 1.09, - 1.1, 1.11, 1.12, 1.13, 1.14, 1.15, 1.175, 1.2, 1.225, 1.25, 1.3, 1.35, 1.4, - 1.45, 1.5, 1.59, 1.68, 1.77, 1.86, 1.94, 2., 2.12, 2.21, 2.3, 2.38, 2.47, - 2.57, 2.67, 2.77, 2.87, 2.97, 3., 3.1, 3.2, 3.5, 3.73, 4.1, 4.7, 5., 5.4, - 6., 6.25, 6.5, 6.75, 6.875, 7., 7.15, 8.1, 9.1, 1.e+1, 1.15e+1, 1.19e+1, - 1.29e+1, 1.44e+1, 1.6e+1, 1.7e+1, 1.85e+1, 1.94e+1, 2.e+1, 2.05e+1, - 2.12e+1, 2.175e+1, 2.25e+1, 2.5e+1, 2.75e+1, 3.e+1, 3.125e+1, 3.175e+1, - 3.325e+1, 3.375e+1, 3.5e+1, 3.55e+1, 3.6e+1, 3.7e+1, 3.713e+1, 3.727e+1, - 3.763e+1, 3.8e+1, 3.91e+1, 3.96e+1, 4.1e+1, 4.24e+1, 4.4e+1, 4.52e+1, - 4.83e+1, 5.06e+1, 5.34e+1, 5.8e+1, 6.1e+1, 6.3e+1, 6.5e+1, 6.75e+1, 7.2e+1, - 7.6e+1, 8.e+1, 8.17e+1, 9.e+1, 9.7e+1, 1.012e+2, 1.05e+2, 1.08e+2, 1.13e+2, - 1.16e+2, 1.175e+2, 1.19e+2, 1.22e+2, 1.43e+2, 1.7e+2, 1.8e+2, 1.877e+2, - 1.885e+2, 1.915e+2, 1.93e+2, 2.02e+2, 2.074e+2, 2.095e+2, 2.2e+2, 2.4e+2, - 2.85e+2, 3.05e+2, 5.5e+2, 6.7e+2, 6.83e+2, 9.5e+2, 1.15e+3, 1.5e+3, - 1.55e+3, 1.8e+3, 2.2e+3, 2.25e+3, 2.5e+3, 3.e+3, 3.74e+3, 3.9e+3, 5.7e+3, - 8.03e+3, 9.5e+3, 1.3e+4, 1.7e+4, 2.e+4, 3.e+4, 4.5e+4, 5.e+4, 5.2e+4, - 6.e+4, 7.3e+4, 7.5e+4, 8.2e+4, 8.5e+4, 1.e+5, 1.283e+5, 1.49e+5, 2.e+5, - 2.7e+5, 3.3e+5, 4.e+5, 4.2e+5, 4.4e+5, 4.7e+5, 4.92e+5, 5.5e+5, 5.73e+5, - 6.e+5, 6.7e+5, 6.79e+5, 7.5e+5, 8.2e+5, 8.611e+5, 8.75e+5, 9.e+5, 9.2e+5, - 1.01e+6, 1.1e+6, 1.2e+6, 1.25e+6, 1.317e+6, 1.356e+6, 1.4e+6, 1.5e+6, - 1.85e+6, 2.354e+6, 2.479e+6, 3.e+6, 4.304e+6, 4.8e+6, 6.434e+6, 8.187e+6, - 1.e+7, 1.284e+7, 1.384e+7, 1.455e+7, 1.568e+7, 1.733e+7, 2.e+7]) -GROUP_STRUCTURES['TRIPOLI-315'] = np.array([ - 1.0e-5, 1.1e-4, 3.000e-3, 5.500e-3, 1.000e-2, 1.500e-2, 2.000e-2, 3.000e-2, - 3.200e-2, 3.238e-2, 4.300e-2, 5.900e-2, 7.700e-2, 9.500e-2, 1.000e-1, - 1.150e-1, 1.340e-1, 1.600e-1, 1.890e-1, 2.200e-1, 2.480e-1, 2.825e-1, - 3.145e-1, 3.520e-1, 3.910e-1, 4.140e-1, 4.330e-1, 4.850e-1, 5.316e-1, - 5.400e-1, 6.250e-1, 6.826e-1, 7.050e-1, 7.900e-1, 8.600e-1, 8.764e-1, - 9.300e-1, 9.860e-1, 1.010, 1.035, 1.070, 1.080, 1.090, - 1.110, 1.125, 1.170, 1.235, 1.305, 1.370, 1.440, - 1.445, 1.510, 1.590, 1.670, 1.755, 1.840, 1.855, - 1.930, 2.020, 2.130, 2.360, 2.372, 2.768, 3.059, - 3.381, 3.928, 4.129, 4.470, 4.670, 5.043, 5.623, - 6.160, 6.476, 7.079, 7.524, 7.943, 8.315, 8.913, - 9.190, 1.000e1, 1.068e1, 1.122e1, 1.259e1, 1.371e1, 1.523e1, - 1.674e1, 1.760e1, 1.903e1, 2.045e1, 2.260e1, 2.498e1, 2.792e1, - 2.920e1, 3.051e1, 3.389e1, 3.727e1, 3.981e1, 4.552e1, 4.785e1, - 5.012e1, 5.559e1, 6.144e1, 6.310e1, 6.790e1, 7.079e1, 7.889e1, - 8.528e1, 9.166e1, 1.013e2, 1.122e2, 1.301e2, 1.367e2, 1.585e2, - 1.670e2, 1.778e2, 2.040e2, 2.145e2, 2.430e2, 2.754e2, 3.043e2, - 3.536e2, 3.981e2, 4.540e2, 5.145e2, 5.830e2, 6.310e2, 6.773e2, - 7.079e2, 7.485e2, 8.482e2, 9.611e2, 1.010e3, 1.117e3, 1.234e3, - 1.364e3, 1.507e3, 1.585e3, 1.796e3, 2.035e3, 2.113e3, 2.249e3, - 2.371e3, 2.485e3, 2.613e3, 2.661e3, 2.747e3, 2.818e3, 3.035e3, - 3.162e3, 3.355e3, 3.548e3, 3.707e3, 3.981e3, 4.307e3, 4.643e3, - 5.004e3, 5.531e3, 6.267e3, 7.102e3, 7.466e3, 8.251e3, 9.119e3, - 1.008e4, 1.114e4, 1.171e4, 1.273e4, 1.383e4, 1.503e4, 1.585e4, - 1.662e4, 1.778e4, 1.931e4, 1.995e4, 2.054e4, 2.113e4, 2.187e4, - 2.239e4, 2.304e4, 2.358e4, 2.418e4, 2.441e4, 2.479e4, 2.512e4, - 2.585e4, 2.606e4, 2.661e4, 2.700e4, 2.738e4, 2.818e4, 2.850e4, - 2.901e4, 2.985e4, 3.073e4, 3.162e4, 3.183e4, 3.431e4, 3.698e4, - 4.087e4, 4.359e4, 4.631e4, 4.939e4, 5.248e4, 5.517e4, 5.656e4, - 6.173e4, 6.738e4, 7.200e4, 7.499e4, 7.950e4, 8.230e4, 8.250e4, - 8.652e4, 9.804e4, 1.111e5, 1.168e5, 1.228e5, 1.291e5, 1.357e5, - 1.426e5, 1.500e5, 1.576e5, 1.657e5, 1.742e5, 1.832e5, 1.925e5, - 2.024e5, 2.128e5, 2.237e5, 2.352e5, 2.472e5, 2.732e5, 2.873e5, - 2.945e5, 2.972e5, 2.985e5, 3.020e5, 3.337e5, 3.688e5, 3.877e5, - 4.076e5, 4.505e5, 5.234e5, 5.502e5, 5.784e5, 6.081e5, 6.393e5, - 6.721e5, 7.065e5, 7.427e5, 7.808e5, 8.209e5, 8.629e5, 9.072e5, - 9.616e5, 1.003e6, 1.108e6, 1.165e6, 1.225e6, 1.287e6, 1.353e6, - 1.423e6, 1.496e6, 1.572e6, 1.653e6, 1.738e6, 1.827e6, 1.921e6, - 2.019e6, 2.122e6, 2.231e6, 2.307e6, 2.346e6, 2.365e6, 2.385e6, - 2.466e6, 2.592e6, 2.725e6, 2.865e6, 3.012e6, 3.166e6, 3.329e6, - 3.679e6, 4.066e6, 4.493e6, 4.724e6, 4.966e6, 5.220e6, 5.488e6, - 5.769e6, 6.065e6, 6.376e6, 6.592e6, 6.703e6, 7.047e6, 7.408e6, - 7.788e6, 8.187e6, 8.607e6, 9.048e6, 9.512e6, 1.000e7, 1.051e7, - 1.105e7, 1.162e7, 1.221e7, 1.284e7, 1.350e7, 1.384e7, 1.419e7, - 1.455e7, 1.492e7, 1.568e7, 1.649e7, 1.691e7, 1.733e7, 1.964e7]) -GROUP_STRUCTURES['SHEM-361'] = np.array([ - 0.00000e+00, 2.49990e-03, 4.55602e-03, 7.14526e-03, 1.04505e-02, - 1.48300e-02, 2.00104e-02, 2.49394e-02, 2.92989e-02, 3.43998e-02, - 4.02999e-02, 4.73019e-02, 5.54982e-02, 6.51999e-02, 7.64969e-02, - 8.97968e-02, 1.04298e-01, 1.19995e-01, 1.37999e-01, 1.61895e-01, - 1.90005e-01, 2.09610e-01, 2.31192e-01, 2.54997e-01, 2.79989e-01, - 3.05012e-01, 3.25008e-01, 3.52994e-01, 3.90001e-01, 4.31579e-01, - 4.75017e-01, 5.20011e-01, 5.54990e-01, 5.94993e-01, 6.24999e-01, - 7.19999e-01, 8.00371e-01, 8.80024e-01, 9.19978e-01, 9.44022e-01, - 9.63960e-01, 9.81959e-01, 9.96501e-01, 1.00904e+00, 1.02101e+00, - 1.03499e+00, 1.07799e+00, 1.09198e+00, 1.10395e+00, 1.11605e+00, - 1.12997e+00, 1.14797e+00, 1.16999e+00, 1.21397e+00, 1.25094e+00, - 1.29304e+00, 1.33095e+00, 1.38098e+00, 1.41001e+00, 1.44397e+00, - 1.51998e+00, 1.58803e+00, 1.66895e+00, 1.77997e+00, 1.90008e+00, - 1.98992e+00, 2.07010e+00, 2.15695e+00, 2.21709e+00, 2.27299e+00, - 2.33006e+00, 2.46994e+00, 2.55000e+00, 2.59009e+00, 2.62005e+00, - 2.64004e+00, 2.70012e+00, 2.71990e+00, 2.74092e+00, 2.77512e+00, - 2.88405e+00, 3.14211e+00, 3.54307e+00, 3.71209e+00, 3.88217e+00, - 4.00000e+00, 4.21983e+00, 4.30981e+00, 4.41980e+00, 4.76785e+00, - 4.93323e+00, 5.10997e+00, 5.21008e+00, 5.32011e+00, 5.38003e+00, - 5.41025e+00, 5.48817e+00, 5.53004e+00, 5.61979e+00, 5.72015e+00, - 5.80021e+00, 5.96014e+00, 6.05991e+00, 6.16011e+00, 6.28016e+00, - 6.35978e+00, 6.43206e+00, 6.48178e+00, 6.51492e+00, 6.53907e+00, - 6.55609e+00, 6.57184e+00, 6.58829e+00, 6.60611e+00, 6.63126e+00, - 6.71668e+00, 6.74225e+00, 6.75981e+00, 6.77605e+00, 6.79165e+00, - 6.81070e+00, 6.83526e+00, 6.87021e+00, 6.91778e+00, 6.99429e+00, - 7.13987e+00, 7.38015e+00, 7.60035e+00, 7.73994e+00, 7.83965e+00, - 7.97008e+00, 8.13027e+00, 8.30032e+00, 8.52407e+00, 8.67369e+00, - 8.80038e+00, 8.97995e+00, 9.14031e+00, 9.50002e+00, 1.05793e+01, - 1.08038e+01, 1.10529e+01, 1.12694e+01, 1.15894e+01, 1.17094e+01, - 1.18153e+01, 1.19795e+01, 1.21302e+01, 1.23086e+01, 1.24721e+01, - 1.26000e+01, 1.33297e+01, 1.35460e+01, 1.40496e+01, 1.42505e+01, - 1.44702e+01, 1.45952e+01, 1.47301e+01, 1.48662e+01, 1.57792e+01, - 1.60498e+01, 1.65501e+01, 1.68305e+01, 1.74457e+01, 1.75648e+01, - 1.77590e+01, 1.79591e+01, 1.90848e+01, 1.91997e+01, 1.93927e+01, - 1.95974e+01, 2.00734e+01, 2.02751e+01, 2.04175e+01, 2.05199e+01, - 2.06021e+01, 2.06847e+01, 2.07676e+01, 2.09763e+01, 2.10604e+01, - 2.11448e+01, 2.12296e+01, 2.13360e+01, 2.14859e+01, 2.17018e+01, - 2.20011e+01, 2.21557e+01, 2.23788e+01, 2.25356e+01, 2.46578e+01, - 2.78852e+01, 3.16930e+01, 3.30855e+01, 3.45392e+01, 3.56980e+01, - 3.60568e+01, 3.64191e+01, 3.68588e+01, 3.73038e+01, 3.77919e+01, - 3.87874e+01, 3.97295e+01, 4.12270e+01, 4.21441e+01, 4.31246e+01, - 4.41721e+01, 4.52904e+01, 4.62053e+01, 4.75173e+01, 4.92591e+01, - 5.17847e+01, 5.29895e+01, 5.40600e+01, 5.70595e+01, 5.99250e+01, - 6.23083e+01, 6.36306e+01, 6.45923e+01, 6.50460e+01, 6.55029e+01, - 6.58312e+01, 6.61612e+01, 6.64929e+01, 6.68261e+01, 6.90682e+01, - 7.18869e+01, 7.35595e+01, 7.63322e+01, 7.93679e+01, 8.39393e+01, - 8.87741e+01, 9.33256e+01, 9.73287e+01, 1.00594e+02, 1.01098e+02, - 1.01605e+02, 1.02115e+02, 1.03038e+02, 1.05646e+02, 1.10288e+02, - 1.12854e+02, 1.15480e+02, 1.16524e+02, 1.17577e+02, 1.20554e+02, - 1.26229e+02, 1.32701e+02, 1.39504e+02, 1.46657e+02, 1.54176e+02, - 1.63056e+02, 1.67519e+02, 1.75229e+02, 1.83295e+02, 1.84952e+02, - 1.86251e+02, 1.87559e+02, 1.88877e+02, 1.90204e+02, 1.93078e+02, - 1.95996e+02, 2.00958e+02, 2.12108e+02, 2.24325e+02, 2.35590e+02, - 2.41796e+02, 2.56748e+02, 2.68297e+02, 2.76468e+02, 2.84888e+02, - 2.88327e+02, 2.95922e+02, 3.19928e+02, 3.35323e+02, 3.53575e+02, - 3.71703e+02, 3.90760e+02, 4.19094e+02, 4.53999e+02, 5.01746e+02, - 5.39204e+02, 5.77146e+02, 5.92941e+02, 6.00099e+02, 6.12834e+02, - 6.46837e+02, 6.77287e+02, 7.48517e+02, 8.32218e+02, 9.09681e+02, - 9.82494e+02, 1.06432e+03, 1.13467e+03, 1.34358e+03, 1.58620e+03, - 1.81183e+03, 2.08410e+03, 2.39729e+03, 2.70024e+03, 2.99618e+03, - 3.48107e+03, 4.09735e+03, 5.00451e+03, 6.11252e+03, 7.46585e+03, - 9.11881e+03, 1.11377e+04, 1.36037e+04, 1.48997e+04, 1.62005e+04, - 1.85847e+04, 2.26994e+04, 2.49991e+04, 2.61001e+04, 2.73944e+04, - 2.92810e+04, 3.34596e+04, 3.69786e+04, 4.08677e+04, 4.99159e+04, - 5.51656e+04, 6.73794e+04, 8.22974e+04, 9.46645e+04, 1.15624e+05, - 1.22773e+05, 1.40000e+05, 1.64999e+05, 1.95008e+05, 2.30014e+05, - 2.67826e+05, 3.20646e+05, 3.83884e+05, 4.12501e+05, 4.56021e+05, - 4.94002e+05, 5.78443e+05, 7.06511e+05, 8.60006e+05, 9.51119e+05, - 1.05115e+06, 1.16205e+06, 1.28696e+06, 1.33694e+06, 1.40577e+06, - 1.63654e+06, 1.90139e+06, 2.23130e+06, 2.72531e+06, 3.32871e+06, - 4.06569e+06, 4.96585e+06, 6.06530e+06, 6.70319e+06, 7.40817e+06, - 8.18730e+06, 9.04836e+06, 9.99999e+06, 1.16183e+07, 1.38403e+07, - 1.49182e+07, 1.96403e+07]) -GROUP_STRUCTURES['CCFE-709'] = np.array([ - 1.e-5, 1.0471e-5, 1.0965e-5, 1.1482e-5, 1.2023e-5, - 1.2589e-5, 1.3183e-5, 1.3804e-5, 1.4454e-5, 1.5136e-5, - 1.5849e-5, 1.6596e-5, 1.7378e-5, 1.8197e-5, 1.9055e-5, - 1.9953e-5, 2.0893e-5, 2.1878e-5, 2.2909e-5, 2.3988e-5, - 2.5119e-5, 2.6303e-5, 2.7542e-5, 2.8840e-5, 3.0200e-5, - 3.1623e-5, 3.3113e-5, 3.4674e-5, 3.6308e-5, 3.8019e-5, - 3.9811e-5, 4.1687e-5, 4.3652e-5, 4.5709e-5, 4.7863e-5, - 5.0119e-5, 5.2481e-5, 5.4954e-5, 5.7544e-5, 6.0256e-5, - 6.3096e-5, 6.6069e-5, 6.9183e-5, 7.2444e-5, 7.5858e-5, - 7.9433e-5, 8.3176e-5, 8.7096e-5, 9.1201e-5, 9.5499e-5, - 1.0000e-4, 1.0471e-4, 1.0965e-4, 1.1482e-4, 1.2023e-4, - 1.2589e-4, 1.3183e-4, 1.3804e-4, 1.4454e-4, 1.5136e-4, - 1.5849e-4, 1.6596e-4, 1.7378e-4, 1.8197e-4, 1.9055e-4, - 1.9953e-4, 2.0893e-4, 2.1878e-4, 2.2909e-4, 2.3988e-4, - 2.5119e-4, 2.6303e-4, 2.7542e-4, 2.8840e-4, 3.0200e-4, - 3.1623e-4, 3.3113e-4, 3.4674e-4, 3.6308e-4, 3.8019e-4, - 3.9811e-4, 4.1687e-4, 4.3652e-4, 4.5709e-4, 4.7863e-4, - 5.0119e-4, 5.2481e-4, 5.4954e-4, 5.7544e-4, 6.0256e-4, - 6.3096e-4, 6.6069e-4, 6.9183e-4, 7.2444e-4, 7.5858e-4, - 7.9433e-4, 8.3176e-4, 8.7096e-4, 9.1201e-4, 9.5499e-4, - 1.0000e-3, 1.0471e-3, 1.0965e-3, 1.1482e-3, 1.2023e-3, - 1.2589e-3, 1.3183e-3, 1.3804e-3, 1.4454e-3, 1.5136e-3, - 1.5849e-3, 1.6596e-3, 1.7378e-3, 1.8197e-3, 1.9055e-3, - 1.9953e-3, 2.0893e-3, 2.1878e-3, 2.2909e-3, 2.3988e-3, - 2.5119e-3, 2.6303e-3, 2.7542e-3, 2.8840e-3, 3.0200e-3, - 3.1623e-3, 3.3113e-3, 3.4674e-3, 3.6308e-3, 3.8019e-3, - 3.9811e-3, 4.1687e-3, 4.3652e-3, 4.5709e-3, 4.7863e-3, - 5.0119e-3, 5.2481e-3, 5.4954e-3, 5.7544e-3, 6.0256e-3, - 6.3096e-3, 6.6069e-3, 6.9183e-3, 7.2444e-3, 7.5858e-3, - 7.9433e-3, 8.3176e-3, 8.7096e-3, 9.1201e-3, 9.5499e-3, - 1.0000e-2, 1.0471e-2, 1.0965e-2, 1.1482e-2, 1.2023e-2, - 1.2589e-2, 1.3183e-2, 1.3804e-2, 1.4454e-2, 1.5136e-2, - 1.5849e-2, 1.6596e-2, 1.7378e-2, 1.8197e-2, 1.9055e-2, - 1.9953e-2, 2.0893e-2, 2.1878e-2, 2.2909e-2, 2.3988e-2, - 2.5119e-2, 2.6303e-2, 2.7542e-2, 2.8840e-2, 3.0200e-2, - 3.1623e-2, 3.3113e-2, 3.4674e-2, 3.6308e-2, 3.8019e-2, - 3.9811e-2, 4.1687e-2, 4.3652e-2, 4.5709e-2, 4.7863e-2, - 5.0119e-2, 5.2481e-2, 5.4954e-2, 5.7544e-2, 6.0256e-2, - 6.3096e-2, 6.6069e-2, 6.9183e-2, 7.2444e-2, 7.5858e-2, - 7.9433e-2, 8.3176e-2, 8.7096e-2, 9.1201e-2, 9.5499e-2, - 1.0000e-1, 1.0471e-1, 1.0965e-1, 1.1482e-1, 1.2023e-1, - 1.2589e-1, 1.3183e-1, 1.3804e-1, 1.4454e-1, 1.5136e-1, - 1.5849e-1, 1.6596e-1, 1.7378e-1, 1.8197e-1, 1.9055e-1, - 1.9953e-1, 2.0893e-1, 2.1878e-1, 2.2909e-1, 2.3988e-1, - 2.5119e-1, 2.6303e-1, 2.7542e-1, 2.8840e-1, 3.0200e-1, - 3.1623e-1, 3.3113e-1, 3.4674e-1, 3.6308e-1, 3.8019e-1, - 3.9811e-1, 4.1687e-1, 4.3652e-1, 4.5709e-1, 4.7863e-1, - 5.0119e-1, 5.2481e-1, 5.4954e-1, 5.7544e-1, 6.0256e-1, - 6.3096e-1, 6.6069e-1, 6.9183e-1, 7.2444e-1, 7.5858e-1, - 7.9433e-1, 8.3176e-1, 8.7096e-1, 9.1201e-1, 9.5499e-1, - 1.0000e0, 1.0471e0, 1.0965e0, 1.1482e0, 1.2023e0, - 1.2589e0, 1.3183e0, 1.3804e0, 1.4454e0, 1.5136e0, - 1.5849e0, 1.6596e0, 1.7378e0, 1.8197e0, 1.9055e0, - 1.9953e0, 2.0893e0, 2.1878e0, 2.2909e0, 2.3988e0, - 2.5119e0, 2.6303e0, 2.7542e0, 2.8840e0, 3.0200e0, - 3.1623e0, 3.3113e0, 3.4674e0, 3.6308e0, 3.8019e0, - 3.9811e0, 4.1687e0, 4.3652e0, 4.5709e0, 4.7863e0, - 5.0119e0, 5.2481e0, 5.4954e0, 5.7544e0, 6.0256e0, - 6.3096e0, 6.6069e0, 6.9183e0, 7.2444e0, 7.5858e0, - 7.9433e0, 8.3176e0, 8.7096e0, 9.1201e0, 9.5499e0, - 1.0000e1, 1.0471e1, 1.0965e1, 1.1482e1, 1.2023e1, - 1.2589e1, 1.3183e1, 1.3804e1, 1.4454e1, 1.5136e1, - 1.5849e1, 1.6596e1, 1.7378e1, 1.8197e1, 1.9055e1, - 1.9953e1, 2.0893e1, 2.1878e1, 2.2909e1, 2.3988e1, - 2.5119e1, 2.6303e1, 2.7542e1, 2.8840e1, 3.0200e1, - 3.1623e1, 3.3113e1, 3.4674e1, 3.6308e1, 3.8019e1, - 3.9811e1, 4.1687e1, 4.3652e1, 4.5709e1, 4.7863e1, - 5.0119e1, 5.2481e1, 5.4954e1, 5.7544e1, 6.0256e1, - 6.3096e1, 6.6069e1, 6.9183e1, 7.2444e1, 7.5858e1, - 7.9433e1, 8.3176e1, 8.7096e1, 9.1201e1, 9.5499e1, - 1.0000e2, 1.0471e2, 1.0965e2, 1.1482e2, 1.2023e2, - 1.2589e2, 1.3183e2, 1.3804e2, 1.4454e2, 1.5136e2, - 1.5849e2, 1.6596e2, 1.7378e2, 1.8197e2, 1.9055e2, - 1.9953e2, 2.0893e2, 2.1878e2, 2.2909e2, 2.3988e2, - 2.5119e2, 2.6303e2, 2.7542e2, 2.8840e2, 3.0200e2, - 3.1623e2, 3.3113e2, 3.4674e2, 3.6308e2, 3.8019e2, - 3.9811e2, 4.1687e2, 4.3652e2, 4.5709e2, 4.7863e2, - 5.0119e2, 5.2481e2, 5.4954e2, 5.7544e2, 6.0256e2, - 6.3096e2, 6.6069e2, 6.9183e2, 7.2444e2, 7.5858e2, - 7.9433e2, 8.3176e2, 8.7096e2, 9.1201e2, 9.5499e2, - 1.0000e3, 1.0471e3, 1.0965e3, 1.1482e3, 1.2023e3, - 1.2589e3, 1.3183e3, 1.3804e3, 1.4454e3, 1.5136e3, - 1.5849e3, 1.6596e3, 1.7378e3, 1.8197e3, 1.9055e3, - 1.9953e3, 2.0893e3, 2.1878e3, 2.2909e3, 2.3988e3, - 2.5119e3, 2.6303e3, 2.7542e3, 2.8840e3, 3.0200e3, - 3.1623e3, 3.3113e3, 3.4674e3, 3.6308e3, 3.8019e3, - 3.9811e3, 4.1687e3, 4.3652e3, 4.5709e3, 4.7863e3, - 5.0119e3, 5.2481e3, 5.4954e3, 5.7544e3, 6.0256e3, - 6.3096e3, 6.6069e3, 6.9183e3, 7.2444e3, 7.5858e3, - 7.9433e3, 8.3176e3, 8.7096e3, 9.1201e3, 9.5499e3, - 1.0000e4, 1.0471e4, 1.0965e4, 1.1482e4, 1.2023e4, - 1.2589e4, 1.3183e4, 1.3804e4, 1.4454e4, 1.5136e4, - 1.5849e4, 1.6596e4, 1.7378e4, 1.8197e4, 1.9055e4, - 1.9953e4, 2.0893e4, 2.1878e4, 2.2909e4, 2.3988e4, - 2.5119e4, 2.6303e4, 2.7542e4, 2.8840e4, 3.0200e4, - 3.1623e4, 3.3113e4, 3.4674e4, 3.6308e4, 3.8019e4, - 3.9811e4, 4.1687e4, 4.3652e4, 4.5709e4, 4.7863e4, - 5.0119e4, 5.2481e4, 5.4954e4, 5.7544e4, 6.0256e4, - 6.3096e4, 6.6069e4, 6.9183e4, 7.2444e4, 7.5858e4, - 7.9433e4, 8.3176e4, 8.7096e4, 9.1201e4, 9.5499e4, - 1.0000e5, 1.0471e5, 1.0965e5, 1.1482e5, 1.2023e5, - 1.2589e5, 1.3183e5, 1.3804e5, 1.4454e5, 1.5136e5, - 1.5849e5, 1.6596e5, 1.7378e5, 1.8197e5, 1.9055e5, - 1.9953e5, 2.0893e5, 2.1878e5, 2.2909e5, 2.3988e5, - 2.5119e5, 2.6303e5, 2.7542e5, 2.8840e5, 3.0200e5, - 3.1623e5, 3.3113e5, 3.4674e5, 3.6308e5, 3.8019e5, - 3.9811e5, 4.1687e5, 4.3652e5, 4.5709e5, 4.7863e5, - 5.0119e5, 5.2481e5, 5.4954e5, 5.7544e5, 6.0256e5, - 6.3096e5, 6.6069e5, 6.9183e5, 7.2444e5, 7.5858e5, - 7.9433e5, 8.3176e5, 8.7096e5, 9.1201e5, 9.5499e5, - 1.0000e6, 1.0471e6, 1.0965e6, 1.1482e6, 1.2023e6, - 1.2589e6, 1.3183e6, 1.3804e6, 1.4454e6, 1.5136e6, - 1.5849e6, 1.6596e6, 1.7378e6, 1.8197e6, 1.9055e6, - 1.9953e6, 2.0893e6, 2.1878e6, 2.2909e6, 2.3988e6, - 2.5119e6, 2.6303e6, 2.7542e6, 2.8840e6, 3.0200e6, - 3.1623e6, 3.3113e6, 3.4674e6, 3.6308e6, 3.8019e6, - 3.9811e6, 4.1687e6, 4.3652e6, 4.5709e6, 4.7863e6, - 5.0119e6, 5.2481e6, 5.4954e6, 5.7544e6, 6.0256e6, - 6.3096e6, 6.6069e6, 6.9183e6, 7.2444e6, 7.5858e6, - 7.9433e6, 8.3176e6, 8.7096e6, 9.1201e6, 9.5499e6, - 1.0000e7, 1.0200e7, 1.0400e7, 1.0600e7, 1.0800e7, - 1.1000e7, 1.1200e7, 1.1400e7, 1.1600e7, 1.1800e7, - 1.2000e7, 1.2200e7, 1.2400e7, 1.2600e7, 1.2800e7, - 1.3000e7, 1.3200e7, 1.3400e7, 1.3600e7, 1.3800e7, - 1.4000e7, 1.4200e7, 1.4400e7, 1.4600e7, 1.4800e7, - 1.5000e7, 1.5200e7, 1.5400e7, 1.5600e7, 1.5800e7, - 1.6000e7, 1.6200e7, 1.6400e7, 1.6600e7, 1.6800e7, - 1.7000e7, 1.7200e7, 1.7400e7, 1.7600e7, 1.7800e7, - 1.8000e7, 1.8200e7, 1.8400e7, 1.8600e7, 1.8800e7, - 1.9000e7, 1.9200e7, 1.9400e7, 1.9600e7, 1.9800e7, - 2.0000e7, 2.1000e7, 2.2000e7, 2.3000e7, 2.4000e7, - 2.5000e7, 2.6000e7, 2.7000e7, 2.8000e7, 2.9000e7, - 3.0000e7, 3.2000e7, 3.4000e7, 3.6000e7, 3.8000e7, - 4.0000e7, 4.2000e7, 4.4000e7, 4.6000e7, 4.8000e7, - 5.0000e7, 5.2000e7, 5.4000e7, 5.6000e7, 5.8000e7, - 6.0000e7, 6.5000e7, 7.0000e7, 7.5000e7, 8.0000e7, - 9.0000e7, 1.0000e8, 1.1000e8, 1.2000e8, 1.3000e8, - 1.4000e8, 1.5000e8, 1.6000e8, 1.8000e8, 2.0000e8, - 2.4000e8, 2.8000e8, 3.2000e8, 3.6000e8, 4.0000e8, - 4.4000e8, 4.8000e8, 5.2000e8, 5.6000e8, 6.0000e8, - 6.4000e8, 6.8000e8, 7.2000e8, 7.6000e8, 8.0000e8, - 8.4000e8, 8.8000e8, 9.2000e8, 9.6000e8, 1.0000e9,]) -GROUP_STRUCTURES['UKAEA-1102'] = np.array([ - 1.0000e-5, 1.0471e-5, 1.0965e-5, 1.1482e-5, 1.2023e-5, - 1.2589e-5, 1.3183e-5, 1.3804e-5, 1.4454e-5, 1.5136e-5, - 1.5849e-5, 1.6596e-5, 1.7378e-5, 1.8197e-5, 1.9055e-5, - 1.9953e-5, 2.0893e-5, 2.1878e-5, 2.2909e-5, 2.3988e-5, - 2.5119e-5, 2.6303e-5, 2.7542e-5, 2.8840e-5, 3.0200e-5, - 3.1623e-5, 3.3113e-5, 3.4674e-5, 3.6308e-5, 3.8019e-5, - 3.9811e-5, 4.1687e-5, 4.3652e-5, 4.5709e-5, 4.7863e-5, - 5.0119e-5, 5.2481e-5, 5.4954e-5, 5.7544e-5, 6.0256e-5, - 6.3096e-5, 6.6069e-5, 6.9183e-5, 7.2444e-5, 7.5858e-5, - 7.9433e-5, 8.3176e-5, 8.7096e-5, 9.1201e-5, 9.5499e-5, - 1.0000e-4, 1.0471e-4, 1.0965e-4, 1.1482e-4, 1.2023e-4, - 1.2589e-4, 1.3183e-4, 1.3804e-4, 1.4454e-4, 1.5136e-4, - 1.5849e-4, 1.6596e-4, 1.7378e-4, 1.8197e-4, 1.9055e-4, - 1.9953e-4, 2.0893e-4, 2.1878e-4, 2.2909e-4, 2.3988e-4, - 2.5119e-4, 2.6303e-4, 2.7542e-4, 2.8840e-4, 3.0200e-4, - 3.1623e-4, 3.3113e-4, 3.4674e-4, 3.6308e-4, 3.8019e-4, - 3.9811e-4, 4.1687e-4, 4.3652e-4, 4.5709e-4, 4.7863e-4, - 5.0119e-4, 5.2481e-4, 5.4954e-4, 5.7544e-4, 6.0256e-4, - 6.3096e-4, 6.6069e-4, 6.9183e-4, 7.2444e-4, 7.5858e-4, - 7.9433e-4, 8.3176e-4, 8.7096e-4, 9.1201e-4, 9.5499e-4, - 1.0000e-3, 1.0471e-3, 1.0965e-3, 1.1482e-3, 1.2023e-3, - 1.2589e-3, 1.3183e-3, 1.3804e-3, 1.4454e-3, 1.5136e-3, - 1.5849e-3, 1.6596e-3, 1.7378e-3, 1.8197e-3, 1.9055e-3, - 1.9953e-3, 2.0893e-3, 2.1878e-3, 2.2909e-3, 2.3988e-3, - 2.5119e-3, 2.6303e-3, 2.7542e-3, 2.8840e-3, 3.0200e-3, - 3.1623e-3, 3.3113e-3, 3.4674e-3, 3.6308e-3, 3.8019e-3, - 3.9811e-3, 4.1687e-3, 4.3652e-3, 4.5709e-3, 4.7863e-3, - 5.0119e-3, 5.2481e-3, 5.4954e-3, 5.7544e-3, 6.0256e-3, - 6.3096e-3, 6.6069e-3, 6.9183e-3, 7.2444e-3, 7.5858e-3, - 7.9433e-3, 8.3176e-3, 8.7096e-3, 9.1201e-3, 9.5499e-3, - 1.0000e-2, 1.0471e-2, 1.0965e-2, 1.1482e-2, 1.2023e-2, - 1.2589e-2, 1.3183e-2, 1.3804e-2, 1.4454e-2, 1.5136e-2, - 1.5849e-2, 1.6596e-2, 1.7378e-2, 1.8197e-2, 1.9055e-2, - 1.9953e-2, 2.0893e-2, 2.1878e-2, 2.2909e-2, 2.3988e-2, - 2.5119e-2, 2.6303e-2, 2.7542e-2, 2.8840e-2, 3.0200e-2, - 3.1623e-2, 3.3113e-2, 3.4674e-2, 3.6308e-2, 3.8019e-2, - 3.9811e-2, 4.1687e-2, 4.3652e-2, 4.5709e-2, 4.7863e-2, - 5.0119e-2, 5.2481e-2, 5.4954e-2, 5.7544e-2, 6.0256e-2, - 6.3096e-2, 6.6069e-2, 6.9183e-2, 7.2444e-2, 7.5858e-2, - 7.9433e-2, 8.3176e-2, 8.7096e-2, 9.1201e-2, 9.5499e-2, - 1.0000e-1, 1.0471e-1, 1.0965e-1, 1.1482e-1, 1.2023e-1, - 1.2589e-1, 1.3183e-1, 1.3804e-1, 1.4454e-1, 1.5136e-1, - 1.5849e-1, 1.6596e-1, 1.7378e-1, 1.8197e-1, 1.9055e-1, - 1.9953e-1, 2.0893e-1, 2.1878e-1, 2.2909e-1, 2.3988e-1, - 2.5119e-1, 2.6303e-1, 2.7542e-1, 2.8840e-1, 3.0200e-1, - 3.1623e-1, 3.3113e-1, 3.4674e-1, 3.6308e-1, 3.8019e-1, - 3.9811e-1, 4.1687e-1, 4.3652e-1, 4.5709e-1, 4.7863e-1, - 5.0119e-1, 5.2481e-1, 5.5000e-1, 5.7500e-1, 6.0000e-1, - 6.2500e-1, 6.5000e-1, 6.7500e-1, 7.0000e-1, 7.2500e-1, - 7.5000e-1, 7.7500e-1, 8.0000e-1, 8.2500e-1, 8.5000e-1, - 8.7500e-1, 9.0000e-1, 9.2500e-1, 9.5000e-1, 9.7500e-1, - 1.0000, 1.0250, 1.0500, 1.0750, 1.1000, - 1.1250, 1.1500, 1.1750, 1.2000, 1.2250, - 1.2500, 1.2750, 1.3000, 1.3250, 1.3500, - 1.3750, 1.4000, 1.4250, 1.4500, 1.4750, - 1.5000, 1.5250, 1.5500, 1.5750, 1.6000, - 1.6250, 1.6500, 1.6750, 1.7000, 1.7250, - 1.7500, 1.7750, 1.8000, 1.8250, 1.8500, - 1.8750, 1.9000, 1.9250, 1.9500, 1.9750, - 2.0000, 2.0250, 2.0500, 2.0750, 2.1000, - 2.1250, 2.1500, 2.1750, 2.2000, 2.2250, - 2.2500, 2.2750, 2.3000, 2.3250, 2.3500, - 2.3750, 2.4000, 2.4250, 2.4500, 2.4750, - 2.5000, 2.5250, 2.5500, 2.5750, 2.6000, - 2.6250, 2.6500, 2.6750, 2.7000, 2.7250, - 2.7500, 2.7750, 2.8000, 2.8250, 2.8500, - 2.8750, 2.9000, 2.9250, 2.9500, 2.9750, - 3.0000, 3.0250, 3.0500, 3.0750, 3.1000, - 3.1250, 3.1500, 3.1750, 3.2000, 3.2250, - 3.2500, 3.2750, 3.3000, 3.3250, 3.3500, - 3.3750, 3.4000, 3.4250, 3.4500, 3.4750, - 3.5000, 3.5250, 3.5500, 3.5750, 3.6000, - 3.6250, 3.6500, 3.6750, 3.7000, 3.7250, - 3.7500, 3.7750, 3.8000, 3.8250, 3.8500, - 3.8750, 3.9000, 3.9250, 3.9500, 3.9750, - 4.0000, 4.0250, 4.0500, 4.0750, 4.1000, - 4.1250, 4.1500, 4.1750, 4.2000, 4.2250, - 4.2500, 4.2750, 4.3000, 4.3250, 4.3500, - 4.3750, 4.4000, 4.4250, 4.4500, 4.4750, - 4.5000, 4.5250, 4.5500, 4.5750, 4.6000, - 4.6250, 4.6500, 4.6750, 4.7000, 4.7250, - 4.7500, 4.7750, 4.8000, 4.8250, 4.8500, - 4.8750, 4.9000, 4.9250, 4.9500, 4.9750, - 5.0000, 5.0250, 5.0500, 5.0750, 5.1000, - 5.1250, 5.1500, 5.1750, 5.2000, 5.2250, - 5.2500, 5.2750, 5.3000, 5.3250, 5.3500, - 5.3750, 5.4000, 5.4250, 5.4500, 5.4750, - 5.5000, 5.5250, 5.5500, 5.5750, 5.6000, - 5.6250, 5.6500, 5.6750, 5.7000, 5.7250, - 5.7500, 5.7750, 5.8000, 5.8250, 5.8500, - 5.8750, 5.9000, 5.9250, 5.9500, 5.9750, - 6.0000, 6.0250, 6.0500, 6.0750, 6.1000, - 6.1250, 6.1500, 6.1750, 6.2000, 6.2250, - 6.2500, 6.2750, 6.3000, 6.3250, 6.3500, - 6.3750, 6.4000, 6.4250, 6.4500, 6.4750, - 6.5000, 6.5250, 6.5500, 6.5750, 6.6000, - 6.6250, 6.6500, 6.6750, 6.7000, 6.7250, - 6.7500, 6.7750, 6.8000, 6.8250, 6.8500, - 6.8750, 6.9000, 6.9250, 6.9500, 6.9750, - 7.0000, 7.0250, 7.0500, 7.0750, 7.1000, - 7.1250, 7.1500, 7.1750, 7.2000, 7.2250, - 7.2500, 7.2750, 7.3000, 7.3250, 7.3500, - 7.3750, 7.4000, 7.4250, 7.4500, 7.4750, - 7.5000, 7.5250, 7.5500, 7.5750, 7.6000, - 7.6250, 7.6500, 7.6750, 7.7000, 7.7250, - 7.7500, 7.7750, 7.8000, 7.8250, 7.8500, - 7.8750, 7.9000, 7.9250, 7.9500, 7.9750, - 8.0000, 8.0250, 8.0500, 8.0750, 8.1000, - 8.1250, 8.1500, 8.1750, 8.2000, 8.2250, - 8.2500, 8.2750, 8.3000, 8.3250, 8.3500, - 8.3750, 8.4000, 8.4250, 8.4500, 8.4750, - 8.5000, 8.5250, 8.5500, 8.5750, 8.6000, - 8.6250, 8.6500, 8.6750, 8.7000, 8.7250, - 8.7500, 8.7750, 8.8000, 8.8250, 8.8500, - 8.8750, 8.9000, 8.9250, 8.9500, 8.9750, - 9.0000, 9.0250, 9.0500, 9.0750, 9.1000, - 9.1250, 9.1500, 9.1750, 9.2000, 9.2250, - 9.2500, 9.2750, 9.3000, 9.3250, 9.3500, - 9.3750, 9.4000, 9.4250, 9.4500, 9.4750, - 9.5000, 9.5250, 9.5500, 9.5750, 9.6000, - 9.6250, 9.6500, 9.6750, 9.7000, 9.7250, - 9.7500, 9.7750, 9.8000, 9.8250, 9.8500, - 9.8750, 9.9000, 9.9250, 9.9500, 9.9750, - 1.0000e1, 1.0471e1, 1.0965e1, 1.1482e1, 1.2023e1, - 1.2589e1, 1.3183e1, 1.3804e1, 1.4454e1, 1.5136e1, - 1.5849e1, 1.6596e1, 1.7378e1, 1.8197e1, 1.9055e1, - 1.9953e1, 2.0893e1, 2.1878e1, 2.2909e1, 2.3988e1, - 2.5119e1, 2.6303e1, 2.7542e1, 2.8840e1, 3.0200e1, - 3.1623e1, 3.3113e1, 3.4674e1, 3.6308e1, 3.8019e1, - 3.9811e1, 4.1687e1, 4.3652e1, 4.5709e1, 4.7863e1, - 5.0119e1, 5.2481e1, 5.4954e1, 5.7544e1, 6.0256e1, - 6.3096e1, 6.6069e1, 6.9183e1, 7.2444e1, 7.5858e1, - 7.9433e1, 8.3176e1, 8.7096e1, 9.1201e1, 9.5499e1, - 1.0000e2, 1.0471e2, 1.0965e2, 1.1482e2, 1.2023e2, - 1.2589e2, 1.3183e2, 1.3804e2, 1.4454e2, 1.5136e2, - 1.5849e2, 1.6596e2, 1.7378e2, 1.8197e2, 1.9055e2, - 1.9953e2, 2.0893e2, 2.1878e2, 2.2909e2, 2.3988e2, - 2.5119e2, 2.6303e2, 2.7542e2, 2.8840e2, 3.0200e2, - 3.1623e2, 3.3113e2, 3.4674e2, 3.6308e2, 3.8019e2, - 3.9811e2, 4.1687e2, 4.3652e2, 4.5709e2, 4.7863e2, - 5.0119e2, 5.2481e2, 5.4954e2, 5.7544e2, 6.0256e2, - 6.3096e2, 6.6069e2, 6.9183e2, 7.2444e2, 7.5858e2, - 7.9433e2, 8.3176e2, 8.7096e2, 9.1201e2, 9.5499e2, - 1.0000e3, 1.0471e3, 1.0965e3, 1.1482e3, 1.2023e3, - 1.2589e3, 1.3183e3, 1.3804e3, 1.4454e3, 1.5136e3, - 1.5849e3, 1.6596e3, 1.7378e3, 1.8197e3, 1.9055e3, - 1.9953e3, 2.0893e3, 2.1878e3, 2.2909e3, 2.3988e3, - 2.5119e3, 2.6303e3, 2.7542e3, 2.8840e3, 3.0200e3, - 3.1623e3, 3.3113e3, 3.4674e3, 3.6308e3, 3.8019e3, - 3.9811e3, 4.1687e3, 4.3652e3, 4.5709e3, 4.7863e3, - 5.0119e3, 5.2481e3, 5.4954e3, 5.7544e3, 6.0256e3, - 6.3096e3, 6.6069e3, 6.9183e3, 7.2444e3, 7.5858e3, - 7.9433e3, 8.3176e3, 8.7096e3, 9.1201e3, 9.5499e3, - 1.0000e4, 1.0471e4, 1.0965e4, 1.1482e4, 1.2023e4, - 1.2589e4, 1.3183e4, 1.3804e4, 1.4454e4, 1.5136e4, - 1.5849e4, 1.6596e4, 1.7378e4, 1.8197e4, 1.9055e4, - 1.9953e4, 2.0893e4, 2.1878e4, 2.2909e4, 2.3988e4, - 2.5119e4, 2.6303e4, 2.7542e4, 2.8840e4, 3.0200e4, - 3.1623e4, 3.3113e4, 3.4674e4, 3.6308e4, 3.8019e4, - 3.9811e4, 4.1687e4, 4.3652e4, 4.5709e4, 4.7863e4, - 5.0119e4, 5.2481e4, 5.4954e4, 5.7544e4, 6.0256e4, - 6.3096e4, 6.6069e4, 6.9183e4, 7.2444e4, 7.5858e4, - 7.9433e4, 8.3176e4, 8.7096e4, 9.1201e4, 9.5499e4, - 1.0000e5, 1.0471e5, 1.0965e5, 1.1482e5, 1.2023e5, - 1.2589e5, 1.3183e5, 1.3804e5, 1.4454e5, 1.5136e5, - 1.5849e5, 1.6596e5, 1.7378e5, 1.8197e5, 1.9055e5, - 1.9953e5, 2.0893e5, 2.1878e5, 2.2909e5, 2.3988e5, - 2.5119e5, 2.6303e5, 2.7542e5, 2.8840e5, 3.0200e5, - 3.1623e5, 3.3113e5, 3.4674e5, 3.6308e5, 3.8019e5, - 3.9811e5, 4.1687e5, 4.3652e5, 4.5709e5, 4.7863e5, - 5.0119e5, 5.2481e5, 5.4954e5, 5.7544e5, 6.0256e5, - 6.3096e5, 6.6069e5, 6.9183e5, 7.2444e5, 7.5858e5, - 7.9433e5, 8.3176e5, 8.7096e5, 9.1201e5, 9.5499e5, - 1.0000e6, 1.0471e6, 1.0965e6, 1.1482e6, 1.2023e6, - 1.2589e6, 1.3183e6, 1.3804e6, 1.4454e6, 1.5136e6, - 1.5849e6, 1.6596e6, 1.7378e6, 1.8197e6, 1.9055e6, - 1.9953e6, 2.0893e6, 2.1878e6, 2.2909e6, 2.3988e6, - 2.5119e6, 2.6303e6, 2.7542e6, 2.8840e6, 3.0200e6, - 3.1623e6, 3.3113e6, 3.4674e6, 3.6308e6, 3.8019e6, - 3.9811e6, 4.1687e6, 4.3652e6, 4.5709e6, 4.7863e6, - 5.0000e6, 5.2000e6, 5.4000e6, 5.6000e6, 5.8000e6, - 6.0000e6, 6.2000e6, 6.4000e6, 6.6000e6, 6.8000e6, - 7.0000e6, 7.2000e6, 7.4000e6, 7.6000e6, 7.8000e6, - 8.0000e6, 8.2000e6, 8.4000e6, 8.6000e6, 8.8000e6, - 9.0000e6, 9.2000e6, 9.4000e6, 9.6000e6, 9.8000e6, - 1.0000e7, 1.0200e7, 1.0400e7, 1.0600e7, 1.0800e7, - 1.1000e7, 1.1200e7, 1.1400e7, 1.1600e7, 1.1800e7, - 1.2000e7, 1.2200e7, 1.2400e7, 1.2600e7, 1.2800e7, - 1.3000e7, 1.3200e7, 1.3400e7, 1.3600e7, 1.3800e7, - 1.4000e7, 1.4200e7, 1.4400e7, 1.4600e7, 1.4800e7, - 1.5000e7, 1.5200e7, 1.5400e7, 1.5600e7, 1.5800e7, - 1.6000e7, 1.6200e7, 1.6400e7, 1.6600e7, 1.6800e7, - 1.7000e7, 1.7200e7, 1.7400e7, 1.7600e7, 1.7800e7, - 1.8000e7, 1.8200e7, 1.8400e7, 1.8600e7, 1.8800e7, - 1.9000e7, 1.9200e7, 1.9400e7, 1.9600e7, 1.9800e7, - 2.0000e7, 2.0200e7, 2.0400e7, 2.0600e7, 2.0800e7, - 2.1000e7, 2.1200e7, 2.1400e7, 2.1600e7, 2.1800e7, - 2.2000e7, 2.2200e7, 2.2400e7, 2.2600e7, 2.2800e7, - 2.3000e7, 2.3200e7, 2.3400e7, 2.3600e7, 2.3800e7, - 2.4000e7, 2.4200e7, 2.4400e7, 2.4600e7, 2.4800e7, - 2.5000e7, 2.5200e7, 2.5400e7, 2.5600e7, 2.5800e7, - 2.6000e7, 2.6200e7, 2.6400e7, 2.6600e7, 2.6800e7, - 2.7000e7, 2.7200e7, 2.7400e7, 2.7600e7, 2.7800e7, - 2.8000e7, 2.8200e7, 2.8400e7, 2.8600e7, 2.8800e7, - 2.9000e7, 2.9200e7, 2.9400e7, 2.9600e7, 2.9800e7, - 3.0000e7, 3.0200e7, 3.1623e7, 3.3113e7, 3.4674e7, - 3.6308e7, 3.8019e7, 3.9811e7, 4.1687e7, 4.3652e7, - 4.5709e7, 4.7863e7, 5.0119e7, 5.2481e7, 5.4954e7, - 5.7544e7, 6.0256e7, 6.3096e7, 6.6069e7, 6.9183e7, - 7.2444e7, 7.5858e7, 7.9433e7, 8.3176e7, 8.7096e7, - 9.1201e7, 9.5499e7, 1.0000e8, 1.0471e8, 1.0965e8, - 1.1482e8, 1.2023e8, 1.2589e8, 1.3183e8, 1.3804e8, - 1.4454e8, 1.5136e8, 1.5849e8, 1.6596e8, 1.7378e8, - 1.8197e8, 1.9055e8, 1.9953e8, 2.0893e8, 2.1878e8, - 2.2909e8, 2.3988e8, 2.5119e8, 2.6303e8, 2.7542e8, - 2.8840e8, 3.0200e8, 3.1623e8, 3.3113e8, 3.4674e8, - 3.6308e8, 3.8019e8, 3.9811e8, 4.1687e8, 4.3652e8, - 4.5709e8, 4.7863e8, 5.0119e8, 5.2481e8, 5.4954e8, - 5.7544e8, 6.0256e8, 6.3096e8, 6.6069e8, 6.9183e8, - 7.2444e8, 7.5858e8, 7.9433e8, 8.3176e8, 8.7096e8, - 9.1201e8, 9.5499e8, 1.e9]) -GROUP_STRUCTURES['ECCO-1968'] = np.array([ - 1.00001e-5, 3.00000e-3, 5.00000e-3, 6.90000e-3, 1.00000e-2, 1.50000e-2, - 2.00000e-2, 2.50000e-2, 3.00000e-2, 3.50000e-2, 4.20000e-2, 5.00000e-2, - 5.80000e-2, 6.70000e-2, 7.70000e-2, 8.00000e-2, 9.50000e-2, 1.00000e-1, - 1.15000e-1, 1.34000e-1, 1.40000e-1, 1.46370e-1, 1.53030e-1, 1.60000e-1, - 1.69710e-1, 1.80000e-1, 1.89000e-1, 1.98810e-1, 2.09140e-1, 2.20000e-1, - 2.33580e-1, 2.48000e-1, 2.63510e-1, 2.80000e-1, 3.00000e-1, 3.14500e-1, - 3.20000e-1, 3.34660e-1, 3.50000e-1, 3.69930e-1, 3.91000e-1, 4.00000e-1, - 4.13990e-1, 4.33000e-1, 4.49680e-1, 4.67010e-1, 4.85000e-1, 5.00000e-1, - 5.19620e-1, 5.31580e-1, 5.40000e-1, 5.66960e-1, 5.95280e-1, 6.25000e-1, - 6.53150e-1, 6.82560e-1, 7.05000e-1, 7.41550e-1, 7.80000e-1, 7.90000e-1, - 8.19450e-1, 8.50000e-1, 8.60000e-1, 8.76425e-1, 9.10000e-1, 9.30000e-1, - 9.50000e-1, 9.72000e-1, 9.86000e-1, 9.96000e-1, - 1.020000, 1.035000, 1.045000, 1.071000, 1.080000, 1.097000, 1.110000, - 1.123000, 1.150000, 1.170000, 1.202060, 1.235000, 1.267080, 1.300000, - 1.337500, 1.370000, 1.404560, 1.440000, 1.475000, 1.500000, 1.544340, - 1.590000, 1.629510, 1.670000, 1.711970, 1.755000, 1.797000, 1.840000, - 1.855390, 1.884460, 1.930000, 1.974490, 2.020000, 2.059610, 2.100000, - 2.130000, 2.185310, 2.242050, 2.300270, 2.360000, 2.382370, 2.421710, - 2.485030, 2.550000, 2.600000, 2.659320, 2.720000, 2.767920, 2.837990, - 2.909830, 2.983490, 3.059020, 3.137330, 3.217630, 3.300000, 3.380750, - 3.466330, 3.554080, 3.644050, 3.736300, 3.830880, 3.927860, 4.000000, - 4.129250, 4.233782, 4.340961, 4.450853, 4.563526, 4.679053, 4.797503, - 4.918953, 5.043477, 5.085681, 5.128239, 5.171153, 5.214426, 5.258061, - 5.302061, 5.346430, 5.391169, 5.436284, 5.481775, 5.527647, 5.573904, - 5.620547, 5.667581, 5.715008, 5.762832, 5.811056, 5.859684, 5.908719, - 5.958164, 6.008022, 6.058298, 6.108995, 6.160116, 6.211665, 6.263645, - 6.316060, 6.368914, 6.422210, 6.475952, 6.530144, 6.584789, 6.639892, - 6.695455, 6.751484, 6.807981, 6.864952, 6.922399, 6.980326, 7.038739, - 7.097640, 7.157034, 7.216925, 7.277317, 7.338215, 7.399622, 7.461544, - 7.523983, 7.586945, 7.650434, 7.714454, 7.779009, 7.844105, 7.909746, - 7.975936, 8.042680, 8.109982, 8.177848, 8.246281, 8.315287, 8.384871, - 8.455037, 8.525790, 8.597135, 8.669077, 8.741621, 8.814772, 8.888536, - 8.962916, 9.037919, 9.113550, 9.189814, 9.266715, 9.344261, 9.422455, - 9.501303, 9.580812, 9.660985, 9.741830, 9.823351, 9.905554, 9.988446, - 1.007203e1, 1.015631e1, 1.024130e1, 1.032701e1, 1.041342e1, 1.050056e1, - 1.058843e1, 1.067704e1, 1.076639e1, 1.085648e1, 1.094733e1, 1.103894e1, - 1.113132e1, 1.122446e1, 1.131839e1, 1.141311e1, 1.150861e1, 1.160492e1, - 1.170203e1, 1.179995e1, 1.189870e1, 1.199827e1, 1.209867e1, 1.219991e1, - 1.230201e1, 1.240495e1, 1.250876e1, 1.261343e1, 1.271898e1, 1.282542e1, - 1.293274e1, 1.304097e1, 1.315010e1, 1.326014e1, 1.337110e1, 1.348299e1, - 1.359582e1, 1.370959e1, 1.382431e1, 1.394000e1, 1.405665e1, 1.417428e1, - 1.429289e1, 1.441250e1, 1.453310e1, 1.465472e1, 1.477735e1, 1.490101e1, - 1.502570e1, 1.515144e1, 1.527823e1, 1.540608e1, 1.553500e1, 1.566500e1, - 1.579609e1, 1.592827e1, 1.606156e1, 1.619597e1, 1.633150e1, 1.646816e1, - 1.660597e1, 1.674493e1, 1.688506e1, 1.702635e1, 1.716883e1, 1.731250e1, - 1.745738e1, 1.760346e1, 1.775077e1, 1.789931e1, 1.804910e1, 1.820013e1, - 1.835244e1, 1.850601e1, 1.866087e1, 1.881703e1, 1.897449e1, 1.913328e1, - 1.929339e1, 1.945484e1, 1.961764e1, 1.978180e1, 1.994734e1, 2.011426e1, - 2.028258e1, 2.045231e1, 2.062345e1, 2.079603e1, 2.097006e1, 2.114554e1, - 2.132249e1, 2.150092e1, 2.168084e1, 2.186227e1, 2.204522e1, 2.222969e1, - 2.241572e1, 2.260329e1, 2.279244e1, 2.298317e1, 2.317550e1, 2.336944e1, - 2.356499e1, 2.376219e1, 2.396104e1, 2.416154e1, 2.436373e1, 2.456761e1, - 2.477320e1, 2.498050e1, 2.518954e1, 2.540033e1, 2.561289e1, 2.582722e1, - 2.604335e1, 2.626128e1, 2.648104e1, 2.670264e1, 2.692609e1, 2.715141e1, - 2.737862e1, 2.760773e1, 2.783875e1, 2.807171e1, 2.830662e1, 2.854349e1, - 2.878235e1, 2.902320e1, 2.926607e1, 2.951098e1, 2.975793e1, 3.000695e1, - 3.025805e1, 3.051126e1, 3.076658e1, 3.102404e1, 3.128365e1, 3.154544e1, - 3.180942e1, 3.207560e1, 3.234401e1, 3.261467e1, 3.288760e1, 3.316281e1, - 3.344032e1, 3.372015e1, 3.400233e1, 3.428686e1, 3.457378e1, 3.486310e1, - 3.515484e1, 3.544902e1, 3.574566e1, 3.604479e1, 3.634642e1, 3.665057e1, - 3.695727e1, 3.726653e1, 3.757838e1, 3.789285e1, 3.820994e1, 3.852969e1, - 3.885211e1, 3.917723e1, 3.950507e1, 3.983565e1, 4.016900e1, 4.050514e1, - 4.084410e1, 4.118589e1, 4.153054e1, 4.187807e1, 4.222851e1, 4.258189e1, - 4.293822e1, 4.329753e1, 4.365985e1, 4.402521e1, 4.439361e1, 4.476511e1, - 4.513971e1, 4.551744e1, 4.589834e1, 4.628243e1, 4.666972e1, 4.706026e1, - 4.745407e1, 4.785117e1, 4.825160e1, 4.865538e1, 4.906253e1, 4.947309e1, - 4.988709e1, 5.030456e1, 5.072551e1, 5.114999e1, 5.157802e1, 5.200963e1, - 5.244486e1, 5.288373e1, 5.332626e1, 5.377251e1, 5.422248e1, 5.467623e1, - 5.513376e1, 5.559513e1, 5.606036e1, 5.652948e1, 5.700253e1, 5.747954e1, - 5.796053e1, 5.844556e1, 5.893464e1, 5.942781e1, 5.992511e1, 6.042657e1, - 6.093223e1, 6.144212e1, 6.195628e1, 6.247474e1, 6.299754e1, 6.352471e1, - 6.405630e1, 6.459233e1, 6.513285e1, 6.567789e1, 6.622749e1, 6.678169e1, - 6.734053e1, 6.790405e1, 6.847228e1, 6.904527e1, 6.962305e1, 7.020566e1, - 7.079316e1, 7.138556e1, 7.198293e1, 7.258529e1, 7.319270e1, 7.380518e1, - 7.442280e1, 7.504558e1, 7.567357e1, 7.630682e1, 7.694537e1, 7.758926e1, - 7.823854e1, 7.889325e1, 7.955344e1, 8.021915e1, 8.089044e1, 8.156734e1, - 8.224991e1, 8.293819e1, 8.363223e1, 8.433208e1, 8.503778e1, 8.574939e1, - 8.646695e1, 8.719052e1, 8.792015e1, 8.865588e1, 8.939776e1, 9.014586e1, - 9.090021e1, 9.166088e1, 9.242791e1, 9.320136e1, 9.398128e1, 9.476773e1, - 9.556076e1, 9.636043e1, 9.716679e1, 9.797990e1, 9.879981e1, 9.962658e1, - 1.004603e2, 1.013009e2, 1.021486e2, 1.030034e2, 1.038654e2, 1.047345e2, - 1.056110e2, 1.064947e2, 1.073859e2, 1.082845e2, 1.091907e2, 1.101044e2, - 1.110258e2, 1.119548e2, 1.128917e2, 1.138364e2, 1.147890e2, 1.157496e2, - 1.167182e2, 1.176949e2, 1.186798e2, 1.196729e2, 1.206744e2, 1.216842e2, - 1.227024e2, 1.237292e2, 1.247646e2, 1.258087e2, 1.268615e2, 1.279231e2, - 1.289935e2, 1.300730e2, 1.311615e2, 1.322590e2, 1.333658e2, 1.344818e2, - 1.356072e2, 1.367420e2, 1.378862e2, 1.390401e2, 1.402036e2, 1.413768e2, - 1.425599e2, 1.437529e2, 1.449558e2, 1.461688e2, 1.473920e2, 1.486254e2, - 1.498691e2, 1.511232e2, 1.523879e2, 1.536631e2, 1.549489e2, 1.562456e2, - 1.575531e2, 1.588715e2, 1.602010e2, 1.615415e2, 1.628933e2, 1.642565e2, - 1.656310e2, 1.670170e2, 1.684146e2, 1.698239e2, 1.712451e2, 1.726781e2, - 1.741231e2, 1.755802e2, 1.770494e2, 1.785310e2, 1.800250e2, 1.815315e2, - 1.830505e2, 1.845823e2, 1.861269e2, 1.876845e2, 1.892551e2, 1.908388e2, - 1.924358e2, 1.940461e2, 1.956699e2, 1.973073e2, 1.989584e2, 2.006233e2, - 2.023021e2, 2.039950e2, 2.057021e2, 2.074234e2, 2.091592e2, 2.109095e2, - 2.126744e2, 2.144541e2, 2.162487e2, 2.180583e2, 2.198830e2, 2.217230e2, - 2.235784e2, 2.254494e2, 2.273360e2, 2.292384e2, 2.311567e2, 2.330910e2, - 2.350416e2, 2.370084e2, 2.389917e2, 2.409917e2, 2.430083e2, 2.450418e2, - 2.470924e2, 2.491601e2, 2.512451e2, 2.533476e2, 2.554676e2, 2.576054e2, - 2.597611e2, 2.619348e2, 2.641267e2, 2.663370e2, 2.685657e2, 2.708131e2, - 2.730793e2, 2.753645e2, 2.776688e2, 2.799924e2, 2.823354e2, 2.846980e2, - 2.870804e2, 2.894827e2, 2.919052e2, 2.943479e2, 2.968110e2, 2.992948e2, - 3.017993e2, 3.043248e2, 3.068715e2, 3.094394e2, 3.120288e2, 3.146399e2, - 3.172729e2, 3.199279e2, 3.226051e2, 3.253047e2, 3.280269e2, 3.307719e2, - 3.335398e2, 3.363309e2, 3.391454e2, 3.419834e2, 3.448452e2, 3.477309e2, - 3.506408e2, 3.535750e2, 3.565338e2, 3.595173e2, 3.625258e2, 3.655595e2, - 3.686185e2, 3.717032e2, 3.748137e2, 3.779502e2, 3.811129e2, 3.843021e2, - 3.875180e2, 3.907608e2, 3.940308e2, 3.973281e2, 4.006530e2, 4.040057e2, - 4.073865e2, 4.107955e2, 4.142332e2, 4.176995e2, 4.211949e2, 4.247195e2, - 4.282736e2, 4.318575e2, 4.354713e2, 4.391154e2, 4.427900e2, 4.464953e2, - 4.502317e2, 4.539993e2, 4.577984e2, 4.616294e2, 4.654923e2, 4.693877e2, - 4.733156e2, 4.772763e2, 4.812703e2, 4.852976e2, 4.893587e2, 4.934537e2, - 4.975830e2, 5.017468e2, 5.059455e2, 5.101793e2, 5.144486e2, 5.187536e2, - 5.230946e2, 5.274719e2, 5.318859e2, 5.363368e2, 5.408249e2, 5.453506e2, - 5.499142e2, 5.545160e2, 5.591563e2, 5.638354e2, 5.685536e2, 5.733114e2, - 5.781089e2, 5.829466e2, 5.878248e2, 5.927438e2, 5.977040e2, 6.027057e2, - 6.077492e2, 6.128350e2, 6.179633e2, 6.231345e2, 6.283489e2, 6.336071e2, - 6.389092e2, 6.442557e2, 6.496469e2, 6.550832e2, 6.605651e2, 6.660928e2, - 6.716668e2, 6.772874e2, 6.829550e2, 6.886701e2, 6.944330e2, 7.002441e2, - 7.061038e2, 7.120126e2, 7.179709e2, 7.239790e2, 7.300373e2, 7.361464e2, - 7.423066e2, 7.485183e2, 7.547820e2, 7.610981e2, 7.674671e2, 7.738894e2, - 7.803654e2, 7.868957e2, 7.934805e2, 8.001205e2, 8.068160e2, 8.135676e2, - 8.203756e2, 8.272407e2, 8.341631e2, 8.411435e2, 8.481824e2, 8.552801e2, - 8.624372e2, 8.696542e2, 8.769316e2, 8.842699e2, 8.916696e2, 8.991312e2, - 9.066553e2, 9.142423e2, 9.218928e2, 9.296074e2, 9.373865e2, 9.452307e2, - 9.531405e2, 9.611165e2, 9.691593e2, 9.772694e2, 9.854473e2, 9.936937e2, - 1.002009e3, 1.010394e3, 1.018849e3, 1.027375e3, 1.035972e3, 1.044641e3, - 1.053383e3, 1.062198e3, 1.071087e3, 1.080050e3, 1.089088e3, 1.098201e3, - 1.107391e3, 1.116658e3, 1.126002e3, 1.135425e3, 1.144926e3, 1.154507e3, - 1.164168e3, 1.173910e3, 1.183734e3, 1.193639e3, 1.203628e3, 1.213700e3, - 1.223857e3, 1.234098e3, 1.244425e3, 1.254839e3, 1.265339e3, 1.275928e3, - 1.286605e3, 1.297372e3, 1.308228e3, 1.319176e3, 1.330215e3, 1.341346e3, - 1.352571e3, 1.363889e3, 1.375303e3, 1.386811e3, 1.398416e3, 1.410118e3, - 1.421919e3, 1.433817e3, 1.445816e3, 1.457915e3, 1.470115e3, 1.482417e3, - 1.494822e3, 1.507331e3, 1.519944e3, 1.532663e3, 1.545489e3, 1.558422e3, - 1.571463e3, 1.584613e3, 1.597874e3, 1.611245e3, 1.624728e3, 1.638324e3, - 1.652034e3, 1.665858e3, 1.679798e3, 1.693855e3, 1.708030e3, 1.722323e3, - 1.736735e3, 1.751268e3, 1.765923e3, 1.780701e3, 1.795602e3, 1.810628e3, - 1.825780e3, 1.841058e3, 1.856464e3, 1.871999e3, 1.887665e3, 1.903461e3, - 1.919389e3, 1.935451e3, 1.951647e3, 1.967979e3, 1.984447e3, 2.001053e3, - 2.017798e3, 2.034684e3, 2.051710e3, 2.068879e3, 2.086192e3, 2.103650e3, - 2.121253e3, 2.139004e3, 2.156904e3, 2.174953e3, 2.193153e3, 2.211506e3, - 2.230012e3, 2.248673e3, 2.267490e3, 2.286465e3, 2.305599e3, 2.324892e3, - 2.344347e3, 2.363965e3, 2.383747e3, 2.403695e3, 2.423809e3, 2.444092e3, - 2.464545e3, 2.485168e3, 2.505965e3, 2.526935e3, 2.548081e3, 2.569403e3, - 2.590904e3, 2.612586e3, 2.634448e3, 2.656494e3, 2.678723e3, 2.701139e3, - 2.723743e3, 2.746536e3, 2.769519e3, 2.792695e3, 2.816065e3, 2.839630e3, - 2.863392e3, 2.887354e3, 2.911515e3, 2.935879e3, 2.960447e3, 2.985221e3, - 3.010202e3, 3.035391e3, 3.060792e3, 3.086405e3, 3.112233e3, 3.138276e3, - 3.164538e3, 3.191019e3, 3.217722e3, 3.244649e3, 3.271800e3, 3.299179e3, - 3.326787e3, 3.354626e3, 3.382698e3, 3.411005e3, 3.439549e3, 3.468332e3, - 3.497355e3, 3.526622e3, 3.556133e3, 3.585891e3, 3.615898e3, 3.646157e3, - 3.676668e3, 3.707435e3, 3.738460e3, 3.769744e3, 3.801290e3, 3.833099e3, - 3.865175e3, 3.897520e3, 3.930135e3, 3.963023e3, 3.996186e3, 4.029627e3, - 4.063347e3, 4.097350e3, 4.131637e3, 4.166211e3, 4.201075e3, 4.236230e3, - 4.271679e3, 4.307425e3, 4.343471e3, 4.379817e3, 4.416468e3, 4.453426e3, - 4.490693e3, 4.528272e3, 4.566165e3, 4.604375e3, 4.642906e3, 4.681758e3, - 4.720936e3, 4.760441e3, 4.800277e3, 4.840447e3, 4.880952e3, 4.921797e3, - 4.962983e3, 5.004514e3, 5.046393e3, 5.088622e3, 5.131204e3, 5.174143e3, - 5.217441e3, 5.261101e3, 5.305127e3, 5.349521e3, 5.394287e3, 5.439427e3, - 5.484945e3, 5.530844e3, 5.577127e3, 5.623797e3, 5.670858e3, 5.718312e3, - 5.766164e3, 5.814416e3, 5.863072e3, 5.912135e3, 5.961609e3, 6.011496e3, - 6.061802e3, 6.112528e3, 6.163678e3, 6.215257e3, 6.267267e3, 6.319712e3, - 6.372597e3, 6.425924e3, 6.479697e3, 6.533920e3, 6.588597e3, 6.643731e3, - 6.699327e3, 6.755388e3, 6.811918e3, 6.868921e3, 6.926401e3, 6.984362e3, - 7.042809e3, 7.101744e3, 7.161172e3, 7.221098e3, 7.281525e3, 7.342458e3, - 7.403901e3, 7.465858e3, 7.528334e3, 7.591332e3, 7.654857e3, 7.718914e3, - 7.783507e3, 7.848641e3, 7.914319e3, 7.980548e3, 8.047330e3, 8.114671e3, - 8.182576e3, 8.251049e3, 8.320095e3, 8.389719e3, 8.459926e3, 8.530719e3, - 8.602106e3, 8.674090e3, 8.746676e3, 8.819869e3, 8.893675e3, 8.968099e3, - 9.043145e3, 9.118820e3, 9.195127e3, 9.272074e3, 9.349664e3, 9.427903e3, - 9.506797e3, 9.586352e3, 9.666572e3, 9.747463e3, 9.829031e3, 9.911282e3, - 9.994221e3, 1.007785e4, 1.016219e4, 1.024723e4, 1.033298e4, 1.041944e4, - 1.050664e4, 1.059456e4, 1.068321e4, 1.077261e4, 1.086276e4, 1.095366e4, - 1.104532e4, 1.113775e4, 1.123095e4, 1.132494e4, 1.141970e4, 1.151527e4, - 1.161163e4, 1.170880e4, 1.180678e4, 1.190558e4, 1.200521e4, 1.210567e4, - 1.220697e4, 1.230912e4, 1.241212e4, 1.251599e4, 1.262073e4, 1.272634e4, - 1.283283e4, 1.294022e4, 1.304851e4, 1.315770e4, 1.326780e4, 1.337883e4, - 1.349079e4, 1.360368e4, 1.371752e4, 1.383231e4, 1.394806e4, 1.406478e4, - 1.418247e4, 1.430116e4, 1.442083e4, 1.454151e4, 1.466319e4, 1.478590e4, - 1.490963e4, 1.503439e4, 1.516020e4, 1.528706e4, 1.541499e4, 1.554398e4, - 1.567406e4, 1.580522e4, 1.593748e4, 1.607085e4, 1.620533e4, 1.634094e4, - 1.647768e4, 1.661557e4, 1.675461e4, 1.689482e4, 1.703620e4, 1.717876e4, - 1.732251e4, 1.746747e4, 1.761364e4, 1.776104e4, 1.790966e4, 1.805953e4, - 1.821066e4, 1.836305e4, 1.851671e4, 1.867166e4, 1.882791e4, 1.898547e4, - 1.914434e4, 1.930454e4, 1.946608e4, 1.962898e4, 1.979324e4, 1.995887e4, - 2.012589e4, 2.029431e4, 2.046413e4, 2.063538e4, 2.080806e4, 2.098218e4, - 2.115777e4, 2.133482e4, 2.151335e4, 2.169338e4, 2.187491e4, 2.205796e4, - 2.224255e4, 2.242868e4, 2.261636e4, 2.280562e4, 2.299646e4, 2.318890e4, - 2.338295e4, 2.357862e4, 2.377593e4, 2.397489e4, 2.417552e4, 2.437782e4, - 2.458182e4, 2.478752e4, 2.499495e4, 2.520411e4, 2.541502e4, 2.562770e4, - 2.584215e4, 2.605841e4, 2.627647e4, 2.649635e4, 2.671808e4, 2.694166e4, - 2.700000e4, 2.716711e4, 2.739445e4, 2.762369e4, 2.785485e4, 2.808794e4, - 2.832299e4, 2.850000e4, 2.856000e4, 2.879899e4, 2.903999e4, 2.928300e4, - 2.952804e4, 2.977514e4, 3.002430e4, 3.027555e4, 3.052890e4, 3.078437e4, - 3.104198e4, 3.130174e4, 3.156368e4, 3.182781e4, 3.209415e4, 3.236272e4, - 3.263353e4, 3.290662e4, 3.318198e4, 3.345965e4, 3.373965e4, 3.402199e4, - 3.430669e4, 3.459377e4, 3.488326e4, 3.517517e4, 3.546952e4, 3.576633e4, - 3.606563e4, 3.636743e4, 3.667176e4, 3.697864e4, 3.728808e4, 3.760011e4, - 3.791476e4, 3.823203e4, 3.855196e4, 3.887457e4, 3.919988e4, 3.952791e4, - 3.985869e4, 4.019223e4, 4.052857e4, 4.086771e4, 4.120970e4, 4.155455e4, - 4.190229e4, 4.225293e4, 4.260651e4, 4.296305e4, 4.332257e4, 4.368510e4, - 4.405066e4, 4.441928e4, 4.479099e4, 4.516581e4, 4.554376e4, 4.592488e4, - 4.630919e4, 4.669671e4, 4.708747e4, 4.748151e4, 4.787884e4, 4.827950e4, - 4.868351e4, 4.909090e4, 4.950170e4, 4.991594e4, 5.033364e4, 5.075484e4, - 5.117957e4, 5.160785e4, 5.203971e4, 5.247518e4, 5.291430e4, 5.335710e4, - 5.380360e4, 5.425384e4, 5.470784e4, 5.516564e4, 5.562728e4, 5.609278e4, - 5.656217e4, 5.703549e4, 5.751277e4, 5.799405e4, 5.847935e4, 5.896871e4, - 5.946217e4, 5.995976e4, 6.046151e4, 6.096747e4, 6.147765e4, 6.199211e4, - 6.251086e4, 6.303396e4, 6.356144e4, 6.409333e4, 6.462968e4, 6.517051e4, - 6.571586e4, 6.626579e4, 6.682031e4, 6.737947e4, 6.794331e4, 6.851187e4, - 6.908519e4, 6.966330e4, 7.024626e4, 7.083409e4, 7.142684e4, 7.202455e4, - 7.262726e4, 7.323502e4, 7.384786e4, 7.446583e4, 7.508897e4, 7.571733e4, - 7.635094e4, 7.698986e4, 7.763412e4, 7.828378e4, 7.893887e4, 7.950000e4, - 7.959944e4, 8.026554e4, 8.093721e4, 8.161451e4, 8.229747e4, 8.250000e4, - 8.298615e4, 8.368059e4, 8.438084e4, 8.508695e4, 8.579897e4, 8.651695e4, - 8.724094e4, 8.797098e4, 8.870714e4, 8.944945e4, 9.019798e4, 9.095277e4, - 9.171388e4, 9.248135e4, 9.325525e4, 9.403563e4, 9.482253e4, 9.561602e4, - 9.641615e4, 9.722297e4, 9.803655e4, 9.885694e4, 9.968419e4, 1.005184e5, - 1.013595e5, 1.022077e5, 1.030630e5, 1.039254e5, 1.047951e5, 1.056720e5, - 1.065563e5, 1.074480e5, 1.083471e5, 1.092538e5, 1.101681e5, 1.110900e5, - 1.120196e5, 1.129570e5, 1.139022e5, 1.148554e5, 1.158165e5, 1.167857e5, - 1.177629e5, 1.187484e5, 1.197421e5, 1.207441e5, 1.217545e5, 1.227734e5, - 1.238008e5, 1.248368e5, 1.258814e5, 1.269348e5, 1.279970e5, 1.290681e5, - 1.301482e5, 1.312373e5, 1.323355e5, 1.334429e5, 1.345596e5, 1.356856e5, - 1.368210e5, 1.379660e5, 1.391205e5, 1.402847e5, 1.414586e5, 1.426423e5, - 1.438360e5, 1.450396e5, 1.462533e5, 1.474772e5, 1.487113e5, 1.499558e5, - 1.512106e5, 1.524760e5, 1.537519e5, 1.550385e5, 1.563359e5, 1.576442e5, - 1.589634e5, 1.602936e5, 1.616349e5, 1.629875e5, 1.643514e5, 1.657268e5, - 1.671136e5, 1.685120e5, 1.699221e5, 1.713441e5, 1.727779e5, 1.742237e5, - 1.756817e5, 1.771518e5, 1.786342e5, 1.801291e5, 1.816364e5, 1.831564e5, - 1.846891e5, 1.862346e5, 1.877930e5, 1.893645e5, 1.909491e5, 1.925470e5, - 1.941583e5, 1.957830e5, 1.974214e5, 1.990734e5, 2.007393e5, 2.024191e5, - 2.041130e5, 2.058210e5, 2.075434e5, 2.092801e5, 2.110314e5, 2.127974e5, - 2.145781e5, 2.163737e5, 2.181844e5, 2.200102e5, 2.218512e5, 2.237077e5, - 2.255797e5, 2.274674e5, 2.293709e5, 2.312903e5, 2.332258e5, 2.351775e5, - 2.371455e5, 2.391299e5, 2.411310e5, 2.431488e5, 2.451835e5, 2.472353e5, - 2.493042e5, 2.513904e5, 2.534941e5, 2.556153e5, 2.577544e5, 2.599113e5, - 2.620863e5, 2.642794e5, 2.664910e5, 2.687210e5, 2.709697e5, 2.732372e5, - 2.755237e5, 2.778293e5, 2.801543e5, 2.824986e5, 2.848626e5, 2.872464e5, - 2.896501e5, 2.920740e5, 2.945181e5, 2.969826e5, 2.972000e5, 2.985000e5, - 2.994678e5, 3.019738e5, 3.045008e5, 3.070489e5, 3.096183e5, 3.122093e5, - 3.148219e5, 3.174564e5, 3.201129e5, 3.227916e5, 3.254928e5, 3.282166e5, - 3.309631e5, 3.337327e5, 3.365254e5, 3.393415e5, 3.421812e5, 3.450446e5, - 3.479320e5, 3.508435e5, 3.537795e5, 3.567399e5, 3.597252e5, 3.627354e5, - 3.657708e5, 3.688317e5, 3.719181e5, 3.750304e5, 3.781687e5, 3.813333e5, - 3.845243e5, 3.877421e5, 3.909868e5, 3.942586e5, 3.975578e5, 4.008846e5, - 4.042393e5, 4.076220e5, 4.110331e5, 4.144727e5, 4.179410e5, 4.214384e5, - 4.249651e5, 4.285213e5, 4.321072e5, 4.357231e5, 4.393693e5, 4.430460e5, - 4.467535e5, 4.504920e5, 4.542618e5, 4.580631e5, 4.618963e5, 4.657615e5, - 4.696591e5, 4.735892e5, 4.775523e5, 4.815485e5, 4.855782e5, 4.896416e5, - 4.937390e5, 4.978707e5, 5.020369e5, 5.062381e5, 5.104743e5, 5.147461e5, - 5.190535e5, 5.233971e5, 5.277769e5, 5.321934e5, 5.366469e5, 5.411377e5, - 5.456660e5, 5.502322e5, 5.548366e5, 5.594796e5, 5.641614e5, 5.688824e5, - 5.736429e5, 5.784432e5, 5.832837e5, 5.881647e5, 5.930866e5, 5.980496e5, - 6.030542e5, 6.081006e5, 6.131893e5, 6.183206e5, 6.234948e5, 6.287123e5, - 6.339734e5, 6.392786e5, 6.446282e5, 6.500225e5, 6.554620e5, 6.609470e5, - 6.664779e5, 6.720551e5, 6.776790e5, 6.833499e5, 6.890683e5, 6.948345e5, - 7.006490e5, 7.065121e5, 7.124243e5, 7.183860e5, 7.243976e5, 7.304594e5, - 7.365720e5, 7.427358e5, 7.489511e5, 7.552184e5, 7.615382e5, 7.679109e5, - 7.743369e5, 7.808167e5, 7.873507e5, 7.939393e5, 8.005831e5, 8.072825e5, - 8.140380e5, 8.208500e5, 8.277190e5, 8.346455e5, 8.416299e5, 8.486728e5, - 8.557746e5, 8.629359e5, 8.701570e5, 8.774387e5, 8.847812e5, 8.921852e5, - 8.996511e5, 9.071795e5, 9.147709e5, 9.224259e5, 9.301449e5, 9.379285e5, - 9.457772e5, 9.536916e5, 9.616723e5, 9.697197e5, 9.778344e5, 9.860171e5, - 9.942682e5, 1.002588e6, 1.010978e6, 1.019438e6, 1.027969e6, 1.036571e6, - 1.045245e6, 1.053992e6, 1.062812e6, 1.071706e6, 1.080674e6, 1.089717e6, - 1.098836e6, 1.108032e6, 1.117304e6, 1.126654e6, 1.136082e6, 1.145588e6, - 1.155175e6, 1.164842e6, 1.174589e6, 1.184418e6, 1.194330e6, 1.204324e6, - 1.214402e6, 1.224564e6, 1.234812e6, 1.245145e6, 1.255564e6, 1.266071e6, - 1.276666e6, 1.287349e6, 1.298122e6, 1.308985e6, 1.319938e6, 1.330984e6, - 1.342122e6, 1.353353e6, 1.364678e6, 1.376098e6, 1.387613e6, 1.399225e6, - 1.410934e6, 1.422741e6, 1.434646e6, 1.446652e6, 1.458758e6, 1.470965e6, - 1.483274e6, 1.495686e6, 1.508202e6, 1.520823e6, 1.533550e6, 1.546383e6, - 1.559323e6, 1.572372e6, 1.585530e6, 1.598797e6, 1.612176e6, 1.625667e6, - 1.639271e6, 1.652989e6, 1.666821e6, 1.680770e6, 1.694834e6, 1.709017e6, - 1.723318e6, 1.737739e6, 1.752281e6, 1.766944e6, 1.781731e6, 1.796640e6, - 1.811675e6, 1.826835e6, 1.842122e6, 1.857538e6, 1.873082e6, 1.888756e6, - 1.904561e6, 1.920499e6, 1.936570e6, 1.952776e6, 1.969117e6, 1.985595e6, - 2.002210e6, 2.018965e6, 2.035860e6, 2.052897e6, 2.070076e6, 2.087398e6, - 2.104866e6, 2.122480e6, 2.140241e6, 2.158151e6, 2.176211e6, 2.194421e6, - 2.212785e6, 2.231302e6, 2.249973e6, 2.268802e6, 2.287787e6, 2.306932e6, - 2.326237e6, 2.345703e6, 2.365332e6, 2.385126e6, 2.405085e6, 2.425211e6, - 2.445505e6, 2.465970e6, 2.486605e6, 2.507414e6, 2.528396e6, 2.549554e6, - 2.570889e6, 2.592403e6, 2.614096e6, 2.635971e6, 2.658030e6, 2.680272e6, - 2.702701e6, 2.725318e6, 2.748124e6, 2.771121e6, 2.794310e6, 2.817693e6, - 2.841272e6, 2.865048e6, 2.889023e6, 2.913199e6, 2.937577e6, 2.962159e6, - 2.986947e6, 3.011942e6, 3.037147e6, 3.062562e6, 3.088190e6, 3.114032e6, - 3.140091e6, 3.166368e6, 3.192864e6, 3.219583e6, 3.246525e6, 3.273692e6, - 3.301087e6, 3.328711e6, 3.356566e6, 3.384654e6, 3.412978e6, 3.441538e6, - 3.470337e6, 3.499377e6, 3.528661e6, 3.558189e6, 3.587965e6, 3.617989e6, - 3.648265e6, 3.678794e6, 3.709579e6, 3.740621e6, 3.771924e6, 3.803488e6, - 3.835316e6, 3.867410e6, 3.899773e6, 3.932407e6, 3.965314e6, 3.998497e6, - 4.031957e6, 4.065697e6, 4.099719e6, 4.134026e6, 4.168620e6, 4.203504e6, - 4.238679e6, 4.274149e6, 4.309916e6, 4.345982e6, 4.382350e6, 4.419022e6, - 4.456001e6, 4.493290e6, 4.530890e6, 4.568805e6, 4.607038e6, 4.645590e6, - 4.684465e6, 4.723666e6, 4.763194e6, 4.803053e6, 4.843246e6, 4.883775e6, - 4.924643e6, 4.965853e6, 5.007408e6, 5.049311e6, 5.091564e6, 5.134171e6, - 5.177135e6, 5.220458e6, 5.264143e6, 5.308195e6, 5.352614e6, 5.397406e6, - 5.442572e6, 5.488116e6, 5.534042e6, 5.580351e6, 5.627049e6, 5.674137e6, - 5.721619e6, 5.769498e6, 5.817778e6, 5.866462e6, 5.915554e6, 5.965056e6, - 6.014972e6, 6.065307e6, 6.116062e6, 6.167242e6, 6.218851e6, 6.270891e6, - 6.323367e6, 6.376282e6, 6.429639e6, 6.483443e6, 6.537698e6, 6.592406e6, - 6.647573e6, 6.703200e6, 6.759294e6, 6.815857e6, 6.872893e6, 6.930406e6, - 6.988401e6, 7.046881e6, 7.105850e6, 7.165313e6, 7.225274e6, 7.285736e6, - 7.346704e6, 7.408182e6, 7.470175e6, 7.532687e6, 7.595721e6, 7.659283e6, - 7.723377e6, 7.788008e6, 7.853179e6, 7.918896e6, 7.985162e6, 8.051983e6, - 8.119363e6, 8.187308e6, 8.255820e6, 8.324906e6, 8.394570e6, 8.464817e6, - 8.535652e6, 8.607080e6, 8.679105e6, 8.751733e6, 8.824969e6, 8.898818e6, - 8.973284e6, 9.048374e6, 9.124092e6, 9.200444e6, 9.277435e6, 9.355070e6, - 9.433354e6, 9.512294e6, 9.591895e6, 9.672161e6, 9.753099e6, 9.834715e6, - 9.917013e6, 1.000000e7, 1.008368e7, 1.016806e7, 1.025315e7, 1.033895e7, - 1.042547e7, 1.051271e7, 1.060068e7, 1.068939e7, 1.077884e7, 1.086904e7, - 1.095999e7, 1.105171e7, 1.114419e7, 1.123745e7, 1.133148e7, 1.142631e7, - 1.152193e7, 1.161834e7, 1.171557e7, 1.181360e7, 1.191246e7, 1.201215e7, - 1.211267e7, 1.221403e7, 1.231624e7, 1.241930e7, 1.252323e7, 1.262802e7, - 1.273370e7, 1.284025e7, 1.294770e7, 1.305605e7, 1.316531e7, 1.327548e7, - 1.338657e7, 1.349859e7, 1.361155e7, 1.372545e7, 1.384031e7, 1.395612e7, - 1.407291e7, 1.419068e7, 1.430943e7, 1.442917e7, 1.454991e7, 1.467167e7, - 1.479444e7, 1.491825e7, 1.504309e7, 1.516897e7, 1.529590e7, 1.542390e7, - 1.555297e7, 1.568312e7, 1.581436e7, 1.594670e7, 1.608014e7, 1.621470e7, - 1.635039e7, 1.648721e7, 1.662518e7, 1.676430e7, 1.690459e7, 1.704605e7, - 1.718869e7, 1.733253e7, 1.747757e7, 1.762383e7, 1.777131e7, 1.792002e7, - 1.806998e7, 1.822119e7, 1.837367e7, 1.852742e7, 1.868246e7, 1.883880e7, - 1.899644e7, 1.915541e7, 1.931570e7, 1.947734e7, 1.964033e7]) +GROUP_STRUCTURES["CASMO-2"] = np.array([0.0, 6.25e-1, 2.0e7]) +GROUP_STRUCTURES["CASMO-4"] = np.array([0.0, 6.25e-1, 5.53e3, 8.21e5, 2.0e7]) +GROUP_STRUCTURES["CASMO-8"] = np.array( + [0.0, 5.8e-2, 1.4e-1, 2.8e-1, 6.25e-1, 4.0, 5.53e3, 8.21e5, 2.0e7] +) +GROUP_STRUCTURES["CASMO-16"] = np.array( + [ + 0.0, + 3.0e-2, + 5.8e-2, + 1.4e-1, + 2.8e-1, + 3.5e-1, + 6.25e-1, + 8.5e-1, + 9.72e-1, + 1.02, + 1.097, + 1.15, + 1.3, + 4.0, + 5.53e3, + 8.21e5, + 2.0e7, + ] +) +GROUP_STRUCTURES["CASMO-25"] = np.array( + [ + 0.0, + 3.0e-2, + 5.8e-2, + 1.4e-1, + 2.8e-1, + 3.5e-1, + 6.25e-1, + 9.72e-1, + 1.02, + 1.097, + 1.15, + 1.855, + 4.0, + 9.877, + 1.5968e1, + 1.4873e2, + 5.53e3, + 9.118e3, + 1.11e5, + 5.0e5, + 8.21e5, + 1.353e6, + 2.231e6, + 3.679e6, + 6.0655e6, + 2.0e7, + ] +) +GROUP_STRUCTURES["CASMO-40"] = np.array( + [ + 0.0, + 1.5e-2, + 3.0e-2, + 4.2e-2, + 5.8e-2, + 8.0e-2, + 1.0e-1, + 1.4e-1, + 1.8e-1, + 2.2e-1, + 2.8e-1, + 3.5e-1, + 6.25e-1, + 8.5e-1, + 9.5e-1, + 9.72e-1, + 1.02, + 1.097, + 1.15, + 1.3, + 1.5, + 1.855, + 2.1, + 2.6, + 3.3, + 4.0, + 9.877, + 1.5968e1, + 2.77e1, + 4.8052e1, + 1.4873e2, + 5.53e3, + 9.118e3, + 1.11e5, + 5.0e5, + 8.21e5, + 1.353e6, + 2.231e6, + 3.679e6, + 6.0655e6, + 2.0e7, + ] +) +GROUP_STRUCTURES["VITAMIN-J-42"] = np.array( + [ + 1.0e3, + 10.0e3, + 20.0e3, + 30.0e3, + 45.0e3, + 60.0e3, + 70.0e3, + 75.0e3, + 0.1e6, + 0.15e6, + 0.2e6, + 0.3e6, + 0.4e6, + 0.45e6, + 0.51e6, + 0.512e6, + 0.6e6, + 0.7e6, + 0.8e6, + 1.0e6, + 1.33e6, + 1.34e6, + 1.5e6, + 1.66e6, + 2.0e6, + 2.5e6, + 3.0e6, + 3.5e6, + 4.0e6, + 4.5e6, + 5.0e6, + 5.5e6, + 6.0e6, + 6.5e6, + 7.0e6, + 7.5e6, + 8.0e6, + 10.0e6, + 12.0e6, + 14.0e6, + 20.0e6, + 30.0e6, + 50.0e6, + ] +) +GROUP_STRUCTURES["SCALE-44"] = np.array( + [ + 1e-5, + 3.0e-3, + 7.5e-3, + 1.0e-2, + 2.53e-2, + 3.0e-2, + 4.0e-2, + 5e-2, + 7.0e-2, + 1.0e-1, + 1.5e-1, + 2.0e-1, + 2.25e-1, + 2.5e-1, + 2.75e-1, + 3.25e-1, + 3.5e-1, + 3.75e-1, + 4.0e-1, + 6.25e-1, + 1.0, + 1.77, + 3.0, + 4.75, + 6.0, + 8.1, + 1.0e1, + 3.0e1, + 1.0e2, + 5.5e2, + 3.0e3, + 1.7e4, + 2.5e4, + 1.0e5, + 4.0e5, + 9.0e5, + 1.4e6, + 1.85e6, + 2.354e6, + 2.479e6, + 3.0e6, + 4.8e6, + 6.434e6, + 8.1873e6, + 2.0e7, + ] +) +GROUP_STRUCTURES["MPACT-51"] = np.array( + [ + 0.0, + 1.0e-2, + 3.0e-2, + 4.0e-2, + 6.0e-2, + 8.0e-2, + 1.0e-1, + 1.5e-1, + 2.0e-1, + 2.75e-1, + 3.5e-1, + 5.0e-1, + 6.25e-1, + 7.5e-1, + 9.25e-1, + 9.75e-1, + 1.010, + 1.080, + 1.130, + 1.175, + 1.250, + 1.450, + 1.860, + 2.470, + 3.730, + 4.700, + 5.000, + 5.400, + 6.250, + 7.150, + 8.100, + 1.19e1, + 1.44e1, + 3.0e1, + 4.83e1, + 7.6e1, + 1.43e2, + 3.05e2, + 9.5e2, + 2.25e3, + 9.5e3, + 2.0e4, + 5.0e4, + 7.3e4, + 2.0e5, + 4.92e5, + 8.2e5, + 1.356e6, + 2.354e6, + 4.304e6, + 6.434e6, + 2.0e7, + ] +) +GROUP_STRUCTURES["MPACT-60"] = np.array( + [ + 0.0, + 1.0e-2, + 3.0e-2, + 4.0e-2, + 6.0e-2, + 8.0e-2, + 1.0e-1, + 1.5e-1, + 2.0e-1, + 2.75e-1, + 3.5e-1, + 5.0e-1, + 6.25e-1, + 7.5e-1, + 9.25e-1, + 9.75e-1, + 1.01, + 1.08, + 1.13, + 1.175, + 1.25, + 1.45, + 1.86, + 2.47, + 3.73, + 4.7, + 5.0, + 5.4, + 6.25, + 7.15, + 8.1, + 1.19e1, + 1.44e1, + 3.0e1, + 4.83e1, + 7.6e1, + 1.43e2, + 2.095e2, + 3.05e2, + 6.7e2, + 9.5e2, + 1.55e3, + 2.25e3, + 3.9e3, + 9.5e3, + 1.3e4, + 2.0e4, + 3.0e4, + 5.0e4, + 7.3e4, + 1.283e5, + 2.0e5, + 3.3e5, + 4.92e5, + 6.7e5, + 8.2e5, + 1.356e6, + 2.354e6, + 4.304e6, + 6.434e6, + 2.0e7, + ] +) +GROUP_STRUCTURES["MPACT-69"] = np.array( + [ + 0.0, + 1.0e-2, + 3.0e-2, + 4.0e-2, + 6.0e-2, + 8.0e-2, + 9.0e-2, + 1.0e-1, + 1.25e-1, + 1.5e-1, + 1.75e-1, + 2.0e-1, + 2.25e-1, + 2.5e-1, + 2.75e-1, + 3.0e-1, + 3.25e-1, + 3.5e-1, + 3.75e-1, + 4.0e-1, + 4.5e-1, + 5.0e-1, + 5.5e-1, + 6.0e-1, + 6.25e-1, + 6.5e-1, + 7.5e-1, + 8.5e-1, + 9.25e-1, + 9.75e-1, + 1.01, + 1.08, + 1.13, + 1.175, + 1.25, + 1.45, + 1.86, + 2.47, + 3.0, + 3.73, + 4.7, + 5.0, + 5.4, + 6.25, + 7.15, + 8.1, + 1.0e1, + 1.19e1, + 1.44e1, + 3.0e1, + 4.83e1, + 7.6e1, + 1.43e2, + 3.05e2, + 5.5e2, + 9.5e2, + 2.25e3, + 3.9e3, + 9.5e3, + 2.0e4, + 5.0e4, + 7.3e4, + 2.0e5, + 4.92e5, + 8.2e5, + 1.356e6, + 2.354e6, + 4.304e6, + 6.434e6, + 2.0e7, + ] +) +GROUP_STRUCTURES["CASMO-70"] = np.array( + [ + 0.0, + 5.0e-3, + 1.0e-2, + 1.5e-2, + 2.0e-2, + 2.5e-2, + 3.0e-2, + 3.5e-2, + 4.2e-2, + 5.0e-2, + 5.8e-2, + 6.7e-2, + 8.0e-2, + 1.0e-1, + 1.4e-1, + 1.8e-1, + 2.2e-1, + 2.5e-1, + 2.8e-1, + 3.0e-1, + 3.2e-1, + 3.5e-1, + 4.0e-1, + 5.0e-1, + 6.25e-1, + 7.8e-1, + 8.5e-1, + 9.1e-1, + 9.5e-1, + 9.72e-1, + 9.96e-1, + 1.02, + 1.045, + 1.071, + 1.097, + 1.123, + 1.15, + 1.3, + 1.5, + 1.855, + 2.1, + 2.6, + 3.3, + 4.0, + 9.877, + 1.5968e1, + 2.77e1, + 4.8052e1, + 7.5501e1, + 1.4873e2, + 3.6726e2, + 9.069e2, + 1.4251e3, + 2.2395e3, + 3.5191e3, + 5.53e3, + 9.118e3, + 1.503e4, + 2.478e4, + 4.085e4, + 6.734e4, + 1.11e5, + 1.83e5, + 3.025e5, + 5.0e5, + 8.21e5, + 1.353e6, + 2.231e6, + 3.679e6, + 6.0655e6, + 2.0e7, + ] +) +GROUP_STRUCTURES["XMAS-172"] = np.array( + [ + 1.00001e-05, + 3.00000e-03, + 5.00000e-03, + 6.90000e-03, + 1.00000e-02, + 1.50000e-02, + 2.00000e-02, + 2.50000e-02, + 3.00000e-02, + 3.50000e-02, + 4.20000e-02, + 5.00000e-02, + 5.80000e-02, + 6.70000e-02, + 7.70000e-02, + 8.00000e-02, + 9.50000e-02, + 1.00001e-01, + 1.15000e-01, + 1.34000e-01, + 1.40000e-01, + 1.60000e-01, + 1.80000e-01, + 1.89000e-01, + 2.20000e-01, + 2.48000e-01, + 2.80000e-01, + 3.00000e-01, + 3.14500e-01, + 3.20000e-01, + 3.50000e-01, + 3.91000e-01, + 4.00000e-01, + 4.33000e-01, + 4.85000e-01, + 5.00000e-01, + 5.40000e-01, + 6.25000e-01, + 7.05000e-01, + 7.80000e-01, + 7.90000e-01, + 8.50000e-01, + 8.60000e-01, + 9.10000e-01, + 9.30000e-01, + 9.50000e-01, + 9.72000e-01, + 9.86000e-01, + 9.96000e-01, + 1.02000e00, + 1.03500e00, + 1.04500e00, + 1.07100e00, + 1.09700e00, + 1.11000e00, + 1.12535e00, + 1.15000e00, + 1.17000e00, + 1.23500e00, + 1.30000e00, + 1.33750e00, + 1.37000e00, + 1.44498e00, + 1.47500e00, + 1.50000e00, + 1.59000e00, + 1.67000e00, + 1.75500e00, + 1.84000e00, + 1.93000e00, + 2.02000e00, + 2.10000e00, + 2.13000e00, + 2.36000e00, + 2.55000e00, + 2.60000e00, + 2.72000e00, + 2.76792e00, + 3.30000e00, + 3.38075e00, + 4.00000e00, + 4.12925e00, + 5.04348e00, + 5.34643e00, + 6.16012e00, + 7.52398e00, + 8.31529e00, + 9.18981e00, + 9.90555e00, + 1.12245e01, + 1.37096e01, + 1.59283e01, + 1.94548e01, + 2.26033e01, + 2.49805e01, + 2.76077e01, + 3.05113e01, + 3.37201e01, + 3.72665e01, + 4.01690e01, + 4.55174e01, + 4.82516e01, + 5.15780e01, + 5.55951e01, + 6.79041e01, + 7.56736e01, + 9.16609e01, + 1.36742e02, + 1.48625e02, + 2.03995e02, + 3.04325e02, + 3.71703e02, + 4.53999e02, + 6.77287e02, + 7.48518e02, + 9.14242e02, + 1.01039e03, + 1.23410e03, + 1.43382e03, + 1.50733e03, + 2.03468e03, + 2.24867e03, + 3.35463e03, + 3.52662e03, + 5.00451e03, + 5.53084e03, + 7.46586e03, + 9.11882e03, + 1.11378e04, + 1.50344e04, + 1.66156e04, + 2.47875e04, + 2.73944e04, + 2.92830e04, + 3.69786e04, + 4.08677e04, + 5.51656e04, + 6.73795e04, + 8.22975e04, + 1.11090e05, + 1.22773e05, + 1.83156e05, + 2.47235e05, + 2.73237e05, + 3.01974e05, + 4.07622e05, + 4.50492e05, + 4.97871e05, + 5.50232e05, + 6.08101e05, + 8.20850e05, + 9.07180e05, + 1.00259e06, + 1.10803e06, + 1.22456e06, + 1.35335e06, + 1.65299e06, + 2.01897e06, + 2.23130e06, + 2.46597e06, + 3.01194e06, + 3.67879e06, + 4.49329e06, + 5.48812e06, + 6.06531e06, + 6.70320e06, + 8.18731e06, + 1.00000e07, + 1.16183e07, + 1.38403e07, + 1.49182e07, + 1.73325e07, + 1.96403e07, + ] +) +GROUP_STRUCTURES["VITAMIN-J-175"] = np.array( + [ + 1.0000e-5, + 1.0000e-1, + 4.1399e-1, + 5.3158e-1, + 6.8256e-1, + 8.7643e-1, + 1.1253, + 1.4450, + 1.8554, + 2.3824, + 3.0590, + 3.9279, + 5.0435, + 6.4759, + 8.3153, + 1.0677e1, + 1.3710e1, + 1.7604e1, + 2.2603e1, + 2.9023e1, + 3.7266e1, + 4.7851e1, + 6.1442e1, + 7.8893e1, + 1.0130e2, + 1.3007e2, + 1.6702e2, + 2.1445e2, + 2.7536e2, + 3.5358e2, + 4.5400e2, + 5.8295e2, + 7.4852e2, + 9.6112e2, + 1.2341e3, + 1.5846e3, + 2.0347e3, + 2.2487e3, + 2.4852e3, + 2.6126e3, + 2.7465e3, + 3.0354e3, + 3.3546e3, + 3.7074e3, + 4.3074e3, + 5.5308e3, + 7.1017e3, + 9.1188e3, + 1.0595e4, + 1.1709e4, + 1.5034e4, + 1.9304e4, + 2.1875e4, + 2.3579e4, + 2.4176e4, + 2.4788e4, + 2.6058e4, + 2.7000e4, + 2.8501e4, + 3.1828e4, + 3.4307e4, + 4.0868e4, + 4.6309e4, + 5.2475e4, + 5.6562e4, + 6.7380e4, + 7.2024e4, + 7.9499e4, + 8.2503e4, + 8.6517e4, + 9.8036e4, + 1.1109e5, + 1.1679e5, + 1.2277e5, + 1.2907e5, + 1.3569e5, + 1.4264e5, + 1.4996e5, + 1.5764e5, + 1.6573e5, + 1.7422e5, + 1.8316e5, + 1.9255e5, + 2.0242e5, + 2.1280e5, + 2.2371e5, + 2.3518e5, + 2.4724e5, + 2.7324e5, + 2.8725e5, + 2.9452e5, + 2.9721e5, + 2.9849e5, + 3.0197e5, + 3.3373e5, + 3.6883e5, + 3.8774e5, + 4.0762e5, + 4.5049e5, + 4.9787e5, + 5.2340e5, + 5.5023e5, + 5.7844e5, + 6.0810e5, + 6.3928e5, + 6.7206e5, + 7.0651e5, + 7.4274e5, + 7.8082e5, + 8.2085e5, + 8.6294e5, + 9.0718e5, + 9.6167e5, + 1.0026e6, + 1.1080e6, + 1.1648e6, + 1.2246e6, + 1.2874e6, + 1.3534e6, + 1.4227e6, + 1.4957e6, + 1.5724e6, + 1.6530e6, + 1.7377e6, + 1.8268e6, + 1.9205e6, + 2.0190e6, + 2.1225e6, + 2.2313e6, + 2.3069e6, + 2.3457e6, + 2.3653e6, + 2.3851e6, + 2.4660e6, + 2.5924e6, + 2.7253e6, + 2.8650e6, + 3.0119e6, + 3.1664e6, + 3.3287e6, + 3.6788e6, + 4.0657e6, + 4.4933e6, + 4.7237e6, + 4.9658e6, + 5.2205e6, + 5.4881e6, + 5.7695e6, + 6.0653e6, + 6.3763e6, + 6.5924e6, + 6.7032e6, + 7.0469e6, + 7.4082e6, + 7.7880e6, + 8.1873e6, + 8.6071e6, + 9.0484e6, + 9.5123e6, + 1.0000e7, + 1.0513e7, + 1.1052e7, + 1.1618e7, + 1.2214e7, + 1.2523e7, + 1.2840e7, + 1.3499e7, + 1.3840e7, + 1.4191e7, + 1.4550e7, + 1.4918e7, + 1.5683e7, + 1.6487e7, + 1.6905e7, + 1.7332e7, + 1.9640e7, + ] +) +GROUP_STRUCTURES["SCALE-252"] = np.array( + [ + 0.0, + 1.0e-4, + 5.0e-4, + 7.5e-4, + 1.0e-3, + 1.2e-3, + 1.5e-3, + 2.0e-3, + 2.5e-3, + 3.0e-3, + 4.0e-3, + 5.0e-3, + 7.5e-3, + 1.0e-2, + 2.53e-2, + 3.0e-2, + 4.0e-2, + 5.0e-2, + 6.0e-2, + 7.0e-2, + 8.0e-2, + 9.0e-2, + 1.0e-1, + 1.25e-1, + 1.5e-1, + 1.75e-1, + 2.0e-1, + 2.25e-1, + 2.5e-1, + 2.75e-1, + 3.0e-1, + 3.25e-1, + 3.5e-1, + 3.75e-1, + 4.0e-1, + 4.5e-1, + 5.0e-1, + 5.5e-1, + 6.0e-1, + 6.25e-1, + 6.5e-1, + 7.0e-1, + 7.5e-1, + 8.0e-1, + 8.5e-1, + 9.0e-1, + 9.25e-1, + 9.5e-1, + 9.75e-1, + 1.0, + 1.01, + 1.02, + 1.03, + 1.04, + 1.05, + 1.06, + 1.07, + 1.08, + 1.09, + 1.1, + 1.11, + 1.12, + 1.13, + 1.14, + 1.15, + 1.175, + 1.2, + 1.225, + 1.25, + 1.3, + 1.35, + 1.4, + 1.45, + 1.5, + 1.59, + 1.68, + 1.77, + 1.86, + 1.94, + 2.0, + 2.12, + 2.21, + 2.3, + 2.38, + 2.47, + 2.57, + 2.67, + 2.77, + 2.87, + 2.97, + 3.0, + 3.1, + 3.2, + 3.5, + 3.73, + 4.1, + 4.7, + 5.0, + 5.4, + 6.0, + 6.25, + 6.5, + 6.75, + 6.875, + 7.0, + 7.15, + 8.1, + 9.1, + 1.0e1, + 1.15e1, + 1.19e1, + 1.29e1, + 1.44e1, + 1.6e1, + 1.7e1, + 1.85e1, + 1.94e1, + 2.0e1, + 2.05e1, + 2.12e1, + 2.175e1, + 2.25e1, + 2.5e1, + 2.75e1, + 3.0e1, + 3.125e1, + 3.175e1, + 3.325e1, + 3.375e1, + 3.5e1, + 3.55e1, + 3.6e1, + 3.7e1, + 3.713e1, + 3.727e1, + 3.763e1, + 3.8e1, + 3.91e1, + 3.96e1, + 4.1e1, + 4.24e1, + 4.4e1, + 4.52e1, + 4.83e1, + 5.06e1, + 5.34e1, + 5.8e1, + 6.1e1, + 6.3e1, + 6.5e1, + 6.75e1, + 7.2e1, + 7.6e1, + 8.0e1, + 8.17e1, + 9.0e1, + 9.7e1, + 1.012e2, + 1.05e2, + 1.08e2, + 1.13e2, + 1.16e2, + 1.175e2, + 1.19e2, + 1.22e2, + 1.43e2, + 1.7e2, + 1.8e2, + 1.877e2, + 1.885e2, + 1.915e2, + 1.93e2, + 2.02e2, + 2.074e2, + 2.095e2, + 2.2e2, + 2.4e2, + 2.85e2, + 3.05e2, + 5.5e2, + 6.7e2, + 6.83e2, + 9.5e2, + 1.15e3, + 1.5e3, + 1.55e3, + 1.8e3, + 2.2e3, + 2.25e3, + 2.5e3, + 3.0e3, + 3.74e3, + 3.9e3, + 5.7e3, + 8.03e3, + 9.5e3, + 1.3e4, + 1.7e4, + 2.0e4, + 3.0e4, + 4.5e4, + 5.0e4, + 5.2e4, + 6.0e4, + 7.3e4, + 7.5e4, + 8.2e4, + 8.5e4, + 1.0e5, + 1.283e5, + 1.49e5, + 2.0e5, + 2.7e5, + 3.3e5, + 4.0e5, + 4.2e5, + 4.4e5, + 4.7e5, + 4.92e5, + 5.5e5, + 5.73e5, + 6.0e5, + 6.7e5, + 6.79e5, + 7.5e5, + 8.2e5, + 8.611e5, + 8.75e5, + 9.0e5, + 9.2e5, + 1.01e6, + 1.1e6, + 1.2e6, + 1.25e6, + 1.317e6, + 1.356e6, + 1.4e6, + 1.5e6, + 1.85e6, + 2.354e6, + 2.479e6, + 3.0e6, + 4.304e6, + 4.8e6, + 6.434e6, + 8.187e6, + 1.0e7, + 1.284e7, + 1.384e7, + 1.455e7, + 1.568e7, + 1.733e7, + 2.0e7, + ] +) +GROUP_STRUCTURES["TRIPOLI-315"] = np.array( + [ + 1.0e-5, + 1.1e-4, + 3.000e-3, + 5.500e-3, + 1.000e-2, + 1.500e-2, + 2.000e-2, + 3.000e-2, + 3.200e-2, + 3.238e-2, + 4.300e-2, + 5.900e-2, + 7.700e-2, + 9.500e-2, + 1.000e-1, + 1.150e-1, + 1.340e-1, + 1.600e-1, + 1.890e-1, + 2.200e-1, + 2.480e-1, + 2.825e-1, + 3.145e-1, + 3.520e-1, + 3.910e-1, + 4.140e-1, + 4.330e-1, + 4.850e-1, + 5.316e-1, + 5.400e-1, + 6.250e-1, + 6.826e-1, + 7.050e-1, + 7.900e-1, + 8.600e-1, + 8.764e-1, + 9.300e-1, + 9.860e-1, + 1.010, + 1.035, + 1.070, + 1.080, + 1.090, + 1.110, + 1.125, + 1.170, + 1.235, + 1.305, + 1.370, + 1.440, + 1.445, + 1.510, + 1.590, + 1.670, + 1.755, + 1.840, + 1.855, + 1.930, + 2.020, + 2.130, + 2.360, + 2.372, + 2.768, + 3.059, + 3.381, + 3.928, + 4.129, + 4.470, + 4.670, + 5.043, + 5.623, + 6.160, + 6.476, + 7.079, + 7.524, + 7.943, + 8.315, + 8.913, + 9.190, + 1.000e1, + 1.068e1, + 1.122e1, + 1.259e1, + 1.371e1, + 1.523e1, + 1.674e1, + 1.760e1, + 1.903e1, + 2.045e1, + 2.260e1, + 2.498e1, + 2.792e1, + 2.920e1, + 3.051e1, + 3.389e1, + 3.727e1, + 3.981e1, + 4.552e1, + 4.785e1, + 5.012e1, + 5.559e1, + 6.144e1, + 6.310e1, + 6.790e1, + 7.079e1, + 7.889e1, + 8.528e1, + 9.166e1, + 1.013e2, + 1.122e2, + 1.301e2, + 1.367e2, + 1.585e2, + 1.670e2, + 1.778e2, + 2.040e2, + 2.145e2, + 2.430e2, + 2.754e2, + 3.043e2, + 3.536e2, + 3.981e2, + 4.540e2, + 5.145e2, + 5.830e2, + 6.310e2, + 6.773e2, + 7.079e2, + 7.485e2, + 8.482e2, + 9.611e2, + 1.010e3, + 1.117e3, + 1.234e3, + 1.364e3, + 1.507e3, + 1.585e3, + 1.796e3, + 2.035e3, + 2.113e3, + 2.249e3, + 2.371e3, + 2.485e3, + 2.613e3, + 2.661e3, + 2.747e3, + 2.818e3, + 3.035e3, + 3.162e3, + 3.355e3, + 3.548e3, + 3.707e3, + 3.981e3, + 4.307e3, + 4.643e3, + 5.004e3, + 5.531e3, + 6.267e3, + 7.102e3, + 7.466e3, + 8.251e3, + 9.119e3, + 1.008e4, + 1.114e4, + 1.171e4, + 1.273e4, + 1.383e4, + 1.503e4, + 1.585e4, + 1.662e4, + 1.778e4, + 1.931e4, + 1.995e4, + 2.054e4, + 2.113e4, + 2.187e4, + 2.239e4, + 2.304e4, + 2.358e4, + 2.418e4, + 2.441e4, + 2.479e4, + 2.512e4, + 2.585e4, + 2.606e4, + 2.661e4, + 2.700e4, + 2.738e4, + 2.818e4, + 2.850e4, + 2.901e4, + 2.985e4, + 3.073e4, + 3.162e4, + 3.183e4, + 3.431e4, + 3.698e4, + 4.087e4, + 4.359e4, + 4.631e4, + 4.939e4, + 5.248e4, + 5.517e4, + 5.656e4, + 6.173e4, + 6.738e4, + 7.200e4, + 7.499e4, + 7.950e4, + 8.230e4, + 8.250e4, + 8.652e4, + 9.804e4, + 1.111e5, + 1.168e5, + 1.228e5, + 1.291e5, + 1.357e5, + 1.426e5, + 1.500e5, + 1.576e5, + 1.657e5, + 1.742e5, + 1.832e5, + 1.925e5, + 2.024e5, + 2.128e5, + 2.237e5, + 2.352e5, + 2.472e5, + 2.732e5, + 2.873e5, + 2.945e5, + 2.972e5, + 2.985e5, + 3.020e5, + 3.337e5, + 3.688e5, + 3.877e5, + 4.076e5, + 4.505e5, + 5.234e5, + 5.502e5, + 5.784e5, + 6.081e5, + 6.393e5, + 6.721e5, + 7.065e5, + 7.427e5, + 7.808e5, + 8.209e5, + 8.629e5, + 9.072e5, + 9.616e5, + 1.003e6, + 1.108e6, + 1.165e6, + 1.225e6, + 1.287e6, + 1.353e6, + 1.423e6, + 1.496e6, + 1.572e6, + 1.653e6, + 1.738e6, + 1.827e6, + 1.921e6, + 2.019e6, + 2.122e6, + 2.231e6, + 2.307e6, + 2.346e6, + 2.365e6, + 2.385e6, + 2.466e6, + 2.592e6, + 2.725e6, + 2.865e6, + 3.012e6, + 3.166e6, + 3.329e6, + 3.679e6, + 4.066e6, + 4.493e6, + 4.724e6, + 4.966e6, + 5.220e6, + 5.488e6, + 5.769e6, + 6.065e6, + 6.376e6, + 6.592e6, + 6.703e6, + 7.047e6, + 7.408e6, + 7.788e6, + 8.187e6, + 8.607e6, + 9.048e6, + 9.512e6, + 1.000e7, + 1.051e7, + 1.105e7, + 1.162e7, + 1.221e7, + 1.284e7, + 1.350e7, + 1.384e7, + 1.419e7, + 1.455e7, + 1.492e7, + 1.568e7, + 1.649e7, + 1.691e7, + 1.733e7, + 1.964e7, + ] +) +GROUP_STRUCTURES["SHEM-361"] = np.array( + [ + 0.00000e00, + 2.49990e-03, + 4.55602e-03, + 7.14526e-03, + 1.04505e-02, + 1.48300e-02, + 2.00104e-02, + 2.49394e-02, + 2.92989e-02, + 3.43998e-02, + 4.02999e-02, + 4.73019e-02, + 5.54982e-02, + 6.51999e-02, + 7.64969e-02, + 8.97968e-02, + 1.04298e-01, + 1.19995e-01, + 1.37999e-01, + 1.61895e-01, + 1.90005e-01, + 2.09610e-01, + 2.31192e-01, + 2.54997e-01, + 2.79989e-01, + 3.05012e-01, + 3.25008e-01, + 3.52994e-01, + 3.90001e-01, + 4.31579e-01, + 4.75017e-01, + 5.20011e-01, + 5.54990e-01, + 5.94993e-01, + 6.24999e-01, + 7.19999e-01, + 8.00371e-01, + 8.80024e-01, + 9.19978e-01, + 9.44022e-01, + 9.63960e-01, + 9.81959e-01, + 9.96501e-01, + 1.00904e00, + 1.02101e00, + 1.03499e00, + 1.07799e00, + 1.09198e00, + 1.10395e00, + 1.11605e00, + 1.12997e00, + 1.14797e00, + 1.16999e00, + 1.21397e00, + 1.25094e00, + 1.29304e00, + 1.33095e00, + 1.38098e00, + 1.41001e00, + 1.44397e00, + 1.51998e00, + 1.58803e00, + 1.66895e00, + 1.77997e00, + 1.90008e00, + 1.98992e00, + 2.07010e00, + 2.15695e00, + 2.21709e00, + 2.27299e00, + 2.33006e00, + 2.46994e00, + 2.55000e00, + 2.59009e00, + 2.62005e00, + 2.64004e00, + 2.70012e00, + 2.71990e00, + 2.74092e00, + 2.77512e00, + 2.88405e00, + 3.14211e00, + 3.54307e00, + 3.71209e00, + 3.88217e00, + 4.00000e00, + 4.21983e00, + 4.30981e00, + 4.41980e00, + 4.76785e00, + 4.93323e00, + 5.10997e00, + 5.21008e00, + 5.32011e00, + 5.38003e00, + 5.41025e00, + 5.48817e00, + 5.53004e00, + 5.61979e00, + 5.72015e00, + 5.80021e00, + 5.96014e00, + 6.05991e00, + 6.16011e00, + 6.28016e00, + 6.35978e00, + 6.43206e00, + 6.48178e00, + 6.51492e00, + 6.53907e00, + 6.55609e00, + 6.57184e00, + 6.58829e00, + 6.60611e00, + 6.63126e00, + 6.71668e00, + 6.74225e00, + 6.75981e00, + 6.77605e00, + 6.79165e00, + 6.81070e00, + 6.83526e00, + 6.87021e00, + 6.91778e00, + 6.99429e00, + 7.13987e00, + 7.38015e00, + 7.60035e00, + 7.73994e00, + 7.83965e00, + 7.97008e00, + 8.13027e00, + 8.30032e00, + 8.52407e00, + 8.67369e00, + 8.80038e00, + 8.97995e00, + 9.14031e00, + 9.50002e00, + 1.05793e01, + 1.08038e01, + 1.10529e01, + 1.12694e01, + 1.15894e01, + 1.17094e01, + 1.18153e01, + 1.19795e01, + 1.21302e01, + 1.23086e01, + 1.24721e01, + 1.26000e01, + 1.33297e01, + 1.35460e01, + 1.40496e01, + 1.42505e01, + 1.44702e01, + 1.45952e01, + 1.47301e01, + 1.48662e01, + 1.57792e01, + 1.60498e01, + 1.65501e01, + 1.68305e01, + 1.74457e01, + 1.75648e01, + 1.77590e01, + 1.79591e01, + 1.90848e01, + 1.91997e01, + 1.93927e01, + 1.95974e01, + 2.00734e01, + 2.02751e01, + 2.04175e01, + 2.05199e01, + 2.06021e01, + 2.06847e01, + 2.07676e01, + 2.09763e01, + 2.10604e01, + 2.11448e01, + 2.12296e01, + 2.13360e01, + 2.14859e01, + 2.17018e01, + 2.20011e01, + 2.21557e01, + 2.23788e01, + 2.25356e01, + 2.46578e01, + 2.78852e01, + 3.16930e01, + 3.30855e01, + 3.45392e01, + 3.56980e01, + 3.60568e01, + 3.64191e01, + 3.68588e01, + 3.73038e01, + 3.77919e01, + 3.87874e01, + 3.97295e01, + 4.12270e01, + 4.21441e01, + 4.31246e01, + 4.41721e01, + 4.52904e01, + 4.62053e01, + 4.75173e01, + 4.92591e01, + 5.17847e01, + 5.29895e01, + 5.40600e01, + 5.70595e01, + 5.99250e01, + 6.23083e01, + 6.36306e01, + 6.45923e01, + 6.50460e01, + 6.55029e01, + 6.58312e01, + 6.61612e01, + 6.64929e01, + 6.68261e01, + 6.90682e01, + 7.18869e01, + 7.35595e01, + 7.63322e01, + 7.93679e01, + 8.39393e01, + 8.87741e01, + 9.33256e01, + 9.73287e01, + 1.00594e02, + 1.01098e02, + 1.01605e02, + 1.02115e02, + 1.03038e02, + 1.05646e02, + 1.10288e02, + 1.12854e02, + 1.15480e02, + 1.16524e02, + 1.17577e02, + 1.20554e02, + 1.26229e02, + 1.32701e02, + 1.39504e02, + 1.46657e02, + 1.54176e02, + 1.63056e02, + 1.67519e02, + 1.75229e02, + 1.83295e02, + 1.84952e02, + 1.86251e02, + 1.87559e02, + 1.88877e02, + 1.90204e02, + 1.93078e02, + 1.95996e02, + 2.00958e02, + 2.12108e02, + 2.24325e02, + 2.35590e02, + 2.41796e02, + 2.56748e02, + 2.68297e02, + 2.76468e02, + 2.84888e02, + 2.88327e02, + 2.95922e02, + 3.19928e02, + 3.35323e02, + 3.53575e02, + 3.71703e02, + 3.90760e02, + 4.19094e02, + 4.53999e02, + 5.01746e02, + 5.39204e02, + 5.77146e02, + 5.92941e02, + 6.00099e02, + 6.12834e02, + 6.46837e02, + 6.77287e02, + 7.48517e02, + 8.32218e02, + 9.09681e02, + 9.82494e02, + 1.06432e03, + 1.13467e03, + 1.34358e03, + 1.58620e03, + 1.81183e03, + 2.08410e03, + 2.39729e03, + 2.70024e03, + 2.99618e03, + 3.48107e03, + 4.09735e03, + 5.00451e03, + 6.11252e03, + 7.46585e03, + 9.11881e03, + 1.11377e04, + 1.36037e04, + 1.48997e04, + 1.62005e04, + 1.85847e04, + 2.26994e04, + 2.49991e04, + 2.61001e04, + 2.73944e04, + 2.92810e04, + 3.34596e04, + 3.69786e04, + 4.08677e04, + 4.99159e04, + 5.51656e04, + 6.73794e04, + 8.22974e04, + 9.46645e04, + 1.15624e05, + 1.22773e05, + 1.40000e05, + 1.64999e05, + 1.95008e05, + 2.30014e05, + 2.67826e05, + 3.20646e05, + 3.83884e05, + 4.12501e05, + 4.56021e05, + 4.94002e05, + 5.78443e05, + 7.06511e05, + 8.60006e05, + 9.51119e05, + 1.05115e06, + 1.16205e06, + 1.28696e06, + 1.33694e06, + 1.40577e06, + 1.63654e06, + 1.90139e06, + 2.23130e06, + 2.72531e06, + 3.32871e06, + 4.06569e06, + 4.96585e06, + 6.06530e06, + 6.70319e06, + 7.40817e06, + 8.18730e06, + 9.04836e06, + 9.99999e06, + 1.16183e07, + 1.38403e07, + 1.49182e07, + 1.96403e07, + ] +) +GROUP_STRUCTURES["CCFE-709"] = np.array( + [ + 1.0e-5, + 1.0471e-5, + 1.0965e-5, + 1.1482e-5, + 1.2023e-5, + 1.2589e-5, + 1.3183e-5, + 1.3804e-5, + 1.4454e-5, + 1.5136e-5, + 1.5849e-5, + 1.6596e-5, + 1.7378e-5, + 1.8197e-5, + 1.9055e-5, + 1.9953e-5, + 2.0893e-5, + 2.1878e-5, + 2.2909e-5, + 2.3988e-5, + 2.5119e-5, + 2.6303e-5, + 2.7542e-5, + 2.8840e-5, + 3.0200e-5, + 3.1623e-5, + 3.3113e-5, + 3.4674e-5, + 3.6308e-5, + 3.8019e-5, + 3.9811e-5, + 4.1687e-5, + 4.3652e-5, + 4.5709e-5, + 4.7863e-5, + 5.0119e-5, + 5.2481e-5, + 5.4954e-5, + 5.7544e-5, + 6.0256e-5, + 6.3096e-5, + 6.6069e-5, + 6.9183e-5, + 7.2444e-5, + 7.5858e-5, + 7.9433e-5, + 8.3176e-5, + 8.7096e-5, + 9.1201e-5, + 9.5499e-5, + 1.0000e-4, + 1.0471e-4, + 1.0965e-4, + 1.1482e-4, + 1.2023e-4, + 1.2589e-4, + 1.3183e-4, + 1.3804e-4, + 1.4454e-4, + 1.5136e-4, + 1.5849e-4, + 1.6596e-4, + 1.7378e-4, + 1.8197e-4, + 1.9055e-4, + 1.9953e-4, + 2.0893e-4, + 2.1878e-4, + 2.2909e-4, + 2.3988e-4, + 2.5119e-4, + 2.6303e-4, + 2.7542e-4, + 2.8840e-4, + 3.0200e-4, + 3.1623e-4, + 3.3113e-4, + 3.4674e-4, + 3.6308e-4, + 3.8019e-4, + 3.9811e-4, + 4.1687e-4, + 4.3652e-4, + 4.5709e-4, + 4.7863e-4, + 5.0119e-4, + 5.2481e-4, + 5.4954e-4, + 5.7544e-4, + 6.0256e-4, + 6.3096e-4, + 6.6069e-4, + 6.9183e-4, + 7.2444e-4, + 7.5858e-4, + 7.9433e-4, + 8.3176e-4, + 8.7096e-4, + 9.1201e-4, + 9.5499e-4, + 1.0000e-3, + 1.0471e-3, + 1.0965e-3, + 1.1482e-3, + 1.2023e-3, + 1.2589e-3, + 1.3183e-3, + 1.3804e-3, + 1.4454e-3, + 1.5136e-3, + 1.5849e-3, + 1.6596e-3, + 1.7378e-3, + 1.8197e-3, + 1.9055e-3, + 1.9953e-3, + 2.0893e-3, + 2.1878e-3, + 2.2909e-3, + 2.3988e-3, + 2.5119e-3, + 2.6303e-3, + 2.7542e-3, + 2.8840e-3, + 3.0200e-3, + 3.1623e-3, + 3.3113e-3, + 3.4674e-3, + 3.6308e-3, + 3.8019e-3, + 3.9811e-3, + 4.1687e-3, + 4.3652e-3, + 4.5709e-3, + 4.7863e-3, + 5.0119e-3, + 5.2481e-3, + 5.4954e-3, + 5.7544e-3, + 6.0256e-3, + 6.3096e-3, + 6.6069e-3, + 6.9183e-3, + 7.2444e-3, + 7.5858e-3, + 7.9433e-3, + 8.3176e-3, + 8.7096e-3, + 9.1201e-3, + 9.5499e-3, + 1.0000e-2, + 1.0471e-2, + 1.0965e-2, + 1.1482e-2, + 1.2023e-2, + 1.2589e-2, + 1.3183e-2, + 1.3804e-2, + 1.4454e-2, + 1.5136e-2, + 1.5849e-2, + 1.6596e-2, + 1.7378e-2, + 1.8197e-2, + 1.9055e-2, + 1.9953e-2, + 2.0893e-2, + 2.1878e-2, + 2.2909e-2, + 2.3988e-2, + 2.5119e-2, + 2.6303e-2, + 2.7542e-2, + 2.8840e-2, + 3.0200e-2, + 3.1623e-2, + 3.3113e-2, + 3.4674e-2, + 3.6308e-2, + 3.8019e-2, + 3.9811e-2, + 4.1687e-2, + 4.3652e-2, + 4.5709e-2, + 4.7863e-2, + 5.0119e-2, + 5.2481e-2, + 5.4954e-2, + 5.7544e-2, + 6.0256e-2, + 6.3096e-2, + 6.6069e-2, + 6.9183e-2, + 7.2444e-2, + 7.5858e-2, + 7.9433e-2, + 8.3176e-2, + 8.7096e-2, + 9.1201e-2, + 9.5499e-2, + 1.0000e-1, + 1.0471e-1, + 1.0965e-1, + 1.1482e-1, + 1.2023e-1, + 1.2589e-1, + 1.3183e-1, + 1.3804e-1, + 1.4454e-1, + 1.5136e-1, + 1.5849e-1, + 1.6596e-1, + 1.7378e-1, + 1.8197e-1, + 1.9055e-1, + 1.9953e-1, + 2.0893e-1, + 2.1878e-1, + 2.2909e-1, + 2.3988e-1, + 2.5119e-1, + 2.6303e-1, + 2.7542e-1, + 2.8840e-1, + 3.0200e-1, + 3.1623e-1, + 3.3113e-1, + 3.4674e-1, + 3.6308e-1, + 3.8019e-1, + 3.9811e-1, + 4.1687e-1, + 4.3652e-1, + 4.5709e-1, + 4.7863e-1, + 5.0119e-1, + 5.2481e-1, + 5.4954e-1, + 5.7544e-1, + 6.0256e-1, + 6.3096e-1, + 6.6069e-1, + 6.9183e-1, + 7.2444e-1, + 7.5858e-1, + 7.9433e-1, + 8.3176e-1, + 8.7096e-1, + 9.1201e-1, + 9.5499e-1, + 1.0000e0, + 1.0471e0, + 1.0965e0, + 1.1482e0, + 1.2023e0, + 1.2589e0, + 1.3183e0, + 1.3804e0, + 1.4454e0, + 1.5136e0, + 1.5849e0, + 1.6596e0, + 1.7378e0, + 1.8197e0, + 1.9055e0, + 1.9953e0, + 2.0893e0, + 2.1878e0, + 2.2909e0, + 2.3988e0, + 2.5119e0, + 2.6303e0, + 2.7542e0, + 2.8840e0, + 3.0200e0, + 3.1623e0, + 3.3113e0, + 3.4674e0, + 3.6308e0, + 3.8019e0, + 3.9811e0, + 4.1687e0, + 4.3652e0, + 4.5709e0, + 4.7863e0, + 5.0119e0, + 5.2481e0, + 5.4954e0, + 5.7544e0, + 6.0256e0, + 6.3096e0, + 6.6069e0, + 6.9183e0, + 7.2444e0, + 7.5858e0, + 7.9433e0, + 8.3176e0, + 8.7096e0, + 9.1201e0, + 9.5499e0, + 1.0000e1, + 1.0471e1, + 1.0965e1, + 1.1482e1, + 1.2023e1, + 1.2589e1, + 1.3183e1, + 1.3804e1, + 1.4454e1, + 1.5136e1, + 1.5849e1, + 1.6596e1, + 1.7378e1, + 1.8197e1, + 1.9055e1, + 1.9953e1, + 2.0893e1, + 2.1878e1, + 2.2909e1, + 2.3988e1, + 2.5119e1, + 2.6303e1, + 2.7542e1, + 2.8840e1, + 3.0200e1, + 3.1623e1, + 3.3113e1, + 3.4674e1, + 3.6308e1, + 3.8019e1, + 3.9811e1, + 4.1687e1, + 4.3652e1, + 4.5709e1, + 4.7863e1, + 5.0119e1, + 5.2481e1, + 5.4954e1, + 5.7544e1, + 6.0256e1, + 6.3096e1, + 6.6069e1, + 6.9183e1, + 7.2444e1, + 7.5858e1, + 7.9433e1, + 8.3176e1, + 8.7096e1, + 9.1201e1, + 9.5499e1, + 1.0000e2, + 1.0471e2, + 1.0965e2, + 1.1482e2, + 1.2023e2, + 1.2589e2, + 1.3183e2, + 1.3804e2, + 1.4454e2, + 1.5136e2, + 1.5849e2, + 1.6596e2, + 1.7378e2, + 1.8197e2, + 1.9055e2, + 1.9953e2, + 2.0893e2, + 2.1878e2, + 2.2909e2, + 2.3988e2, + 2.5119e2, + 2.6303e2, + 2.7542e2, + 2.8840e2, + 3.0200e2, + 3.1623e2, + 3.3113e2, + 3.4674e2, + 3.6308e2, + 3.8019e2, + 3.9811e2, + 4.1687e2, + 4.3652e2, + 4.5709e2, + 4.7863e2, + 5.0119e2, + 5.2481e2, + 5.4954e2, + 5.7544e2, + 6.0256e2, + 6.3096e2, + 6.6069e2, + 6.9183e2, + 7.2444e2, + 7.5858e2, + 7.9433e2, + 8.3176e2, + 8.7096e2, + 9.1201e2, + 9.5499e2, + 1.0000e3, + 1.0471e3, + 1.0965e3, + 1.1482e3, + 1.2023e3, + 1.2589e3, + 1.3183e3, + 1.3804e3, + 1.4454e3, + 1.5136e3, + 1.5849e3, + 1.6596e3, + 1.7378e3, + 1.8197e3, + 1.9055e3, + 1.9953e3, + 2.0893e3, + 2.1878e3, + 2.2909e3, + 2.3988e3, + 2.5119e3, + 2.6303e3, + 2.7542e3, + 2.8840e3, + 3.0200e3, + 3.1623e3, + 3.3113e3, + 3.4674e3, + 3.6308e3, + 3.8019e3, + 3.9811e3, + 4.1687e3, + 4.3652e3, + 4.5709e3, + 4.7863e3, + 5.0119e3, + 5.2481e3, + 5.4954e3, + 5.7544e3, + 6.0256e3, + 6.3096e3, + 6.6069e3, + 6.9183e3, + 7.2444e3, + 7.5858e3, + 7.9433e3, + 8.3176e3, + 8.7096e3, + 9.1201e3, + 9.5499e3, + 1.0000e4, + 1.0471e4, + 1.0965e4, + 1.1482e4, + 1.2023e4, + 1.2589e4, + 1.3183e4, + 1.3804e4, + 1.4454e4, + 1.5136e4, + 1.5849e4, + 1.6596e4, + 1.7378e4, + 1.8197e4, + 1.9055e4, + 1.9953e4, + 2.0893e4, + 2.1878e4, + 2.2909e4, + 2.3988e4, + 2.5119e4, + 2.6303e4, + 2.7542e4, + 2.8840e4, + 3.0200e4, + 3.1623e4, + 3.3113e4, + 3.4674e4, + 3.6308e4, + 3.8019e4, + 3.9811e4, + 4.1687e4, + 4.3652e4, + 4.5709e4, + 4.7863e4, + 5.0119e4, + 5.2481e4, + 5.4954e4, + 5.7544e4, + 6.0256e4, + 6.3096e4, + 6.6069e4, + 6.9183e4, + 7.2444e4, + 7.5858e4, + 7.9433e4, + 8.3176e4, + 8.7096e4, + 9.1201e4, + 9.5499e4, + 1.0000e5, + 1.0471e5, + 1.0965e5, + 1.1482e5, + 1.2023e5, + 1.2589e5, + 1.3183e5, + 1.3804e5, + 1.4454e5, + 1.5136e5, + 1.5849e5, + 1.6596e5, + 1.7378e5, + 1.8197e5, + 1.9055e5, + 1.9953e5, + 2.0893e5, + 2.1878e5, + 2.2909e5, + 2.3988e5, + 2.5119e5, + 2.6303e5, + 2.7542e5, + 2.8840e5, + 3.0200e5, + 3.1623e5, + 3.3113e5, + 3.4674e5, + 3.6308e5, + 3.8019e5, + 3.9811e5, + 4.1687e5, + 4.3652e5, + 4.5709e5, + 4.7863e5, + 5.0119e5, + 5.2481e5, + 5.4954e5, + 5.7544e5, + 6.0256e5, + 6.3096e5, + 6.6069e5, + 6.9183e5, + 7.2444e5, + 7.5858e5, + 7.9433e5, + 8.3176e5, + 8.7096e5, + 9.1201e5, + 9.5499e5, + 1.0000e6, + 1.0471e6, + 1.0965e6, + 1.1482e6, + 1.2023e6, + 1.2589e6, + 1.3183e6, + 1.3804e6, + 1.4454e6, + 1.5136e6, + 1.5849e6, + 1.6596e6, + 1.7378e6, + 1.8197e6, + 1.9055e6, + 1.9953e6, + 2.0893e6, + 2.1878e6, + 2.2909e6, + 2.3988e6, + 2.5119e6, + 2.6303e6, + 2.7542e6, + 2.8840e6, + 3.0200e6, + 3.1623e6, + 3.3113e6, + 3.4674e6, + 3.6308e6, + 3.8019e6, + 3.9811e6, + 4.1687e6, + 4.3652e6, + 4.5709e6, + 4.7863e6, + 5.0119e6, + 5.2481e6, + 5.4954e6, + 5.7544e6, + 6.0256e6, + 6.3096e6, + 6.6069e6, + 6.9183e6, + 7.2444e6, + 7.5858e6, + 7.9433e6, + 8.3176e6, + 8.7096e6, + 9.1201e6, + 9.5499e6, + 1.0000e7, + 1.0200e7, + 1.0400e7, + 1.0600e7, + 1.0800e7, + 1.1000e7, + 1.1200e7, + 1.1400e7, + 1.1600e7, + 1.1800e7, + 1.2000e7, + 1.2200e7, + 1.2400e7, + 1.2600e7, + 1.2800e7, + 1.3000e7, + 1.3200e7, + 1.3400e7, + 1.3600e7, + 1.3800e7, + 1.4000e7, + 1.4200e7, + 1.4400e7, + 1.4600e7, + 1.4800e7, + 1.5000e7, + 1.5200e7, + 1.5400e7, + 1.5600e7, + 1.5800e7, + 1.6000e7, + 1.6200e7, + 1.6400e7, + 1.6600e7, + 1.6800e7, + 1.7000e7, + 1.7200e7, + 1.7400e7, + 1.7600e7, + 1.7800e7, + 1.8000e7, + 1.8200e7, + 1.8400e7, + 1.8600e7, + 1.8800e7, + 1.9000e7, + 1.9200e7, + 1.9400e7, + 1.9600e7, + 1.9800e7, + 2.0000e7, + 2.1000e7, + 2.2000e7, + 2.3000e7, + 2.4000e7, + 2.5000e7, + 2.6000e7, + 2.7000e7, + 2.8000e7, + 2.9000e7, + 3.0000e7, + 3.2000e7, + 3.4000e7, + 3.6000e7, + 3.8000e7, + 4.0000e7, + 4.2000e7, + 4.4000e7, + 4.6000e7, + 4.8000e7, + 5.0000e7, + 5.2000e7, + 5.4000e7, + 5.6000e7, + 5.8000e7, + 6.0000e7, + 6.5000e7, + 7.0000e7, + 7.5000e7, + 8.0000e7, + 9.0000e7, + 1.0000e8, + 1.1000e8, + 1.2000e8, + 1.3000e8, + 1.4000e8, + 1.5000e8, + 1.6000e8, + 1.8000e8, + 2.0000e8, + 2.4000e8, + 2.8000e8, + 3.2000e8, + 3.6000e8, + 4.0000e8, + 4.4000e8, + 4.8000e8, + 5.2000e8, + 5.6000e8, + 6.0000e8, + 6.4000e8, + 6.8000e8, + 7.2000e8, + 7.6000e8, + 8.0000e8, + 8.4000e8, + 8.8000e8, + 9.2000e8, + 9.6000e8, + 1.0000e9, + ] +) +GROUP_STRUCTURES["UKAEA-1102"] = np.array( + [ + 1.0000e-5, + 1.0471e-5, + 1.0965e-5, + 1.1482e-5, + 1.2023e-5, + 1.2589e-5, + 1.3183e-5, + 1.3804e-5, + 1.4454e-5, + 1.5136e-5, + 1.5849e-5, + 1.6596e-5, + 1.7378e-5, + 1.8197e-5, + 1.9055e-5, + 1.9953e-5, + 2.0893e-5, + 2.1878e-5, + 2.2909e-5, + 2.3988e-5, + 2.5119e-5, + 2.6303e-5, + 2.7542e-5, + 2.8840e-5, + 3.0200e-5, + 3.1623e-5, + 3.3113e-5, + 3.4674e-5, + 3.6308e-5, + 3.8019e-5, + 3.9811e-5, + 4.1687e-5, + 4.3652e-5, + 4.5709e-5, + 4.7863e-5, + 5.0119e-5, + 5.2481e-5, + 5.4954e-5, + 5.7544e-5, + 6.0256e-5, + 6.3096e-5, + 6.6069e-5, + 6.9183e-5, + 7.2444e-5, + 7.5858e-5, + 7.9433e-5, + 8.3176e-5, + 8.7096e-5, + 9.1201e-5, + 9.5499e-5, + 1.0000e-4, + 1.0471e-4, + 1.0965e-4, + 1.1482e-4, + 1.2023e-4, + 1.2589e-4, + 1.3183e-4, + 1.3804e-4, + 1.4454e-4, + 1.5136e-4, + 1.5849e-4, + 1.6596e-4, + 1.7378e-4, + 1.8197e-4, + 1.9055e-4, + 1.9953e-4, + 2.0893e-4, + 2.1878e-4, + 2.2909e-4, + 2.3988e-4, + 2.5119e-4, + 2.6303e-4, + 2.7542e-4, + 2.8840e-4, + 3.0200e-4, + 3.1623e-4, + 3.3113e-4, + 3.4674e-4, + 3.6308e-4, + 3.8019e-4, + 3.9811e-4, + 4.1687e-4, + 4.3652e-4, + 4.5709e-4, + 4.7863e-4, + 5.0119e-4, + 5.2481e-4, + 5.4954e-4, + 5.7544e-4, + 6.0256e-4, + 6.3096e-4, + 6.6069e-4, + 6.9183e-4, + 7.2444e-4, + 7.5858e-4, + 7.9433e-4, + 8.3176e-4, + 8.7096e-4, + 9.1201e-4, + 9.5499e-4, + 1.0000e-3, + 1.0471e-3, + 1.0965e-3, + 1.1482e-3, + 1.2023e-3, + 1.2589e-3, + 1.3183e-3, + 1.3804e-3, + 1.4454e-3, + 1.5136e-3, + 1.5849e-3, + 1.6596e-3, + 1.7378e-3, + 1.8197e-3, + 1.9055e-3, + 1.9953e-3, + 2.0893e-3, + 2.1878e-3, + 2.2909e-3, + 2.3988e-3, + 2.5119e-3, + 2.6303e-3, + 2.7542e-3, + 2.8840e-3, + 3.0200e-3, + 3.1623e-3, + 3.3113e-3, + 3.4674e-3, + 3.6308e-3, + 3.8019e-3, + 3.9811e-3, + 4.1687e-3, + 4.3652e-3, + 4.5709e-3, + 4.7863e-3, + 5.0119e-3, + 5.2481e-3, + 5.4954e-3, + 5.7544e-3, + 6.0256e-3, + 6.3096e-3, + 6.6069e-3, + 6.9183e-3, + 7.2444e-3, + 7.5858e-3, + 7.9433e-3, + 8.3176e-3, + 8.7096e-3, + 9.1201e-3, + 9.5499e-3, + 1.0000e-2, + 1.0471e-2, + 1.0965e-2, + 1.1482e-2, + 1.2023e-2, + 1.2589e-2, + 1.3183e-2, + 1.3804e-2, + 1.4454e-2, + 1.5136e-2, + 1.5849e-2, + 1.6596e-2, + 1.7378e-2, + 1.8197e-2, + 1.9055e-2, + 1.9953e-2, + 2.0893e-2, + 2.1878e-2, + 2.2909e-2, + 2.3988e-2, + 2.5119e-2, + 2.6303e-2, + 2.7542e-2, + 2.8840e-2, + 3.0200e-2, + 3.1623e-2, + 3.3113e-2, + 3.4674e-2, + 3.6308e-2, + 3.8019e-2, + 3.9811e-2, + 4.1687e-2, + 4.3652e-2, + 4.5709e-2, + 4.7863e-2, + 5.0119e-2, + 5.2481e-2, + 5.4954e-2, + 5.7544e-2, + 6.0256e-2, + 6.3096e-2, + 6.6069e-2, + 6.9183e-2, + 7.2444e-2, + 7.5858e-2, + 7.9433e-2, + 8.3176e-2, + 8.7096e-2, + 9.1201e-2, + 9.5499e-2, + 1.0000e-1, + 1.0471e-1, + 1.0965e-1, + 1.1482e-1, + 1.2023e-1, + 1.2589e-1, + 1.3183e-1, + 1.3804e-1, + 1.4454e-1, + 1.5136e-1, + 1.5849e-1, + 1.6596e-1, + 1.7378e-1, + 1.8197e-1, + 1.9055e-1, + 1.9953e-1, + 2.0893e-1, + 2.1878e-1, + 2.2909e-1, + 2.3988e-1, + 2.5119e-1, + 2.6303e-1, + 2.7542e-1, + 2.8840e-1, + 3.0200e-1, + 3.1623e-1, + 3.3113e-1, + 3.4674e-1, + 3.6308e-1, + 3.8019e-1, + 3.9811e-1, + 4.1687e-1, + 4.3652e-1, + 4.5709e-1, + 4.7863e-1, + 5.0119e-1, + 5.2481e-1, + 5.5000e-1, + 5.7500e-1, + 6.0000e-1, + 6.2500e-1, + 6.5000e-1, + 6.7500e-1, + 7.0000e-1, + 7.2500e-1, + 7.5000e-1, + 7.7500e-1, + 8.0000e-1, + 8.2500e-1, + 8.5000e-1, + 8.7500e-1, + 9.0000e-1, + 9.2500e-1, + 9.5000e-1, + 9.7500e-1, + 1.0000, + 1.0250, + 1.0500, + 1.0750, + 1.1000, + 1.1250, + 1.1500, + 1.1750, + 1.2000, + 1.2250, + 1.2500, + 1.2750, + 1.3000, + 1.3250, + 1.3500, + 1.3750, + 1.4000, + 1.4250, + 1.4500, + 1.4750, + 1.5000, + 1.5250, + 1.5500, + 1.5750, + 1.6000, + 1.6250, + 1.6500, + 1.6750, + 1.7000, + 1.7250, + 1.7500, + 1.7750, + 1.8000, + 1.8250, + 1.8500, + 1.8750, + 1.9000, + 1.9250, + 1.9500, + 1.9750, + 2.0000, + 2.0250, + 2.0500, + 2.0750, + 2.1000, + 2.1250, + 2.1500, + 2.1750, + 2.2000, + 2.2250, + 2.2500, + 2.2750, + 2.3000, + 2.3250, + 2.3500, + 2.3750, + 2.4000, + 2.4250, + 2.4500, + 2.4750, + 2.5000, + 2.5250, + 2.5500, + 2.5750, + 2.6000, + 2.6250, + 2.6500, + 2.6750, + 2.7000, + 2.7250, + 2.7500, + 2.7750, + 2.8000, + 2.8250, + 2.8500, + 2.8750, + 2.9000, + 2.9250, + 2.9500, + 2.9750, + 3.0000, + 3.0250, + 3.0500, + 3.0750, + 3.1000, + 3.1250, + 3.1500, + 3.1750, + 3.2000, + 3.2250, + 3.2500, + 3.2750, + 3.3000, + 3.3250, + 3.3500, + 3.3750, + 3.4000, + 3.4250, + 3.4500, + 3.4750, + 3.5000, + 3.5250, + 3.5500, + 3.5750, + 3.6000, + 3.6250, + 3.6500, + 3.6750, + 3.7000, + 3.7250, + 3.7500, + 3.7750, + 3.8000, + 3.8250, + 3.8500, + 3.8750, + 3.9000, + 3.9250, + 3.9500, + 3.9750, + 4.0000, + 4.0250, + 4.0500, + 4.0750, + 4.1000, + 4.1250, + 4.1500, + 4.1750, + 4.2000, + 4.2250, + 4.2500, + 4.2750, + 4.3000, + 4.3250, + 4.3500, + 4.3750, + 4.4000, + 4.4250, + 4.4500, + 4.4750, + 4.5000, + 4.5250, + 4.5500, + 4.5750, + 4.6000, + 4.6250, + 4.6500, + 4.6750, + 4.7000, + 4.7250, + 4.7500, + 4.7750, + 4.8000, + 4.8250, + 4.8500, + 4.8750, + 4.9000, + 4.9250, + 4.9500, + 4.9750, + 5.0000, + 5.0250, + 5.0500, + 5.0750, + 5.1000, + 5.1250, + 5.1500, + 5.1750, + 5.2000, + 5.2250, + 5.2500, + 5.2750, + 5.3000, + 5.3250, + 5.3500, + 5.3750, + 5.4000, + 5.4250, + 5.4500, + 5.4750, + 5.5000, + 5.5250, + 5.5500, + 5.5750, + 5.6000, + 5.6250, + 5.6500, + 5.6750, + 5.7000, + 5.7250, + 5.7500, + 5.7750, + 5.8000, + 5.8250, + 5.8500, + 5.8750, + 5.9000, + 5.9250, + 5.9500, + 5.9750, + 6.0000, + 6.0250, + 6.0500, + 6.0750, + 6.1000, + 6.1250, + 6.1500, + 6.1750, + 6.2000, + 6.2250, + 6.2500, + 6.2750, + 6.3000, + 6.3250, + 6.3500, + 6.3750, + 6.4000, + 6.4250, + 6.4500, + 6.4750, + 6.5000, + 6.5250, + 6.5500, + 6.5750, + 6.6000, + 6.6250, + 6.6500, + 6.6750, + 6.7000, + 6.7250, + 6.7500, + 6.7750, + 6.8000, + 6.8250, + 6.8500, + 6.8750, + 6.9000, + 6.9250, + 6.9500, + 6.9750, + 7.0000, + 7.0250, + 7.0500, + 7.0750, + 7.1000, + 7.1250, + 7.1500, + 7.1750, + 7.2000, + 7.2250, + 7.2500, + 7.2750, + 7.3000, + 7.3250, + 7.3500, + 7.3750, + 7.4000, + 7.4250, + 7.4500, + 7.4750, + 7.5000, + 7.5250, + 7.5500, + 7.5750, + 7.6000, + 7.6250, + 7.6500, + 7.6750, + 7.7000, + 7.7250, + 7.7500, + 7.7750, + 7.8000, + 7.8250, + 7.8500, + 7.8750, + 7.9000, + 7.9250, + 7.9500, + 7.9750, + 8.0000, + 8.0250, + 8.0500, + 8.0750, + 8.1000, + 8.1250, + 8.1500, + 8.1750, + 8.2000, + 8.2250, + 8.2500, + 8.2750, + 8.3000, + 8.3250, + 8.3500, + 8.3750, + 8.4000, + 8.4250, + 8.4500, + 8.4750, + 8.5000, + 8.5250, + 8.5500, + 8.5750, + 8.6000, + 8.6250, + 8.6500, + 8.6750, + 8.7000, + 8.7250, + 8.7500, + 8.7750, + 8.8000, + 8.8250, + 8.8500, + 8.8750, + 8.9000, + 8.9250, + 8.9500, + 8.9750, + 9.0000, + 9.0250, + 9.0500, + 9.0750, + 9.1000, + 9.1250, + 9.1500, + 9.1750, + 9.2000, + 9.2250, + 9.2500, + 9.2750, + 9.3000, + 9.3250, + 9.3500, + 9.3750, + 9.4000, + 9.4250, + 9.4500, + 9.4750, + 9.5000, + 9.5250, + 9.5500, + 9.5750, + 9.6000, + 9.6250, + 9.6500, + 9.6750, + 9.7000, + 9.7250, + 9.7500, + 9.7750, + 9.8000, + 9.8250, + 9.8500, + 9.8750, + 9.9000, + 9.9250, + 9.9500, + 9.9750, + 1.0000e1, + 1.0471e1, + 1.0965e1, + 1.1482e1, + 1.2023e1, + 1.2589e1, + 1.3183e1, + 1.3804e1, + 1.4454e1, + 1.5136e1, + 1.5849e1, + 1.6596e1, + 1.7378e1, + 1.8197e1, + 1.9055e1, + 1.9953e1, + 2.0893e1, + 2.1878e1, + 2.2909e1, + 2.3988e1, + 2.5119e1, + 2.6303e1, + 2.7542e1, + 2.8840e1, + 3.0200e1, + 3.1623e1, + 3.3113e1, + 3.4674e1, + 3.6308e1, + 3.8019e1, + 3.9811e1, + 4.1687e1, + 4.3652e1, + 4.5709e1, + 4.7863e1, + 5.0119e1, + 5.2481e1, + 5.4954e1, + 5.7544e1, + 6.0256e1, + 6.3096e1, + 6.6069e1, + 6.9183e1, + 7.2444e1, + 7.5858e1, + 7.9433e1, + 8.3176e1, + 8.7096e1, + 9.1201e1, + 9.5499e1, + 1.0000e2, + 1.0471e2, + 1.0965e2, + 1.1482e2, + 1.2023e2, + 1.2589e2, + 1.3183e2, + 1.3804e2, + 1.4454e2, + 1.5136e2, + 1.5849e2, + 1.6596e2, + 1.7378e2, + 1.8197e2, + 1.9055e2, + 1.9953e2, + 2.0893e2, + 2.1878e2, + 2.2909e2, + 2.3988e2, + 2.5119e2, + 2.6303e2, + 2.7542e2, + 2.8840e2, + 3.0200e2, + 3.1623e2, + 3.3113e2, + 3.4674e2, + 3.6308e2, + 3.8019e2, + 3.9811e2, + 4.1687e2, + 4.3652e2, + 4.5709e2, + 4.7863e2, + 5.0119e2, + 5.2481e2, + 5.4954e2, + 5.7544e2, + 6.0256e2, + 6.3096e2, + 6.6069e2, + 6.9183e2, + 7.2444e2, + 7.5858e2, + 7.9433e2, + 8.3176e2, + 8.7096e2, + 9.1201e2, + 9.5499e2, + 1.0000e3, + 1.0471e3, + 1.0965e3, + 1.1482e3, + 1.2023e3, + 1.2589e3, + 1.3183e3, + 1.3804e3, + 1.4454e3, + 1.5136e3, + 1.5849e3, + 1.6596e3, + 1.7378e3, + 1.8197e3, + 1.9055e3, + 1.9953e3, + 2.0893e3, + 2.1878e3, + 2.2909e3, + 2.3988e3, + 2.5119e3, + 2.6303e3, + 2.7542e3, + 2.8840e3, + 3.0200e3, + 3.1623e3, + 3.3113e3, + 3.4674e3, + 3.6308e3, + 3.8019e3, + 3.9811e3, + 4.1687e3, + 4.3652e3, + 4.5709e3, + 4.7863e3, + 5.0119e3, + 5.2481e3, + 5.4954e3, + 5.7544e3, + 6.0256e3, + 6.3096e3, + 6.6069e3, + 6.9183e3, + 7.2444e3, + 7.5858e3, + 7.9433e3, + 8.3176e3, + 8.7096e3, + 9.1201e3, + 9.5499e3, + 1.0000e4, + 1.0471e4, + 1.0965e4, + 1.1482e4, + 1.2023e4, + 1.2589e4, + 1.3183e4, + 1.3804e4, + 1.4454e4, + 1.5136e4, + 1.5849e4, + 1.6596e4, + 1.7378e4, + 1.8197e4, + 1.9055e4, + 1.9953e4, + 2.0893e4, + 2.1878e4, + 2.2909e4, + 2.3988e4, + 2.5119e4, + 2.6303e4, + 2.7542e4, + 2.8840e4, + 3.0200e4, + 3.1623e4, + 3.3113e4, + 3.4674e4, + 3.6308e4, + 3.8019e4, + 3.9811e4, + 4.1687e4, + 4.3652e4, + 4.5709e4, + 4.7863e4, + 5.0119e4, + 5.2481e4, + 5.4954e4, + 5.7544e4, + 6.0256e4, + 6.3096e4, + 6.6069e4, + 6.9183e4, + 7.2444e4, + 7.5858e4, + 7.9433e4, + 8.3176e4, + 8.7096e4, + 9.1201e4, + 9.5499e4, + 1.0000e5, + 1.0471e5, + 1.0965e5, + 1.1482e5, + 1.2023e5, + 1.2589e5, + 1.3183e5, + 1.3804e5, + 1.4454e5, + 1.5136e5, + 1.5849e5, + 1.6596e5, + 1.7378e5, + 1.8197e5, + 1.9055e5, + 1.9953e5, + 2.0893e5, + 2.1878e5, + 2.2909e5, + 2.3988e5, + 2.5119e5, + 2.6303e5, + 2.7542e5, + 2.8840e5, + 3.0200e5, + 3.1623e5, + 3.3113e5, + 3.4674e5, + 3.6308e5, + 3.8019e5, + 3.9811e5, + 4.1687e5, + 4.3652e5, + 4.5709e5, + 4.7863e5, + 5.0119e5, + 5.2481e5, + 5.4954e5, + 5.7544e5, + 6.0256e5, + 6.3096e5, + 6.6069e5, + 6.9183e5, + 7.2444e5, + 7.5858e5, + 7.9433e5, + 8.3176e5, + 8.7096e5, + 9.1201e5, + 9.5499e5, + 1.0000e6, + 1.0471e6, + 1.0965e6, + 1.1482e6, + 1.2023e6, + 1.2589e6, + 1.3183e6, + 1.3804e6, + 1.4454e6, + 1.5136e6, + 1.5849e6, + 1.6596e6, + 1.7378e6, + 1.8197e6, + 1.9055e6, + 1.9953e6, + 2.0893e6, + 2.1878e6, + 2.2909e6, + 2.3988e6, + 2.5119e6, + 2.6303e6, + 2.7542e6, + 2.8840e6, + 3.0200e6, + 3.1623e6, + 3.3113e6, + 3.4674e6, + 3.6308e6, + 3.8019e6, + 3.9811e6, + 4.1687e6, + 4.3652e6, + 4.5709e6, + 4.7863e6, + 5.0000e6, + 5.2000e6, + 5.4000e6, + 5.6000e6, + 5.8000e6, + 6.0000e6, + 6.2000e6, + 6.4000e6, + 6.6000e6, + 6.8000e6, + 7.0000e6, + 7.2000e6, + 7.4000e6, + 7.6000e6, + 7.8000e6, + 8.0000e6, + 8.2000e6, + 8.4000e6, + 8.6000e6, + 8.8000e6, + 9.0000e6, + 9.2000e6, + 9.4000e6, + 9.6000e6, + 9.8000e6, + 1.0000e7, + 1.0200e7, + 1.0400e7, + 1.0600e7, + 1.0800e7, + 1.1000e7, + 1.1200e7, + 1.1400e7, + 1.1600e7, + 1.1800e7, + 1.2000e7, + 1.2200e7, + 1.2400e7, + 1.2600e7, + 1.2800e7, + 1.3000e7, + 1.3200e7, + 1.3400e7, + 1.3600e7, + 1.3800e7, + 1.4000e7, + 1.4200e7, + 1.4400e7, + 1.4600e7, + 1.4800e7, + 1.5000e7, + 1.5200e7, + 1.5400e7, + 1.5600e7, + 1.5800e7, + 1.6000e7, + 1.6200e7, + 1.6400e7, + 1.6600e7, + 1.6800e7, + 1.7000e7, + 1.7200e7, + 1.7400e7, + 1.7600e7, + 1.7800e7, + 1.8000e7, + 1.8200e7, + 1.8400e7, + 1.8600e7, + 1.8800e7, + 1.9000e7, + 1.9200e7, + 1.9400e7, + 1.9600e7, + 1.9800e7, + 2.0000e7, + 2.0200e7, + 2.0400e7, + 2.0600e7, + 2.0800e7, + 2.1000e7, + 2.1200e7, + 2.1400e7, + 2.1600e7, + 2.1800e7, + 2.2000e7, + 2.2200e7, + 2.2400e7, + 2.2600e7, + 2.2800e7, + 2.3000e7, + 2.3200e7, + 2.3400e7, + 2.3600e7, + 2.3800e7, + 2.4000e7, + 2.4200e7, + 2.4400e7, + 2.4600e7, + 2.4800e7, + 2.5000e7, + 2.5200e7, + 2.5400e7, + 2.5600e7, + 2.5800e7, + 2.6000e7, + 2.6200e7, + 2.6400e7, + 2.6600e7, + 2.6800e7, + 2.7000e7, + 2.7200e7, + 2.7400e7, + 2.7600e7, + 2.7800e7, + 2.8000e7, + 2.8200e7, + 2.8400e7, + 2.8600e7, + 2.8800e7, + 2.9000e7, + 2.9200e7, + 2.9400e7, + 2.9600e7, + 2.9800e7, + 3.0000e7, + 3.0200e7, + 3.1623e7, + 3.3113e7, + 3.4674e7, + 3.6308e7, + 3.8019e7, + 3.9811e7, + 4.1687e7, + 4.3652e7, + 4.5709e7, + 4.7863e7, + 5.0119e7, + 5.2481e7, + 5.4954e7, + 5.7544e7, + 6.0256e7, + 6.3096e7, + 6.6069e7, + 6.9183e7, + 7.2444e7, + 7.5858e7, + 7.9433e7, + 8.3176e7, + 8.7096e7, + 9.1201e7, + 9.5499e7, + 1.0000e8, + 1.0471e8, + 1.0965e8, + 1.1482e8, + 1.2023e8, + 1.2589e8, + 1.3183e8, + 1.3804e8, + 1.4454e8, + 1.5136e8, + 1.5849e8, + 1.6596e8, + 1.7378e8, + 1.8197e8, + 1.9055e8, + 1.9953e8, + 2.0893e8, + 2.1878e8, + 2.2909e8, + 2.3988e8, + 2.5119e8, + 2.6303e8, + 2.7542e8, + 2.8840e8, + 3.0200e8, + 3.1623e8, + 3.3113e8, + 3.4674e8, + 3.6308e8, + 3.8019e8, + 3.9811e8, + 4.1687e8, + 4.3652e8, + 4.5709e8, + 4.7863e8, + 5.0119e8, + 5.2481e8, + 5.4954e8, + 5.7544e8, + 6.0256e8, + 6.3096e8, + 6.6069e8, + 6.9183e8, + 7.2444e8, + 7.5858e8, + 7.9433e8, + 8.3176e8, + 8.7096e8, + 9.1201e8, + 9.5499e8, + 1.0e9, + ] +) +GROUP_STRUCTURES["ECCO-1968"] = np.array( + [ + 1.00001e-5, + 3.00000e-3, + 5.00000e-3, + 6.90000e-3, + 1.00000e-2, + 1.50000e-2, + 2.00000e-2, + 2.50000e-2, + 3.00000e-2, + 3.50000e-2, + 4.20000e-2, + 5.00000e-2, + 5.80000e-2, + 6.70000e-2, + 7.70000e-2, + 8.00000e-2, + 9.50000e-2, + 1.00000e-1, + 1.15000e-1, + 1.34000e-1, + 1.40000e-1, + 1.46370e-1, + 1.53030e-1, + 1.60000e-1, + 1.69710e-1, + 1.80000e-1, + 1.89000e-1, + 1.98810e-1, + 2.09140e-1, + 2.20000e-1, + 2.33580e-1, + 2.48000e-1, + 2.63510e-1, + 2.80000e-1, + 3.00000e-1, + 3.14500e-1, + 3.20000e-1, + 3.34660e-1, + 3.50000e-1, + 3.69930e-1, + 3.91000e-1, + 4.00000e-1, + 4.13990e-1, + 4.33000e-1, + 4.49680e-1, + 4.67010e-1, + 4.85000e-1, + 5.00000e-1, + 5.19620e-1, + 5.31580e-1, + 5.40000e-1, + 5.66960e-1, + 5.95280e-1, + 6.25000e-1, + 6.53150e-1, + 6.82560e-1, + 7.05000e-1, + 7.41550e-1, + 7.80000e-1, + 7.90000e-1, + 8.19450e-1, + 8.50000e-1, + 8.60000e-1, + 8.76425e-1, + 9.10000e-1, + 9.30000e-1, + 9.50000e-1, + 9.72000e-1, + 9.86000e-1, + 9.96000e-1, + 1.020000, + 1.035000, + 1.045000, + 1.071000, + 1.080000, + 1.097000, + 1.110000, + 1.123000, + 1.150000, + 1.170000, + 1.202060, + 1.235000, + 1.267080, + 1.300000, + 1.337500, + 1.370000, + 1.404560, + 1.440000, + 1.475000, + 1.500000, + 1.544340, + 1.590000, + 1.629510, + 1.670000, + 1.711970, + 1.755000, + 1.797000, + 1.840000, + 1.855390, + 1.884460, + 1.930000, + 1.974490, + 2.020000, + 2.059610, + 2.100000, + 2.130000, + 2.185310, + 2.242050, + 2.300270, + 2.360000, + 2.382370, + 2.421710, + 2.485030, + 2.550000, + 2.600000, + 2.659320, + 2.720000, + 2.767920, + 2.837990, + 2.909830, + 2.983490, + 3.059020, + 3.137330, + 3.217630, + 3.300000, + 3.380750, + 3.466330, + 3.554080, + 3.644050, + 3.736300, + 3.830880, + 3.927860, + 4.000000, + 4.129250, + 4.233782, + 4.340961, + 4.450853, + 4.563526, + 4.679053, + 4.797503, + 4.918953, + 5.043477, + 5.085681, + 5.128239, + 5.171153, + 5.214426, + 5.258061, + 5.302061, + 5.346430, + 5.391169, + 5.436284, + 5.481775, + 5.527647, + 5.573904, + 5.620547, + 5.667581, + 5.715008, + 5.762832, + 5.811056, + 5.859684, + 5.908719, + 5.958164, + 6.008022, + 6.058298, + 6.108995, + 6.160116, + 6.211665, + 6.263645, + 6.316060, + 6.368914, + 6.422210, + 6.475952, + 6.530144, + 6.584789, + 6.639892, + 6.695455, + 6.751484, + 6.807981, + 6.864952, + 6.922399, + 6.980326, + 7.038739, + 7.097640, + 7.157034, + 7.216925, + 7.277317, + 7.338215, + 7.399622, + 7.461544, + 7.523983, + 7.586945, + 7.650434, + 7.714454, + 7.779009, + 7.844105, + 7.909746, + 7.975936, + 8.042680, + 8.109982, + 8.177848, + 8.246281, + 8.315287, + 8.384871, + 8.455037, + 8.525790, + 8.597135, + 8.669077, + 8.741621, + 8.814772, + 8.888536, + 8.962916, + 9.037919, + 9.113550, + 9.189814, + 9.266715, + 9.344261, + 9.422455, + 9.501303, + 9.580812, + 9.660985, + 9.741830, + 9.823351, + 9.905554, + 9.988446, + 1.007203e1, + 1.015631e1, + 1.024130e1, + 1.032701e1, + 1.041342e1, + 1.050056e1, + 1.058843e1, + 1.067704e1, + 1.076639e1, + 1.085648e1, + 1.094733e1, + 1.103894e1, + 1.113132e1, + 1.122446e1, + 1.131839e1, + 1.141311e1, + 1.150861e1, + 1.160492e1, + 1.170203e1, + 1.179995e1, + 1.189870e1, + 1.199827e1, + 1.209867e1, + 1.219991e1, + 1.230201e1, + 1.240495e1, + 1.250876e1, + 1.261343e1, + 1.271898e1, + 1.282542e1, + 1.293274e1, + 1.304097e1, + 1.315010e1, + 1.326014e1, + 1.337110e1, + 1.348299e1, + 1.359582e1, + 1.370959e1, + 1.382431e1, + 1.394000e1, + 1.405665e1, + 1.417428e1, + 1.429289e1, + 1.441250e1, + 1.453310e1, + 1.465472e1, + 1.477735e1, + 1.490101e1, + 1.502570e1, + 1.515144e1, + 1.527823e1, + 1.540608e1, + 1.553500e1, + 1.566500e1, + 1.579609e1, + 1.592827e1, + 1.606156e1, + 1.619597e1, + 1.633150e1, + 1.646816e1, + 1.660597e1, + 1.674493e1, + 1.688506e1, + 1.702635e1, + 1.716883e1, + 1.731250e1, + 1.745738e1, + 1.760346e1, + 1.775077e1, + 1.789931e1, + 1.804910e1, + 1.820013e1, + 1.835244e1, + 1.850601e1, + 1.866087e1, + 1.881703e1, + 1.897449e1, + 1.913328e1, + 1.929339e1, + 1.945484e1, + 1.961764e1, + 1.978180e1, + 1.994734e1, + 2.011426e1, + 2.028258e1, + 2.045231e1, + 2.062345e1, + 2.079603e1, + 2.097006e1, + 2.114554e1, + 2.132249e1, + 2.150092e1, + 2.168084e1, + 2.186227e1, + 2.204522e1, + 2.222969e1, + 2.241572e1, + 2.260329e1, + 2.279244e1, + 2.298317e1, + 2.317550e1, + 2.336944e1, + 2.356499e1, + 2.376219e1, + 2.396104e1, + 2.416154e1, + 2.436373e1, + 2.456761e1, + 2.477320e1, + 2.498050e1, + 2.518954e1, + 2.540033e1, + 2.561289e1, + 2.582722e1, + 2.604335e1, + 2.626128e1, + 2.648104e1, + 2.670264e1, + 2.692609e1, + 2.715141e1, + 2.737862e1, + 2.760773e1, + 2.783875e1, + 2.807171e1, + 2.830662e1, + 2.854349e1, + 2.878235e1, + 2.902320e1, + 2.926607e1, + 2.951098e1, + 2.975793e1, + 3.000695e1, + 3.025805e1, + 3.051126e1, + 3.076658e1, + 3.102404e1, + 3.128365e1, + 3.154544e1, + 3.180942e1, + 3.207560e1, + 3.234401e1, + 3.261467e1, + 3.288760e1, + 3.316281e1, + 3.344032e1, + 3.372015e1, + 3.400233e1, + 3.428686e1, + 3.457378e1, + 3.486310e1, + 3.515484e1, + 3.544902e1, + 3.574566e1, + 3.604479e1, + 3.634642e1, + 3.665057e1, + 3.695727e1, + 3.726653e1, + 3.757838e1, + 3.789285e1, + 3.820994e1, + 3.852969e1, + 3.885211e1, + 3.917723e1, + 3.950507e1, + 3.983565e1, + 4.016900e1, + 4.050514e1, + 4.084410e1, + 4.118589e1, + 4.153054e1, + 4.187807e1, + 4.222851e1, + 4.258189e1, + 4.293822e1, + 4.329753e1, + 4.365985e1, + 4.402521e1, + 4.439361e1, + 4.476511e1, + 4.513971e1, + 4.551744e1, + 4.589834e1, + 4.628243e1, + 4.666972e1, + 4.706026e1, + 4.745407e1, + 4.785117e1, + 4.825160e1, + 4.865538e1, + 4.906253e1, + 4.947309e1, + 4.988709e1, + 5.030456e1, + 5.072551e1, + 5.114999e1, + 5.157802e1, + 5.200963e1, + 5.244486e1, + 5.288373e1, + 5.332626e1, + 5.377251e1, + 5.422248e1, + 5.467623e1, + 5.513376e1, + 5.559513e1, + 5.606036e1, + 5.652948e1, + 5.700253e1, + 5.747954e1, + 5.796053e1, + 5.844556e1, + 5.893464e1, + 5.942781e1, + 5.992511e1, + 6.042657e1, + 6.093223e1, + 6.144212e1, + 6.195628e1, + 6.247474e1, + 6.299754e1, + 6.352471e1, + 6.405630e1, + 6.459233e1, + 6.513285e1, + 6.567789e1, + 6.622749e1, + 6.678169e1, + 6.734053e1, + 6.790405e1, + 6.847228e1, + 6.904527e1, + 6.962305e1, + 7.020566e1, + 7.079316e1, + 7.138556e1, + 7.198293e1, + 7.258529e1, + 7.319270e1, + 7.380518e1, + 7.442280e1, + 7.504558e1, + 7.567357e1, + 7.630682e1, + 7.694537e1, + 7.758926e1, + 7.823854e1, + 7.889325e1, + 7.955344e1, + 8.021915e1, + 8.089044e1, + 8.156734e1, + 8.224991e1, + 8.293819e1, + 8.363223e1, + 8.433208e1, + 8.503778e1, + 8.574939e1, + 8.646695e1, + 8.719052e1, + 8.792015e1, + 8.865588e1, + 8.939776e1, + 9.014586e1, + 9.090021e1, + 9.166088e1, + 9.242791e1, + 9.320136e1, + 9.398128e1, + 9.476773e1, + 9.556076e1, + 9.636043e1, + 9.716679e1, + 9.797990e1, + 9.879981e1, + 9.962658e1, + 1.004603e2, + 1.013009e2, + 1.021486e2, + 1.030034e2, + 1.038654e2, + 1.047345e2, + 1.056110e2, + 1.064947e2, + 1.073859e2, + 1.082845e2, + 1.091907e2, + 1.101044e2, + 1.110258e2, + 1.119548e2, + 1.128917e2, + 1.138364e2, + 1.147890e2, + 1.157496e2, + 1.167182e2, + 1.176949e2, + 1.186798e2, + 1.196729e2, + 1.206744e2, + 1.216842e2, + 1.227024e2, + 1.237292e2, + 1.247646e2, + 1.258087e2, + 1.268615e2, + 1.279231e2, + 1.289935e2, + 1.300730e2, + 1.311615e2, + 1.322590e2, + 1.333658e2, + 1.344818e2, + 1.356072e2, + 1.367420e2, + 1.378862e2, + 1.390401e2, + 1.402036e2, + 1.413768e2, + 1.425599e2, + 1.437529e2, + 1.449558e2, + 1.461688e2, + 1.473920e2, + 1.486254e2, + 1.498691e2, + 1.511232e2, + 1.523879e2, + 1.536631e2, + 1.549489e2, + 1.562456e2, + 1.575531e2, + 1.588715e2, + 1.602010e2, + 1.615415e2, + 1.628933e2, + 1.642565e2, + 1.656310e2, + 1.670170e2, + 1.684146e2, + 1.698239e2, + 1.712451e2, + 1.726781e2, + 1.741231e2, + 1.755802e2, + 1.770494e2, + 1.785310e2, + 1.800250e2, + 1.815315e2, + 1.830505e2, + 1.845823e2, + 1.861269e2, + 1.876845e2, + 1.892551e2, + 1.908388e2, + 1.924358e2, + 1.940461e2, + 1.956699e2, + 1.973073e2, + 1.989584e2, + 2.006233e2, + 2.023021e2, + 2.039950e2, + 2.057021e2, + 2.074234e2, + 2.091592e2, + 2.109095e2, + 2.126744e2, + 2.144541e2, + 2.162487e2, + 2.180583e2, + 2.198830e2, + 2.217230e2, + 2.235784e2, + 2.254494e2, + 2.273360e2, + 2.292384e2, + 2.311567e2, + 2.330910e2, + 2.350416e2, + 2.370084e2, + 2.389917e2, + 2.409917e2, + 2.430083e2, + 2.450418e2, + 2.470924e2, + 2.491601e2, + 2.512451e2, + 2.533476e2, + 2.554676e2, + 2.576054e2, + 2.597611e2, + 2.619348e2, + 2.641267e2, + 2.663370e2, + 2.685657e2, + 2.708131e2, + 2.730793e2, + 2.753645e2, + 2.776688e2, + 2.799924e2, + 2.823354e2, + 2.846980e2, + 2.870804e2, + 2.894827e2, + 2.919052e2, + 2.943479e2, + 2.968110e2, + 2.992948e2, + 3.017993e2, + 3.043248e2, + 3.068715e2, + 3.094394e2, + 3.120288e2, + 3.146399e2, + 3.172729e2, + 3.199279e2, + 3.226051e2, + 3.253047e2, + 3.280269e2, + 3.307719e2, + 3.335398e2, + 3.363309e2, + 3.391454e2, + 3.419834e2, + 3.448452e2, + 3.477309e2, + 3.506408e2, + 3.535750e2, + 3.565338e2, + 3.595173e2, + 3.625258e2, + 3.655595e2, + 3.686185e2, + 3.717032e2, + 3.748137e2, + 3.779502e2, + 3.811129e2, + 3.843021e2, + 3.875180e2, + 3.907608e2, + 3.940308e2, + 3.973281e2, + 4.006530e2, + 4.040057e2, + 4.073865e2, + 4.107955e2, + 4.142332e2, + 4.176995e2, + 4.211949e2, + 4.247195e2, + 4.282736e2, + 4.318575e2, + 4.354713e2, + 4.391154e2, + 4.427900e2, + 4.464953e2, + 4.502317e2, + 4.539993e2, + 4.577984e2, + 4.616294e2, + 4.654923e2, + 4.693877e2, + 4.733156e2, + 4.772763e2, + 4.812703e2, + 4.852976e2, + 4.893587e2, + 4.934537e2, + 4.975830e2, + 5.017468e2, + 5.059455e2, + 5.101793e2, + 5.144486e2, + 5.187536e2, + 5.230946e2, + 5.274719e2, + 5.318859e2, + 5.363368e2, + 5.408249e2, + 5.453506e2, + 5.499142e2, + 5.545160e2, + 5.591563e2, + 5.638354e2, + 5.685536e2, + 5.733114e2, + 5.781089e2, + 5.829466e2, + 5.878248e2, + 5.927438e2, + 5.977040e2, + 6.027057e2, + 6.077492e2, + 6.128350e2, + 6.179633e2, + 6.231345e2, + 6.283489e2, + 6.336071e2, + 6.389092e2, + 6.442557e2, + 6.496469e2, + 6.550832e2, + 6.605651e2, + 6.660928e2, + 6.716668e2, + 6.772874e2, + 6.829550e2, + 6.886701e2, + 6.944330e2, + 7.002441e2, + 7.061038e2, + 7.120126e2, + 7.179709e2, + 7.239790e2, + 7.300373e2, + 7.361464e2, + 7.423066e2, + 7.485183e2, + 7.547820e2, + 7.610981e2, + 7.674671e2, + 7.738894e2, + 7.803654e2, + 7.868957e2, + 7.934805e2, + 8.001205e2, + 8.068160e2, + 8.135676e2, + 8.203756e2, + 8.272407e2, + 8.341631e2, + 8.411435e2, + 8.481824e2, + 8.552801e2, + 8.624372e2, + 8.696542e2, + 8.769316e2, + 8.842699e2, + 8.916696e2, + 8.991312e2, + 9.066553e2, + 9.142423e2, + 9.218928e2, + 9.296074e2, + 9.373865e2, + 9.452307e2, + 9.531405e2, + 9.611165e2, + 9.691593e2, + 9.772694e2, + 9.854473e2, + 9.936937e2, + 1.002009e3, + 1.010394e3, + 1.018849e3, + 1.027375e3, + 1.035972e3, + 1.044641e3, + 1.053383e3, + 1.062198e3, + 1.071087e3, + 1.080050e3, + 1.089088e3, + 1.098201e3, + 1.107391e3, + 1.116658e3, + 1.126002e3, + 1.135425e3, + 1.144926e3, + 1.154507e3, + 1.164168e3, + 1.173910e3, + 1.183734e3, + 1.193639e3, + 1.203628e3, + 1.213700e3, + 1.223857e3, + 1.234098e3, + 1.244425e3, + 1.254839e3, + 1.265339e3, + 1.275928e3, + 1.286605e3, + 1.297372e3, + 1.308228e3, + 1.319176e3, + 1.330215e3, + 1.341346e3, + 1.352571e3, + 1.363889e3, + 1.375303e3, + 1.386811e3, + 1.398416e3, + 1.410118e3, + 1.421919e3, + 1.433817e3, + 1.445816e3, + 1.457915e3, + 1.470115e3, + 1.482417e3, + 1.494822e3, + 1.507331e3, + 1.519944e3, + 1.532663e3, + 1.545489e3, + 1.558422e3, + 1.571463e3, + 1.584613e3, + 1.597874e3, + 1.611245e3, + 1.624728e3, + 1.638324e3, + 1.652034e3, + 1.665858e3, + 1.679798e3, + 1.693855e3, + 1.708030e3, + 1.722323e3, + 1.736735e3, + 1.751268e3, + 1.765923e3, + 1.780701e3, + 1.795602e3, + 1.810628e3, + 1.825780e3, + 1.841058e3, + 1.856464e3, + 1.871999e3, + 1.887665e3, + 1.903461e3, + 1.919389e3, + 1.935451e3, + 1.951647e3, + 1.967979e3, + 1.984447e3, + 2.001053e3, + 2.017798e3, + 2.034684e3, + 2.051710e3, + 2.068879e3, + 2.086192e3, + 2.103650e3, + 2.121253e3, + 2.139004e3, + 2.156904e3, + 2.174953e3, + 2.193153e3, + 2.211506e3, + 2.230012e3, + 2.248673e3, + 2.267490e3, + 2.286465e3, + 2.305599e3, + 2.324892e3, + 2.344347e3, + 2.363965e3, + 2.383747e3, + 2.403695e3, + 2.423809e3, + 2.444092e3, + 2.464545e3, + 2.485168e3, + 2.505965e3, + 2.526935e3, + 2.548081e3, + 2.569403e3, + 2.590904e3, + 2.612586e3, + 2.634448e3, + 2.656494e3, + 2.678723e3, + 2.701139e3, + 2.723743e3, + 2.746536e3, + 2.769519e3, + 2.792695e3, + 2.816065e3, + 2.839630e3, + 2.863392e3, + 2.887354e3, + 2.911515e3, + 2.935879e3, + 2.960447e3, + 2.985221e3, + 3.010202e3, + 3.035391e3, + 3.060792e3, + 3.086405e3, + 3.112233e3, + 3.138276e3, + 3.164538e3, + 3.191019e3, + 3.217722e3, + 3.244649e3, + 3.271800e3, + 3.299179e3, + 3.326787e3, + 3.354626e3, + 3.382698e3, + 3.411005e3, + 3.439549e3, + 3.468332e3, + 3.497355e3, + 3.526622e3, + 3.556133e3, + 3.585891e3, + 3.615898e3, + 3.646157e3, + 3.676668e3, + 3.707435e3, + 3.738460e3, + 3.769744e3, + 3.801290e3, + 3.833099e3, + 3.865175e3, + 3.897520e3, + 3.930135e3, + 3.963023e3, + 3.996186e3, + 4.029627e3, + 4.063347e3, + 4.097350e3, + 4.131637e3, + 4.166211e3, + 4.201075e3, + 4.236230e3, + 4.271679e3, + 4.307425e3, + 4.343471e3, + 4.379817e3, + 4.416468e3, + 4.453426e3, + 4.490693e3, + 4.528272e3, + 4.566165e3, + 4.604375e3, + 4.642906e3, + 4.681758e3, + 4.720936e3, + 4.760441e3, + 4.800277e3, + 4.840447e3, + 4.880952e3, + 4.921797e3, + 4.962983e3, + 5.004514e3, + 5.046393e3, + 5.088622e3, + 5.131204e3, + 5.174143e3, + 5.217441e3, + 5.261101e3, + 5.305127e3, + 5.349521e3, + 5.394287e3, + 5.439427e3, + 5.484945e3, + 5.530844e3, + 5.577127e3, + 5.623797e3, + 5.670858e3, + 5.718312e3, + 5.766164e3, + 5.814416e3, + 5.863072e3, + 5.912135e3, + 5.961609e3, + 6.011496e3, + 6.061802e3, + 6.112528e3, + 6.163678e3, + 6.215257e3, + 6.267267e3, + 6.319712e3, + 6.372597e3, + 6.425924e3, + 6.479697e3, + 6.533920e3, + 6.588597e3, + 6.643731e3, + 6.699327e3, + 6.755388e3, + 6.811918e3, + 6.868921e3, + 6.926401e3, + 6.984362e3, + 7.042809e3, + 7.101744e3, + 7.161172e3, + 7.221098e3, + 7.281525e3, + 7.342458e3, + 7.403901e3, + 7.465858e3, + 7.528334e3, + 7.591332e3, + 7.654857e3, + 7.718914e3, + 7.783507e3, + 7.848641e3, + 7.914319e3, + 7.980548e3, + 8.047330e3, + 8.114671e3, + 8.182576e3, + 8.251049e3, + 8.320095e3, + 8.389719e3, + 8.459926e3, + 8.530719e3, + 8.602106e3, + 8.674090e3, + 8.746676e3, + 8.819869e3, + 8.893675e3, + 8.968099e3, + 9.043145e3, + 9.118820e3, + 9.195127e3, + 9.272074e3, + 9.349664e3, + 9.427903e3, + 9.506797e3, + 9.586352e3, + 9.666572e3, + 9.747463e3, + 9.829031e3, + 9.911282e3, + 9.994221e3, + 1.007785e4, + 1.016219e4, + 1.024723e4, + 1.033298e4, + 1.041944e4, + 1.050664e4, + 1.059456e4, + 1.068321e4, + 1.077261e4, + 1.086276e4, + 1.095366e4, + 1.104532e4, + 1.113775e4, + 1.123095e4, + 1.132494e4, + 1.141970e4, + 1.151527e4, + 1.161163e4, + 1.170880e4, + 1.180678e4, + 1.190558e4, + 1.200521e4, + 1.210567e4, + 1.220697e4, + 1.230912e4, + 1.241212e4, + 1.251599e4, + 1.262073e4, + 1.272634e4, + 1.283283e4, + 1.294022e4, + 1.304851e4, + 1.315770e4, + 1.326780e4, + 1.337883e4, + 1.349079e4, + 1.360368e4, + 1.371752e4, + 1.383231e4, + 1.394806e4, + 1.406478e4, + 1.418247e4, + 1.430116e4, + 1.442083e4, + 1.454151e4, + 1.466319e4, + 1.478590e4, + 1.490963e4, + 1.503439e4, + 1.516020e4, + 1.528706e4, + 1.541499e4, + 1.554398e4, + 1.567406e4, + 1.580522e4, + 1.593748e4, + 1.607085e4, + 1.620533e4, + 1.634094e4, + 1.647768e4, + 1.661557e4, + 1.675461e4, + 1.689482e4, + 1.703620e4, + 1.717876e4, + 1.732251e4, + 1.746747e4, + 1.761364e4, + 1.776104e4, + 1.790966e4, + 1.805953e4, + 1.821066e4, + 1.836305e4, + 1.851671e4, + 1.867166e4, + 1.882791e4, + 1.898547e4, + 1.914434e4, + 1.930454e4, + 1.946608e4, + 1.962898e4, + 1.979324e4, + 1.995887e4, + 2.012589e4, + 2.029431e4, + 2.046413e4, + 2.063538e4, + 2.080806e4, + 2.098218e4, + 2.115777e4, + 2.133482e4, + 2.151335e4, + 2.169338e4, + 2.187491e4, + 2.205796e4, + 2.224255e4, + 2.242868e4, + 2.261636e4, + 2.280562e4, + 2.299646e4, + 2.318890e4, + 2.338295e4, + 2.357862e4, + 2.377593e4, + 2.397489e4, + 2.417552e4, + 2.437782e4, + 2.458182e4, + 2.478752e4, + 2.499495e4, + 2.520411e4, + 2.541502e4, + 2.562770e4, + 2.584215e4, + 2.605841e4, + 2.627647e4, + 2.649635e4, + 2.671808e4, + 2.694166e4, + 2.700000e4, + 2.716711e4, + 2.739445e4, + 2.762369e4, + 2.785485e4, + 2.808794e4, + 2.832299e4, + 2.850000e4, + 2.856000e4, + 2.879899e4, + 2.903999e4, + 2.928300e4, + 2.952804e4, + 2.977514e4, + 3.002430e4, + 3.027555e4, + 3.052890e4, + 3.078437e4, + 3.104198e4, + 3.130174e4, + 3.156368e4, + 3.182781e4, + 3.209415e4, + 3.236272e4, + 3.263353e4, + 3.290662e4, + 3.318198e4, + 3.345965e4, + 3.373965e4, + 3.402199e4, + 3.430669e4, + 3.459377e4, + 3.488326e4, + 3.517517e4, + 3.546952e4, + 3.576633e4, + 3.606563e4, + 3.636743e4, + 3.667176e4, + 3.697864e4, + 3.728808e4, + 3.760011e4, + 3.791476e4, + 3.823203e4, + 3.855196e4, + 3.887457e4, + 3.919988e4, + 3.952791e4, + 3.985869e4, + 4.019223e4, + 4.052857e4, + 4.086771e4, + 4.120970e4, + 4.155455e4, + 4.190229e4, + 4.225293e4, + 4.260651e4, + 4.296305e4, + 4.332257e4, + 4.368510e4, + 4.405066e4, + 4.441928e4, + 4.479099e4, + 4.516581e4, + 4.554376e4, + 4.592488e4, + 4.630919e4, + 4.669671e4, + 4.708747e4, + 4.748151e4, + 4.787884e4, + 4.827950e4, + 4.868351e4, + 4.909090e4, + 4.950170e4, + 4.991594e4, + 5.033364e4, + 5.075484e4, + 5.117957e4, + 5.160785e4, + 5.203971e4, + 5.247518e4, + 5.291430e4, + 5.335710e4, + 5.380360e4, + 5.425384e4, + 5.470784e4, + 5.516564e4, + 5.562728e4, + 5.609278e4, + 5.656217e4, + 5.703549e4, + 5.751277e4, + 5.799405e4, + 5.847935e4, + 5.896871e4, + 5.946217e4, + 5.995976e4, + 6.046151e4, + 6.096747e4, + 6.147765e4, + 6.199211e4, + 6.251086e4, + 6.303396e4, + 6.356144e4, + 6.409333e4, + 6.462968e4, + 6.517051e4, + 6.571586e4, + 6.626579e4, + 6.682031e4, + 6.737947e4, + 6.794331e4, + 6.851187e4, + 6.908519e4, + 6.966330e4, + 7.024626e4, + 7.083409e4, + 7.142684e4, + 7.202455e4, + 7.262726e4, + 7.323502e4, + 7.384786e4, + 7.446583e4, + 7.508897e4, + 7.571733e4, + 7.635094e4, + 7.698986e4, + 7.763412e4, + 7.828378e4, + 7.893887e4, + 7.950000e4, + 7.959944e4, + 8.026554e4, + 8.093721e4, + 8.161451e4, + 8.229747e4, + 8.250000e4, + 8.298615e4, + 8.368059e4, + 8.438084e4, + 8.508695e4, + 8.579897e4, + 8.651695e4, + 8.724094e4, + 8.797098e4, + 8.870714e4, + 8.944945e4, + 9.019798e4, + 9.095277e4, + 9.171388e4, + 9.248135e4, + 9.325525e4, + 9.403563e4, + 9.482253e4, + 9.561602e4, + 9.641615e4, + 9.722297e4, + 9.803655e4, + 9.885694e4, + 9.968419e4, + 1.005184e5, + 1.013595e5, + 1.022077e5, + 1.030630e5, + 1.039254e5, + 1.047951e5, + 1.056720e5, + 1.065563e5, + 1.074480e5, + 1.083471e5, + 1.092538e5, + 1.101681e5, + 1.110900e5, + 1.120196e5, + 1.129570e5, + 1.139022e5, + 1.148554e5, + 1.158165e5, + 1.167857e5, + 1.177629e5, + 1.187484e5, + 1.197421e5, + 1.207441e5, + 1.217545e5, + 1.227734e5, + 1.238008e5, + 1.248368e5, + 1.258814e5, + 1.269348e5, + 1.279970e5, + 1.290681e5, + 1.301482e5, + 1.312373e5, + 1.323355e5, + 1.334429e5, + 1.345596e5, + 1.356856e5, + 1.368210e5, + 1.379660e5, + 1.391205e5, + 1.402847e5, + 1.414586e5, + 1.426423e5, + 1.438360e5, + 1.450396e5, + 1.462533e5, + 1.474772e5, + 1.487113e5, + 1.499558e5, + 1.512106e5, + 1.524760e5, + 1.537519e5, + 1.550385e5, + 1.563359e5, + 1.576442e5, + 1.589634e5, + 1.602936e5, + 1.616349e5, + 1.629875e5, + 1.643514e5, + 1.657268e5, + 1.671136e5, + 1.685120e5, + 1.699221e5, + 1.713441e5, + 1.727779e5, + 1.742237e5, + 1.756817e5, + 1.771518e5, + 1.786342e5, + 1.801291e5, + 1.816364e5, + 1.831564e5, + 1.846891e5, + 1.862346e5, + 1.877930e5, + 1.893645e5, + 1.909491e5, + 1.925470e5, + 1.941583e5, + 1.957830e5, + 1.974214e5, + 1.990734e5, + 2.007393e5, + 2.024191e5, + 2.041130e5, + 2.058210e5, + 2.075434e5, + 2.092801e5, + 2.110314e5, + 2.127974e5, + 2.145781e5, + 2.163737e5, + 2.181844e5, + 2.200102e5, + 2.218512e5, + 2.237077e5, + 2.255797e5, + 2.274674e5, + 2.293709e5, + 2.312903e5, + 2.332258e5, + 2.351775e5, + 2.371455e5, + 2.391299e5, + 2.411310e5, + 2.431488e5, + 2.451835e5, + 2.472353e5, + 2.493042e5, + 2.513904e5, + 2.534941e5, + 2.556153e5, + 2.577544e5, + 2.599113e5, + 2.620863e5, + 2.642794e5, + 2.664910e5, + 2.687210e5, + 2.709697e5, + 2.732372e5, + 2.755237e5, + 2.778293e5, + 2.801543e5, + 2.824986e5, + 2.848626e5, + 2.872464e5, + 2.896501e5, + 2.920740e5, + 2.945181e5, + 2.969826e5, + 2.972000e5, + 2.985000e5, + 2.994678e5, + 3.019738e5, + 3.045008e5, + 3.070489e5, + 3.096183e5, + 3.122093e5, + 3.148219e5, + 3.174564e5, + 3.201129e5, + 3.227916e5, + 3.254928e5, + 3.282166e5, + 3.309631e5, + 3.337327e5, + 3.365254e5, + 3.393415e5, + 3.421812e5, + 3.450446e5, + 3.479320e5, + 3.508435e5, + 3.537795e5, + 3.567399e5, + 3.597252e5, + 3.627354e5, + 3.657708e5, + 3.688317e5, + 3.719181e5, + 3.750304e5, + 3.781687e5, + 3.813333e5, + 3.845243e5, + 3.877421e5, + 3.909868e5, + 3.942586e5, + 3.975578e5, + 4.008846e5, + 4.042393e5, + 4.076220e5, + 4.110331e5, + 4.144727e5, + 4.179410e5, + 4.214384e5, + 4.249651e5, + 4.285213e5, + 4.321072e5, + 4.357231e5, + 4.393693e5, + 4.430460e5, + 4.467535e5, + 4.504920e5, + 4.542618e5, + 4.580631e5, + 4.618963e5, + 4.657615e5, + 4.696591e5, + 4.735892e5, + 4.775523e5, + 4.815485e5, + 4.855782e5, + 4.896416e5, + 4.937390e5, + 4.978707e5, + 5.020369e5, + 5.062381e5, + 5.104743e5, + 5.147461e5, + 5.190535e5, + 5.233971e5, + 5.277769e5, + 5.321934e5, + 5.366469e5, + 5.411377e5, + 5.456660e5, + 5.502322e5, + 5.548366e5, + 5.594796e5, + 5.641614e5, + 5.688824e5, + 5.736429e5, + 5.784432e5, + 5.832837e5, + 5.881647e5, + 5.930866e5, + 5.980496e5, + 6.030542e5, + 6.081006e5, + 6.131893e5, + 6.183206e5, + 6.234948e5, + 6.287123e5, + 6.339734e5, + 6.392786e5, + 6.446282e5, + 6.500225e5, + 6.554620e5, + 6.609470e5, + 6.664779e5, + 6.720551e5, + 6.776790e5, + 6.833499e5, + 6.890683e5, + 6.948345e5, + 7.006490e5, + 7.065121e5, + 7.124243e5, + 7.183860e5, + 7.243976e5, + 7.304594e5, + 7.365720e5, + 7.427358e5, + 7.489511e5, + 7.552184e5, + 7.615382e5, + 7.679109e5, + 7.743369e5, + 7.808167e5, + 7.873507e5, + 7.939393e5, + 8.005831e5, + 8.072825e5, + 8.140380e5, + 8.208500e5, + 8.277190e5, + 8.346455e5, + 8.416299e5, + 8.486728e5, + 8.557746e5, + 8.629359e5, + 8.701570e5, + 8.774387e5, + 8.847812e5, + 8.921852e5, + 8.996511e5, + 9.071795e5, + 9.147709e5, + 9.224259e5, + 9.301449e5, + 9.379285e5, + 9.457772e5, + 9.536916e5, + 9.616723e5, + 9.697197e5, + 9.778344e5, + 9.860171e5, + 9.942682e5, + 1.002588e6, + 1.010978e6, + 1.019438e6, + 1.027969e6, + 1.036571e6, + 1.045245e6, + 1.053992e6, + 1.062812e6, + 1.071706e6, + 1.080674e6, + 1.089717e6, + 1.098836e6, + 1.108032e6, + 1.117304e6, + 1.126654e6, + 1.136082e6, + 1.145588e6, + 1.155175e6, + 1.164842e6, + 1.174589e6, + 1.184418e6, + 1.194330e6, + 1.204324e6, + 1.214402e6, + 1.224564e6, + 1.234812e6, + 1.245145e6, + 1.255564e6, + 1.266071e6, + 1.276666e6, + 1.287349e6, + 1.298122e6, + 1.308985e6, + 1.319938e6, + 1.330984e6, + 1.342122e6, + 1.353353e6, + 1.364678e6, + 1.376098e6, + 1.387613e6, + 1.399225e6, + 1.410934e6, + 1.422741e6, + 1.434646e6, + 1.446652e6, + 1.458758e6, + 1.470965e6, + 1.483274e6, + 1.495686e6, + 1.508202e6, + 1.520823e6, + 1.533550e6, + 1.546383e6, + 1.559323e6, + 1.572372e6, + 1.585530e6, + 1.598797e6, + 1.612176e6, + 1.625667e6, + 1.639271e6, + 1.652989e6, + 1.666821e6, + 1.680770e6, + 1.694834e6, + 1.709017e6, + 1.723318e6, + 1.737739e6, + 1.752281e6, + 1.766944e6, + 1.781731e6, + 1.796640e6, + 1.811675e6, + 1.826835e6, + 1.842122e6, + 1.857538e6, + 1.873082e6, + 1.888756e6, + 1.904561e6, + 1.920499e6, + 1.936570e6, + 1.952776e6, + 1.969117e6, + 1.985595e6, + 2.002210e6, + 2.018965e6, + 2.035860e6, + 2.052897e6, + 2.070076e6, + 2.087398e6, + 2.104866e6, + 2.122480e6, + 2.140241e6, + 2.158151e6, + 2.176211e6, + 2.194421e6, + 2.212785e6, + 2.231302e6, + 2.249973e6, + 2.268802e6, + 2.287787e6, + 2.306932e6, + 2.326237e6, + 2.345703e6, + 2.365332e6, + 2.385126e6, + 2.405085e6, + 2.425211e6, + 2.445505e6, + 2.465970e6, + 2.486605e6, + 2.507414e6, + 2.528396e6, + 2.549554e6, + 2.570889e6, + 2.592403e6, + 2.614096e6, + 2.635971e6, + 2.658030e6, + 2.680272e6, + 2.702701e6, + 2.725318e6, + 2.748124e6, + 2.771121e6, + 2.794310e6, + 2.817693e6, + 2.841272e6, + 2.865048e6, + 2.889023e6, + 2.913199e6, + 2.937577e6, + 2.962159e6, + 2.986947e6, + 3.011942e6, + 3.037147e6, + 3.062562e6, + 3.088190e6, + 3.114032e6, + 3.140091e6, + 3.166368e6, + 3.192864e6, + 3.219583e6, + 3.246525e6, + 3.273692e6, + 3.301087e6, + 3.328711e6, + 3.356566e6, + 3.384654e6, + 3.412978e6, + 3.441538e6, + 3.470337e6, + 3.499377e6, + 3.528661e6, + 3.558189e6, + 3.587965e6, + 3.617989e6, + 3.648265e6, + 3.678794e6, + 3.709579e6, + 3.740621e6, + 3.771924e6, + 3.803488e6, + 3.835316e6, + 3.867410e6, + 3.899773e6, + 3.932407e6, + 3.965314e6, + 3.998497e6, + 4.031957e6, + 4.065697e6, + 4.099719e6, + 4.134026e6, + 4.168620e6, + 4.203504e6, + 4.238679e6, + 4.274149e6, + 4.309916e6, + 4.345982e6, + 4.382350e6, + 4.419022e6, + 4.456001e6, + 4.493290e6, + 4.530890e6, + 4.568805e6, + 4.607038e6, + 4.645590e6, + 4.684465e6, + 4.723666e6, + 4.763194e6, + 4.803053e6, + 4.843246e6, + 4.883775e6, + 4.924643e6, + 4.965853e6, + 5.007408e6, + 5.049311e6, + 5.091564e6, + 5.134171e6, + 5.177135e6, + 5.220458e6, + 5.264143e6, + 5.308195e6, + 5.352614e6, + 5.397406e6, + 5.442572e6, + 5.488116e6, + 5.534042e6, + 5.580351e6, + 5.627049e6, + 5.674137e6, + 5.721619e6, + 5.769498e6, + 5.817778e6, + 5.866462e6, + 5.915554e6, + 5.965056e6, + 6.014972e6, + 6.065307e6, + 6.116062e6, + 6.167242e6, + 6.218851e6, + 6.270891e6, + 6.323367e6, + 6.376282e6, + 6.429639e6, + 6.483443e6, + 6.537698e6, + 6.592406e6, + 6.647573e6, + 6.703200e6, + 6.759294e6, + 6.815857e6, + 6.872893e6, + 6.930406e6, + 6.988401e6, + 7.046881e6, + 7.105850e6, + 7.165313e6, + 7.225274e6, + 7.285736e6, + 7.346704e6, + 7.408182e6, + 7.470175e6, + 7.532687e6, + 7.595721e6, + 7.659283e6, + 7.723377e6, + 7.788008e6, + 7.853179e6, + 7.918896e6, + 7.985162e6, + 8.051983e6, + 8.119363e6, + 8.187308e6, + 8.255820e6, + 8.324906e6, + 8.394570e6, + 8.464817e6, + 8.535652e6, + 8.607080e6, + 8.679105e6, + 8.751733e6, + 8.824969e6, + 8.898818e6, + 8.973284e6, + 9.048374e6, + 9.124092e6, + 9.200444e6, + 9.277435e6, + 9.355070e6, + 9.433354e6, + 9.512294e6, + 9.591895e6, + 9.672161e6, + 9.753099e6, + 9.834715e6, + 9.917013e6, + 1.000000e7, + 1.008368e7, + 1.016806e7, + 1.025315e7, + 1.033895e7, + 1.042547e7, + 1.051271e7, + 1.060068e7, + 1.068939e7, + 1.077884e7, + 1.086904e7, + 1.095999e7, + 1.105171e7, + 1.114419e7, + 1.123745e7, + 1.133148e7, + 1.142631e7, + 1.152193e7, + 1.161834e7, + 1.171557e7, + 1.181360e7, + 1.191246e7, + 1.201215e7, + 1.211267e7, + 1.221403e7, + 1.231624e7, + 1.241930e7, + 1.252323e7, + 1.262802e7, + 1.273370e7, + 1.284025e7, + 1.294770e7, + 1.305605e7, + 1.316531e7, + 1.327548e7, + 1.338657e7, + 1.349859e7, + 1.361155e7, + 1.372545e7, + 1.384031e7, + 1.395612e7, + 1.407291e7, + 1.419068e7, + 1.430943e7, + 1.442917e7, + 1.454991e7, + 1.467167e7, + 1.479444e7, + 1.491825e7, + 1.504309e7, + 1.516897e7, + 1.529590e7, + 1.542390e7, + 1.555297e7, + 1.568312e7, + 1.581436e7, + 1.594670e7, + 1.608014e7, + 1.621470e7, + 1.635039e7, + 1.648721e7, + 1.662518e7, + 1.676430e7, + 1.690459e7, + 1.704605e7, + 1.718869e7, + 1.733253e7, + 1.747757e7, + 1.762383e7, + 1.777131e7, + 1.792002e7, + 1.806998e7, + 1.822119e7, + 1.837367e7, + 1.852742e7, + 1.868246e7, + 1.883880e7, + 1.899644e7, + 1.915541e7, + 1.931570e7, + 1.947734e7, + 1.964033e7, + ] +) diff --git a/openmc/mgxs/groups.py b/openmc/mgxs/groups.py index 182402ed7fc..67b6564c5cb 100644 --- a/openmc/mgxs/groups.py +++ b/openmc/mgxs/groups.py @@ -66,7 +66,7 @@ def __hash__(self): return hash(tuple(self.group_edges)) def __repr__(self): - if hasattr(self, '_name'): + if hasattr(self, "_name"): return f"" else: return f"" @@ -77,8 +77,8 @@ def group_edges(self): @group_edges.setter def group_edges(self, edges): - cv.check_type('group edges', edges, Iterable, Real) - cv.check_greater_than('number of group edges', len(edges), 1) + cv.check_type("group edges", edges, Iterable, Real) + cv.check_greater_than("number of group edges", len(edges), 1) self._group_edges = np.array(edges) @property @@ -106,8 +106,10 @@ def get_group(self, energy): """ if self.group_edges is None: - msg = 'Unable to get energy group for energy "{0}" eV since ' \ - 'the group edges have not yet been set'.format(energy) + msg = ( + 'Unable to get energy group for energy "{0}" eV since ' + "the group edges have not yet been set".format(energy) + ) raise ValueError(msg) index = np.where(self.group_edges > energy)[0][0] @@ -135,18 +137,20 @@ def get_group_bounds(self, group): """ if self.group_edges is None: - msg = 'Unable to get energy group bounds for group "{0}" since ' \ - 'the group edges have not yet been set'.format(group) + msg = ( + 'Unable to get energy group bounds for group "{0}" since ' + "the group edges have not yet been set".format(group) + ) raise ValueError(msg) - cv.check_greater_than('group', group, 0) - cv.check_less_than('group', group, self.num_groups, equality=True) + cv.check_greater_than("group", group, 0) + cv.check_less_than("group", group, self.num_groups, equality=True) - lower = self.group_edges[self.num_groups-group] - upper = self.group_edges[self.num_groups-group+1] + lower = self.group_edges[self.num_groups - group] + upper = self.group_edges[self.num_groups - group + 1] return lower, upper - def get_group_indices(self, groups='all'): + def get_group_indices(self, groups="all"): """Returns the array indices for one or more energy groups. Parameters @@ -169,18 +173,20 @@ def get_group_indices(self, groups='all'): """ if self.group_edges is None: - msg = 'Unable to get energy group indices for groups "{0}" since ' \ - 'the group edges have not yet been set'.format(groups) + msg = ( + 'Unable to get energy group indices for groups "{0}" since ' + "the group edges have not yet been set".format(groups) + ) raise ValueError(msg) - if groups == 'all': + if groups == "all": return np.arange(self.num_groups) else: indices = np.zeros(len(groups), dtype=int) for i, group in enumerate(groups): - cv.check_greater_than('group', group, 0) - cv.check_less_than('group', group, self.num_groups, equality=True) + cv.check_greater_than("group", group, 0) + cv.check_less_than("group", group, self.num_groups, equality=True) indices[i] = group - 1 return indices @@ -213,15 +219,15 @@ def get_condensed_groups(self, coarse_groups): If the group edges have not yet been set. """ - cv.check_type('group edges', coarse_groups, Iterable) + cv.check_type("group edges", coarse_groups, Iterable) for group in coarse_groups: - cv.check_type('group edges', group, Iterable) - cv.check_length('group edges', group, 2) - cv.check_greater_than('lower group', group[0], 1, True) - cv.check_less_than('lower group', group[0], self.num_groups, True) - cv.check_greater_than('upper group', group[0], 1, True) - cv.check_less_than('upper group', group[0], self.num_groups, True) - cv.check_less_than('lower group', group[0], group[1], False) + cv.check_type("group edges", group, Iterable) + cv.check_length("group edges", group, 2) + cv.check_greater_than("lower group", group[0], 1, True) + cv.check_less_than("lower group", group[0], self.num_groups, True) + cv.check_greater_than("upper group", group[0], 1, True) + cv.check_less_than("upper group", group[0], self.num_groups, True) + cv.check_less_than("lower group", group[0], group[1], False) # Compute the group indices into the coarse group group_bounds = [group[1] for group in coarse_groups] @@ -287,7 +293,7 @@ def merge(self, other): """ if not self.can_merge(other): - raise ValueError('Unable to merge energy groups') + raise ValueError("Unable to merge energy groups") # Create deep copy to return as merged energy groups merged_groups = copy.deepcopy(self) diff --git a/openmc/mgxs/library.py b/openmc/mgxs/library.py index 12a4630bdc4..bad383fc98e 100644 --- a/openmc/mgxs/library.py +++ b/openmc/mgxs/library.py @@ -99,22 +99,21 @@ class Library: """ - def __init__(self, geometry, by_nuclide=False, - mgxs_types=None, name=''): + def __init__(self, geometry, by_nuclide=False, mgxs_types=None, name=""): - self._name = '' + self._name = "" self._geometry = None self._by_nuclide = None self._mgxs_types = [] self._domain_type = None - self._domains = 'all' + self._domains = "all" self._energy_groups = None self._num_polar = 1 self._num_azimuthal = 1 self._nuclides = None self._num_delayed_groups = 0 - self._correction = 'P0' - self._scatter_format = 'legendre' + self._correction = "P0" + self._scatter_format = "legendre" self._legendre_order = 0 self._histogram_bins = 16 self._tally_trigger = None @@ -179,7 +178,7 @@ def geometry(self): @geometry.setter def geometry(self, geometry): - cv.check_type('geometry', geometry, openmc.Geometry) + cv.check_type("geometry", geometry, openmc.Geometry) self._geometry = geometry @property @@ -188,7 +187,7 @@ def name(self): @name.setter def name(self, name): - cv.check_type('name', name, str) + cv.check_type("name", name, str) self._name = name @property @@ -197,15 +196,18 @@ def mgxs_types(self): @mgxs_types.setter def mgxs_types(self, mgxs_types): - all_mgxs_types = openmc.mgxs.MGXS_TYPES + openmc.mgxs.MDGXS_TYPES + \ - openmc.mgxs.ARBITRARY_VECTOR_TYPES + \ - openmc.mgxs.ARBITRARY_MATRIX_TYPES - if mgxs_types == 'all': + all_mgxs_types = ( + openmc.mgxs.MGXS_TYPES + + openmc.mgxs.MDGXS_TYPES + + openmc.mgxs.ARBITRARY_VECTOR_TYPES + + openmc.mgxs.ARBITRARY_MATRIX_TYPES + ) + if mgxs_types == "all": self._mgxs_types = all_mgxs_types else: - cv.check_iterable_type('mgxs_types', mgxs_types, str) + cv.check_iterable_type("mgxs_types", mgxs_types, str) for mgxs_type in mgxs_types: - cv.check_value('mgxs_type', mgxs_type, all_mgxs_types) + cv.check_value("mgxs_type", mgxs_type, all_mgxs_types) self._mgxs_types = mgxs_types @property @@ -214,11 +216,12 @@ def by_nuclide(self): @by_nuclide.setter def by_nuclide(self, by_nuclide): - cv.check_type('by_nuclide', by_nuclide, bool) + cv.check_type("by_nuclide", by_nuclide, bool) - if by_nuclide and self.domain_type == 'mesh': - raise ValueError('Unable to create MGXS library by nuclide with ' - 'mesh domain') + if by_nuclide and self.domain_type == "mesh": + raise ValueError( + "Unable to create MGXS library by nuclide with " "mesh domain" + ) self._by_nuclide = by_nuclide @@ -228,29 +231,30 @@ def domain_type(self): @domain_type.setter def domain_type(self, domain_type): - cv.check_value('domain type', domain_type, openmc.mgxs.DOMAIN_TYPES) + cv.check_value("domain type", domain_type, openmc.mgxs.DOMAIN_TYPES) - if self.by_nuclide and domain_type == 'mesh': - raise ValueError('Unable to create MGXS library by nuclide with ' - 'mesh domain') + if self.by_nuclide and domain_type == "mesh": + raise ValueError( + "Unable to create MGXS library by nuclide with " "mesh domain" + ) self._domain_type = domain_type @property def domains(self): - if self._domains == 'all': - if self.domain_type == 'material': + if self._domains == "all": + if self.domain_type == "material": return list(self.geometry.get_all_materials().values()) - elif self.domain_type == 'cell': + elif self.domain_type == "cell": return list(self.geometry.get_all_cells().values()) - elif self.domain_type in 'distribcell': + elif self.domain_type in "distribcell": return list(self.geometry.get_all_material_cells().values()) - elif self.domain_type == 'universe': + elif self.domain_type == "universe": return list(self.geometry.get_all_universes().values()) - elif self.domain_type == 'mesh': - raise ValueError('Unable to get domains for Mesh domain type') + elif self.domain_type == "mesh": + raise ValueError("Unable to get domains for Mesh domain type") else: - raise ValueError('Unable to get domains without a domain type') + raise ValueError("Unable to get domains without a domain type") else: return self._domains @@ -258,38 +262,42 @@ def domains(self): def domains(self, domains): # Use all materials, cells or universes in the geometry as domains - if domains == 'all': + if domains == "all": self._domains = domains # User specified a list of material, cell or universe domains else: - if self.domain_type == 'material': - cv.check_type('domain', domains, Iterable, openmc.Material) + if self.domain_type == "material": + cv.check_type("domain", domains, Iterable, openmc.Material) all_domains = self.geometry.get_all_materials().values() - elif self.domain_type == 'cell': - cv.check_type('domain', domains, Iterable, openmc.Cell) + elif self.domain_type == "cell": + cv.check_type("domain", domains, Iterable, openmc.Cell) all_domains = self.geometry.get_all_cells().values() - elif self.domain_type == 'distribcell': - cv.check_type('domain', domains, Iterable, openmc.Cell) + elif self.domain_type == "distribcell": + cv.check_type("domain", domains, Iterable, openmc.Cell) all_domains = self.geometry.get_all_material_cells().values() - elif self.domain_type == 'universe': - cv.check_type('domain', domains, Iterable, openmc.Universe) + elif self.domain_type == "universe": + cv.check_type("domain", domains, Iterable, openmc.Universe) all_domains = self.geometry.get_all_universes().values() - elif self.domain_type == 'mesh': - cv.check_type('domain', domains, Iterable, openmc.RegularMesh) + elif self.domain_type == "mesh": + cv.check_type("domain", domains, Iterable, openmc.RegularMesh) # The mesh and geometry are independent, so set all_domains # to the input domains all_domains = domains else: - raise ValueError('Unable to set domains with domain ' - 'type "{}"'.format(self.domain_type)) + 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)) + raise ValueError( + 'Domain "{}" could not be found in the ' + "geometry.".format(domain) + ) self._domains = list(domains) @@ -299,7 +307,7 @@ def nuclides(self): @nuclides.setter def nuclides(self, nuclides): - cv.check_iterable_type('nuclides', nuclides, str) + cv.check_iterable_type("nuclides", nuclides, str) self._nuclides = nuclides @property @@ -308,7 +316,7 @@ def energy_groups(self): @energy_groups.setter def energy_groups(self, energy_groups): - cv.check_type('energy groups', energy_groups, openmc.mgxs.EnergyGroups) + cv.check_type("energy groups", energy_groups, openmc.mgxs.EnergyGroups) self._energy_groups = energy_groups @property @@ -318,10 +326,15 @@ def num_delayed_groups(self): @num_delayed_groups.setter def num_delayed_groups(self, num_delayed_groups): - cv.check_less_than('num delayed groups', num_delayed_groups, - openmc.mgxs.MAX_DELAYED_GROUPS, equality=True) - cv.check_greater_than('num delayed groups', num_delayed_groups, 0, - equality=True) + cv.check_less_than( + "num delayed groups", + num_delayed_groups, + openmc.mgxs.MAX_DELAYED_GROUPS, + equality=True, + ) + cv.check_greater_than( + "num delayed groups", num_delayed_groups, 0, equality=True + ) self._num_delayed_groups = num_delayed_groups @property @@ -330,8 +343,8 @@ def num_polar(self): @num_polar.setter def num_polar(self, num_polar): - cv.check_type('num_polar', num_polar, Integral) - cv.check_greater_than('num_polar', num_polar, 0) + cv.check_type("num_polar", num_polar, Integral) + cv.check_greater_than("num_polar", num_polar, 0) self._num_polar = num_polar @property @@ -340,8 +353,8 @@ def num_azimuthal(self): @num_azimuthal.setter def num_azimuthal(self, num_azimuthal): - cv.check_type('num_azimuthal', num_azimuthal, Integral) - cv.check_greater_than('num_azimuthal', num_azimuthal, 0) + cv.check_type("num_azimuthal", num_azimuthal, Integral) + cv.check_greater_than("num_azimuthal", num_azimuthal, 0) self._num_azimuthal = num_azimuthal @property @@ -350,17 +363,21 @@ def correction(self): @correction.setter def correction(self, correction): - cv.check_value('correction', correction, ('P0', None)) - - if self.scatter_format == 'legendre': - if correction == 'P0' and self.legendre_order > 0: - msg = 'The P0 correction will be ignored since the ' \ - 'scattering order {} is greater than '\ - 'zero'.format(self.legendre_order) + cv.check_value("correction", correction, ("P0", None)) + + if self.scatter_format == "legendre": + if correction == "P0" and self.legendre_order > 0: + msg = ( + "The P0 correction will be ignored since the " + "scattering order {} is greater than " + "zero".format(self.legendre_order) + ) warn(msg) - elif self.scatter_format == 'histogram': - msg = 'The P0 correction will be ignored since the ' \ - 'scatter format is set to histogram' + elif self.scatter_format == "histogram": + msg = ( + "The P0 correction will be ignored since the " + "scatter format is set to histogram" + ) warn(msg) self._correction = correction @@ -371,12 +388,13 @@ def scatter_format(self): @scatter_format.setter def scatter_format(self, scatter_format): - cv.check_value('scatter_format', scatter_format, - openmc.mgxs.MU_TREATMENTS) + cv.check_value("scatter_format", scatter_format, openmc.mgxs.MU_TREATMENTS) - if scatter_format == 'histogram' and self.correction == 'P0': - msg = 'The P0 correction will be ignored since the ' \ - 'scatter format is set to histogram' + if scatter_format == "histogram" and self.correction == "P0": + msg = ( + "The P0 correction will be ignored since the " + "scatter format is set to histogram" + ) warn(msg) self.correction = None @@ -388,21 +406,24 @@ def legendre_order(self): @legendre_order.setter def legendre_order(self, legendre_order): - cv.check_type('legendre_order', legendre_order, Integral) - cv.check_greater_than('legendre_order', legendre_order, 0, - equality=True) - cv.check_less_than('legendre_order', legendre_order, 10, equality=True) - - if self.scatter_format == 'legendre': - if self.correction == 'P0' and legendre_order > 0: - msg = 'The P0 correction will be ignored since the ' \ - 'scattering order {} is greater than '\ - 'zero'.format(legendre_order) + cv.check_type("legendre_order", legendre_order, Integral) + cv.check_greater_than("legendre_order", legendre_order, 0, equality=True) + cv.check_less_than("legendre_order", legendre_order, 10, equality=True) + + if self.scatter_format == "legendre": + if self.correction == "P0" and legendre_order > 0: + msg = ( + "The P0 correction will be ignored since the " + "scattering order {} is greater than " + "zero".format(legendre_order) + ) warn(msg, RuntimeWarning) self.correction = None - elif self.scatter_format == 'histogram': - msg = 'The legendre order will be ignored since the ' \ - 'scatter format is set to histogram' + elif self.scatter_format == "histogram": + msg = ( + "The legendre order will be ignored since the " + "scatter format is set to histogram" + ) warn(msg) self._legendre_order = legendre_order @@ -413,18 +434,22 @@ def histogram_bins(self): @histogram_bins.setter def histogram_bins(self, histogram_bins): - cv.check_type('histogram_bins', histogram_bins, Integral) - cv.check_greater_than('histogram_bins', histogram_bins, 0) - - if self.scatter_format == 'legendre': - msg = 'The histogram bins will be ignored since the ' \ - 'scatter format is set to legendre' + cv.check_type("histogram_bins", histogram_bins, Integral) + cv.check_greater_than("histogram_bins", histogram_bins, 0) + + if self.scatter_format == "legendre": + msg = ( + "The histogram bins will be ignored since the " + "scatter format is set to legendre" + ) warn(msg) - elif self.scatter_format == 'histogram': - if self.correction == 'P0': - msg = 'The P0 correction will be ignored since ' \ - 'a histogram representation of the scattering '\ - 'kernel is requested' + elif self.scatter_format == "histogram": + if self.correction == "P0": + msg = ( + "The P0 correction will be ignored since " + "a histogram representation of the scattering " + "kernel is requested" + ) warn(msg, RuntimeWarning) self.correction = None @@ -436,7 +461,7 @@ def tally_trigger(self): @tally_trigger.setter def tally_trigger(self, tally_trigger): - cv.check_type('tally trigger', tally_trigger, openmc.Trigger) + cv.check_type("tally trigger", tally_trigger, openmc.Trigger) self._tally_trigger = tally_trigger @property @@ -445,7 +470,7 @@ def estimator(self): @estimator.setter def estimator(self, estimator): - cv.check_value('estimator', estimator, ESTIMATOR_TYPES) + cv.check_value("estimator", estimator, ESTIMATOR_TYPES) self._estimator = estimator @property @@ -480,7 +505,7 @@ def sparse(self, sparse): """ - cv.check_type('sparse', sparse, bool) + cv.check_type("sparse", sparse, bool) # Sparsify or densify each MGXS in the Library for domain in self.domains: @@ -506,12 +531,18 @@ def build_library(self): for mgxs_type in self.mgxs_types: if mgxs_type in openmc.mgxs.MDGXS_TYPES: mgxs = openmc.mgxs.MDGXS.get_mgxs( - mgxs_type, name=self.name, num_polar=self.num_polar, - num_azimuthal=self.num_azimuthal) + mgxs_type, + name=self.name, + num_polar=self.num_polar, + num_azimuthal=self.num_azimuthal, + ) else: mgxs = openmc.mgxs.MGXS.get_mgxs( - mgxs_type, name=self.name, num_polar=self.num_polar, - num_azimuthal=self.num_azimuthal) + mgxs_type, + name=self.name, + num_polar=self.num_polar, + num_azimuthal=self.num_azimuthal, + ) mgxs.domain = domain mgxs.domain_type = self.domain_type @@ -524,8 +555,7 @@ def build_library(self): if self.num_delayed_groups == 0: mgxs.delayed_groups = None else: - delayed_groups \ - = list(range(1, self.num_delayed_groups + 1)) + delayed_groups = list(range(1, self.num_delayed_groups + 1)) mgxs.delayed_groups = delayed_groups # If a tally trigger was specified, add it to the MGXS @@ -547,7 +577,8 @@ def build_library(self): if self.nuclides: if domain_nuclides: mgxs.nuclides = [ - nuclide for nuclide in self.nuclides + nuclide + for nuclide in self.nuclides if nuclide in domain_nuclides ] + ["total"] else: @@ -571,7 +602,7 @@ def add_to_tallies_file(self, tallies_file, merge=True): """ - cv.check_type('tallies_file', tallies_file, openmc.Tallies) + cv.check_type("tallies_file", tallies_file, openmc.Tallies) # Add tallies from each MGXS for each domain and mgxs type for domain in self.domains: @@ -582,8 +613,9 @@ def add_to_tallies_file(self, tallies_file, merge=True): if self.num_delayed_groups == 0: mgxs.delayed_groups = None else: - mgxs.delayed_groups \ - = list(range(1, self.num_delayed_groups + 1)) + mgxs.delayed_groups = list( + range(1, self.num_delayed_groups + 1) + ) for tally in mgxs.tallies.values(): tallies_file.append(tally, merge=merge) @@ -610,18 +642,20 @@ def load_from_statepoint(self, statepoint): """ - cv.check_type('statepoint', statepoint, openmc.StatePoint) + cv.check_type("statepoint", statepoint, openmc.StatePoint) if statepoint.summary is None: - msg = 'Unable to load data from a statepoint which has not been ' \ - 'linked with a summary file' + msg = ( + "Unable to load data from a statepoint which has not been " + "linked with a summary file" + ) raise ValueError(msg) self._sp_filename = statepoint._f.filename self._geometry = statepoint.summary.geometry self._atomic_weight_ratios = statepoint.summary.nuclides - if statepoint.run_mode == 'eigenvalue': + if statepoint.run_mode == "eigenvalue": self._keff = statepoint.keff.n # Load tallies for each MGXS for each domain and mgxs type @@ -661,14 +695,14 @@ def get_mgxs(self, domain, mgxs_type): """ - if self.domain_type == 'material': - cv.check_type('domain', domain, (openmc.Material, Integral)) - elif self.domain_type == 'cell' or self.domain_type == 'distribcell': - cv.check_type('domain', domain, (openmc.Cell, Integral)) - elif self.domain_type == 'universe': - cv.check_type('domain', domain, (openmc.Universe, Integral)) - elif self.domain_type == 'mesh': - cv.check_type('domain', domain, (openmc.RegularMesh, Integral)) + if self.domain_type == "material": + cv.check_type("domain", domain, (openmc.Material, Integral)) + elif self.domain_type == "cell" or self.domain_type == "distribcell": + cv.check_type("domain", domain, (openmc.Cell, Integral)) + elif self.domain_type == "universe": + cv.check_type("domain", domain, (openmc.Universe, Integral)) + elif self.domain_type == "mesh": + cv.check_type("domain", domain, (openmc.RegularMesh, Integral)) # Check that requested domain is included in library if isinstance(domain, Integral): @@ -677,8 +711,9 @@ def get_mgxs(self, domain, mgxs_type): if domain_id == domain.id: break else: - msg = 'Unable to find MGXS for "{0}" "{1}" in ' \ - 'library'.format(self.domain_type, domain_id) + msg = 'Unable to find MGXS for "{0}" "{1}" in ' "library".format( + self.domain_type, domain_id + ) raise ValueError(msg) else: domain_id = domain.id @@ -721,17 +756,26 @@ def get_condensed_library(self, coarse_groups): """ if self.sp_filename is None: - msg = 'Unable to get a condensed coarse group cross section ' \ - 'library since the statepoint has not yet been loaded' + msg = ( + "Unable to get a condensed coarse group cross section " + "library since the statepoint has not yet been loaded" + ) raise ValueError(msg) - cv.check_type('coarse_groups', coarse_groups, openmc.mgxs.EnergyGroups) - cv.check_less_than('coarse groups', coarse_groups.num_groups, - self.num_groups, equality=True) - cv.check_value('upper coarse energy', coarse_groups.group_edges[-1], - [self.energy_groups.group_edges[-1]]) - cv.check_value('lower coarse energy', coarse_groups.group_edges[0], - [self.energy_groups.group_edges[0]]) + cv.check_type("coarse_groups", coarse_groups, openmc.mgxs.EnergyGroups) + cv.check_less_than( + "coarse groups", coarse_groups.num_groups, self.num_groups, equality=True + ) + cv.check_value( + "upper coarse energy", + coarse_groups.group_edges[-1], + [self.energy_groups.group_edges[-1]], + ) + cv.check_value( + "lower coarse energy", + coarse_groups.group_edges[0], + [self.energy_groups.group_edges[0]], + ) # Clone this Library to initialize the condensed version condensed_library = copy.deepcopy(self) @@ -773,15 +817,17 @@ def get_subdomain_avg_library(self): """ if self.sp_filename is None: - msg = 'Unable to get a subdomain-averaged cross section ' \ - 'library since the statepoint has not yet been loaded' + msg = ( + "Unable to get a subdomain-averaged cross section " + "library since the statepoint has not yet been loaded" + ) raise ValueError(msg) # Clone this Library to initialize the subdomain-averaged version subdomain_avg_library = copy.deepcopy(self) - if subdomain_avg_library.domain_type == 'distribcell': - subdomain_avg_library.domain_type = 'cell' + if subdomain_avg_library.domain_type == "distribcell": + subdomain_avg_library.domain_type = "cell" else: return subdomain_avg_library @@ -789,15 +835,22 @@ def get_subdomain_avg_library(self): for domain in self.domains: for mgxs_type in self.mgxs_types: mgxs = subdomain_avg_library.get_mgxs(domain, mgxs_type) - if mgxs.domain_type == 'distribcell': + if mgxs.domain_type == "distribcell": avg_mgxs = mgxs.get_subdomain_avg_xs() subdomain_avg_library.all_mgxs[domain.id][mgxs_type] = avg_mgxs return subdomain_avg_library - def build_hdf5_store(self, filename='mgxs.h5', directory='mgxs', - subdomains='all', nuclides='all', xs_type='macro', - row_column='inout', libver='earliest'): + def build_hdf5_store( + self, + filename="mgxs.h5", + directory="mgxs", + subdomains="all", + nuclides="all", + xs_type="macro", + row_column="inout", + libver="earliest", + ): """Export the multi-group cross section library to an HDF5 binary file. This method constructs an HDF5 file which stores the library's @@ -847,12 +900,14 @@ def build_hdf5_store(self, filename='mgxs.h5', directory='mgxs', """ if self.sp_filename is None: - msg = 'Unable to export multi-group cross section library ' \ - 'since a statepoint has not yet been loaded' + msg = ( + "Unable to export multi-group cross section library " + "since a statepoint has not yet been loaded" + ) raise ValueError(msg) - cv.check_type('filename', filename, str) - cv.check_type('directory', directory, str) + cv.check_type("filename", filename, str) + cv.check_type("directory", directory, str) import h5py @@ -862,9 +917,9 @@ def build_hdf5_store(self, filename='mgxs.h5', directory='mgxs', # Add an attribute for the number of energy groups to the HDF5 file full_filename = os.path.join(directory, filename) - full_filename = full_filename.replace(' ', '-') - f = h5py.File(full_filename, 'w', libver=libver) - f.attrs['# groups'] = self.num_groups + full_filename = full_filename.replace(" ", "-") + f = h5py.File(full_filename, "w", libver=libver) + f.attrs["# groups"] = self.num_groups f.close() # Export MGXS for each domain and mgxs type to an HDF5 file @@ -872,13 +927,18 @@ def build_hdf5_store(self, filename='mgxs.h5', directory='mgxs', for mgxs_type in self.mgxs_types: mgxs = self.all_mgxs[domain.id][mgxs_type] - if subdomains == 'avg': + if subdomains == "avg": mgxs = mgxs.get_subdomain_avg_xs() - mgxs.build_hdf5_store(filename, directory, xs_type=xs_type, - nuclides=nuclides, row_column=row_column) + mgxs.build_hdf5_store( + filename, + directory, + xs_type=xs_type, + nuclides=nuclides, + row_column=row_column, + ) - def dump_to_file(self, filename='mgxs', directory='mgxs'): + def dump_to_file(self, filename="mgxs", directory="mgxs"): """Store this Library object in a pickle binary file. Parameters @@ -894,22 +954,22 @@ def dump_to_file(self, filename='mgxs', directory='mgxs'): """ - cv.check_type('filename', filename, str) - cv.check_type('directory', directory, str) + cv.check_type("filename", filename, str) + cv.check_type("directory", directory, str) # Make directory if it does not exist if not os.path.exists(directory): os.makedirs(directory) - full_filename = os.path.join(directory, f'{filename}.pkl') - full_filename = full_filename.replace(' ', '-') + full_filename = os.path.join(directory, f"{filename}.pkl") + full_filename = full_filename.replace(" ", "-") # Load and return pickled Library object - with open(full_filename, 'wb') as f: + with open(full_filename, "wb") as f: pickle.dump(self, f) @staticmethod - def load_from_file(filename='mgxs', directory='mgxs'): + def load_from_file(filename="mgxs", directory="mgxs"): """Load a Library object from a pickle binary file. Parameters @@ -930,22 +990,29 @@ def load_from_file(filename='mgxs', directory='mgxs'): """ - cv.check_type('filename', filename, str) - cv.check_type('directory', directory, str) + cv.check_type("filename", filename, str) + cv.check_type("directory", directory, str) # Make directory if it does not exist if not os.path.exists(directory): os.makedirs(directory) - full_filename = os.path.join(directory, filename + '.pkl') - full_filename = full_filename.replace(' ', '-') + full_filename = os.path.join(directory, filename + ".pkl") + full_filename = full_filename.replace(" ", "-") # Load and return pickled Library object - with open(full_filename, 'rb') as f: + with open(full_filename, "rb") as f: return pickle.load(f) - def get_xsdata(self, domain, xsdata_name, nuclide='total', xs_type='macro', - subdomain=None, apply_domain_chi=False): + def get_xsdata( + self, + domain, + xsdata_name, + nuclide="total", + xs_type="macro", + subdomain=None, + apply_domain_chi=False, + ): """Generates an openmc.XSdata object describing a multi-group cross section dataset for writing to an openmc.MGXSLibrary object. @@ -999,259 +1066,299 @@ def get_xsdata(self, domain, xsdata_name, nuclide='total', xs_type='macro', """ - cv.check_type('domain', domain, (openmc.Material, openmc.Cell, - openmc.Universe, openmc.RegularMesh)) - cv.check_type('xsdata_name', xsdata_name, str) - cv.check_type('nuclide', nuclide, str) - cv.check_value('xs_type', xs_type, ['macro', 'micro']) + cv.check_type( + "domain", + domain, + (openmc.Material, openmc.Cell, openmc.Universe, openmc.RegularMesh), + ) + cv.check_type("xsdata_name", xsdata_name, str) + cv.check_type("nuclide", nuclide, str) + cv.check_value("xs_type", xs_type, ["macro", "micro"]) if subdomain is not None: - cv.check_iterable_type('subdomain', subdomain, Integral, - max_depth=3) + cv.check_iterable_type("subdomain", subdomain, Integral, max_depth=3) # Make sure statepoint has been loaded if self._sp_filename is None: - msg = 'A StatePoint must be loaded before calling ' \ - 'the create_mg_library() function' + msg = ( + "A StatePoint must be loaded before calling " + "the create_mg_library() function" + ) raise ValueError(msg) # If gathering material-specific data, set the xs_type to macro if not self.by_nuclide: - xs_type = 'macro' + xs_type = "macro" # Build & add metadata to XSdata object name = xsdata_name - if nuclide != 'total': - name += '_' + nuclide + if nuclide != "total": + name += "_" + nuclide if self.num_polar > 1 or self.num_azimuthal > 1: - representation = 'angle' + representation = "angle" else: - representation = 'isotropic' - xsdata = openmc.XSdata(name, self.energy_groups, - representation=representation) + representation = "isotropic" + xsdata = openmc.XSdata(name, self.energy_groups, representation=representation) xsdata.num_delayed_groups = self.num_delayed_groups if self.num_polar > 1 or self.num_azimuthal > 1: xsdata.num_polar = self.num_polar xsdata.num_azimuthal = self.num_azimuthal - if nuclide != 'total': + if nuclide != "total": xsdata.atomic_weight_ratio = self._atomic_weight_ratios[nuclide] if subdomain is None: - subdomain = 'all' + subdomain = "all" else: subdomain = [subdomain] # Now get xs data itself - if 'nu-transport' in self.mgxs_types and self.correction == 'P0': - mymgxs = self.get_mgxs(domain, 'nu-transport') - xsdata.set_total_mgxs(mymgxs, xs_type=xs_type, nuclide=[nuclide], - subdomain=subdomain) - - elif 'transport' in self.mgxs_types and self.correction == 'P0': - mymgxs = self.get_mgxs(domain, 'transport') - xsdata.set_total_mgxs(mymgxs, xs_type=xs_type, nuclide=[nuclide], - subdomain=subdomain) - - elif 'total' in self.mgxs_types: - mymgxs = self.get_mgxs(domain, 'total') - xsdata.set_total_mgxs(mymgxs, xs_type=xs_type, nuclide=[nuclide], - subdomain=subdomain) - - if 'absorption' in self.mgxs_types: - mymgxs = self.get_mgxs(domain, 'absorption') - xsdata.set_absorption_mgxs(mymgxs, xs_type=xs_type, - nuclide=[nuclide], - subdomain=subdomain) - - if 'fission' in self.mgxs_types: - mymgxs = self.get_mgxs(domain, 'fission') - xsdata.set_fission_mgxs(mymgxs, xs_type=xs_type, - nuclide=[nuclide], subdomain=subdomain) - - if 'kappa-fission' in self.mgxs_types: - mymgxs = self.get_mgxs(domain, 'kappa-fission') - xsdata.set_kappa_fission_mgxs(mymgxs, xs_type=xs_type, - nuclide=[nuclide], - subdomain=subdomain) - - if 'inverse-velocity' in self.mgxs_types: - mymgxs = self.get_mgxs(domain, 'inverse-velocity') - xsdata.set_inverse_velocity_mgxs(mymgxs, xs_type=xs_type, - nuclide=[nuclide], - subdomain=subdomain) - - if 'nu-fission matrix' in self.mgxs_types: - mymgxs = self.get_mgxs(domain, 'nu-fission matrix') - xsdata.set_nu_fission_mgxs(mymgxs, xs_type=xs_type, - nuclide=[nuclide], - subdomain=subdomain) - - if 'chi' in self.mgxs_types: - mymgxs = self.get_mgxs(domain, 'chi') + if "nu-transport" in self.mgxs_types and self.correction == "P0": + mymgxs = self.get_mgxs(domain, "nu-transport") + xsdata.set_total_mgxs( + mymgxs, xs_type=xs_type, nuclide=[nuclide], subdomain=subdomain + ) + + elif "transport" in self.mgxs_types and self.correction == "P0": + mymgxs = self.get_mgxs(domain, "transport") + xsdata.set_total_mgxs( + mymgxs, xs_type=xs_type, nuclide=[nuclide], subdomain=subdomain + ) + + elif "total" in self.mgxs_types: + mymgxs = self.get_mgxs(domain, "total") + xsdata.set_total_mgxs( + mymgxs, xs_type=xs_type, nuclide=[nuclide], subdomain=subdomain + ) + + if "absorption" in self.mgxs_types: + mymgxs = self.get_mgxs(domain, "absorption") + xsdata.set_absorption_mgxs( + mymgxs, xs_type=xs_type, nuclide=[nuclide], subdomain=subdomain + ) + + if "fission" in self.mgxs_types: + mymgxs = self.get_mgxs(domain, "fission") + xsdata.set_fission_mgxs( + mymgxs, xs_type=xs_type, nuclide=[nuclide], subdomain=subdomain + ) + + if "kappa-fission" in self.mgxs_types: + mymgxs = self.get_mgxs(domain, "kappa-fission") + xsdata.set_kappa_fission_mgxs( + mymgxs, xs_type=xs_type, nuclide=[nuclide], subdomain=subdomain + ) + + if "inverse-velocity" in self.mgxs_types: + mymgxs = self.get_mgxs(domain, "inverse-velocity") + xsdata.set_inverse_velocity_mgxs( + mymgxs, xs_type=xs_type, nuclide=[nuclide], subdomain=subdomain + ) + + if "nu-fission matrix" in self.mgxs_types: + mymgxs = self.get_mgxs(domain, "nu-fission matrix") + xsdata.set_nu_fission_mgxs( + mymgxs, xs_type=xs_type, nuclide=[nuclide], subdomain=subdomain + ) + + if "chi" in self.mgxs_types: + mymgxs = self.get_mgxs(domain, "chi") if apply_domain_chi and nuclide != "total": nuc = "sum" else: nuc = nuclide - xsdata.set_chi_mgxs(mymgxs, xs_type=xs_type, nuclide=[nuc], - subdomain=subdomain) + xsdata.set_chi_mgxs( + mymgxs, xs_type=xs_type, nuclide=[nuc], subdomain=subdomain + ) - if 'chi-prompt' in self.mgxs_types: - mymgxs = self.get_mgxs(domain, 'chi-prompt') + if "chi-prompt" in self.mgxs_types: + mymgxs = self.get_mgxs(domain, "chi-prompt") if apply_domain_chi and nuclide != "total": nuc = "sum" else: nuc = nuclide - xsdata.set_chi_prompt_mgxs(mymgxs, xs_type=xs_type, - nuclide=[nuc], subdomain=subdomain) + xsdata.set_chi_prompt_mgxs( + mymgxs, xs_type=xs_type, nuclide=[nuc], subdomain=subdomain + ) - if 'chi-delayed' in self.mgxs_types: - mymgxs = self.get_mgxs(domain, 'chi-delayed') + if "chi-delayed" in self.mgxs_types: + mymgxs = self.get_mgxs(domain, "chi-delayed") if apply_domain_chi and nuclide != "total": nuc = "sum" else: nuc = nuclide - xsdata.set_chi_delayed_mgxs(mymgxs, xs_type=xs_type, - nuclide=[nuc], subdomain=subdomain) - - if 'nu-fission' in self.mgxs_types: - mymgxs = self.get_mgxs(domain, 'nu-fission') - xsdata.set_nu_fission_mgxs(mymgxs, xs_type=xs_type, - nuclide=[nuclide], - subdomain=subdomain) - - if 'prompt-nu-fission' in self.mgxs_types: - mymgxs = self.get_mgxs(domain, 'prompt-nu-fission') - xsdata.set_prompt_nu_fission_mgxs(mymgxs, xs_type=xs_type, - nuclide=[nuclide], - subdomain=subdomain) - - if 'prompt-nu-fission matrix' in self.mgxs_types: - mymgxs = self.get_mgxs(domain, 'prompt-nu-fission matrix') - xsdata.set_prompt_nu_fission_mgxs(mymgxs, xs_type=xs_type, - nuclide=[nuclide], - subdomain=subdomain) - - if 'delayed-nu-fission' in self.mgxs_types: - mymgxs = self.get_mgxs(domain, 'delayed-nu-fission') - xsdata.set_delayed_nu_fission_mgxs(mymgxs, xs_type=xs_type, - nuclide=[nuclide], - subdomain=subdomain) - - if 'delayed-nu-fission matrix' in self.mgxs_types: - mymgxs = self.get_mgxs(domain, 'delayed-nu-fission matrix') - xsdata.set_delayed_nu_fission_mgxs(mymgxs, xs_type=xs_type, - nuclide=[nuclide], - subdomain=subdomain) - - if 'beta' in self.mgxs_types: - mymgxs = self.get_mgxs(domain, 'beta') - xsdata.set_beta_mgxs(mymgxs, xs_type=xs_type, nuclide=[nuclide], - subdomain=subdomain) - - if 'decay-rate' in self.mgxs_types: - mymgxs = self.get_mgxs(domain, 'decay-rate') - xsdata.set_decay_rate_mgxs(mymgxs, xs_type=xs_type, nuclide=[nuclide], - subdomain=subdomain) + xsdata.set_chi_delayed_mgxs( + mymgxs, xs_type=xs_type, nuclide=[nuc], subdomain=subdomain + ) + + if "nu-fission" in self.mgxs_types: + mymgxs = self.get_mgxs(domain, "nu-fission") + xsdata.set_nu_fission_mgxs( + mymgxs, xs_type=xs_type, nuclide=[nuclide], subdomain=subdomain + ) + + if "prompt-nu-fission" in self.mgxs_types: + mymgxs = self.get_mgxs(domain, "prompt-nu-fission") + xsdata.set_prompt_nu_fission_mgxs( + mymgxs, xs_type=xs_type, nuclide=[nuclide], subdomain=subdomain + ) + + if "prompt-nu-fission matrix" in self.mgxs_types: + mymgxs = self.get_mgxs(domain, "prompt-nu-fission matrix") + xsdata.set_prompt_nu_fission_mgxs( + mymgxs, xs_type=xs_type, nuclide=[nuclide], subdomain=subdomain + ) + + if "delayed-nu-fission" in self.mgxs_types: + mymgxs = self.get_mgxs(domain, "delayed-nu-fission") + xsdata.set_delayed_nu_fission_mgxs( + mymgxs, xs_type=xs_type, nuclide=[nuclide], subdomain=subdomain + ) + + if "delayed-nu-fission matrix" in self.mgxs_types: + mymgxs = self.get_mgxs(domain, "delayed-nu-fission matrix") + xsdata.set_delayed_nu_fission_mgxs( + mymgxs, xs_type=xs_type, nuclide=[nuclide], subdomain=subdomain + ) + + if "beta" in self.mgxs_types: + mymgxs = self.get_mgxs(domain, "beta") + xsdata.set_beta_mgxs( + mymgxs, xs_type=xs_type, nuclide=[nuclide], subdomain=subdomain + ) + + if "decay-rate" in self.mgxs_types: + mymgxs = self.get_mgxs(domain, "decay-rate") + xsdata.set_decay_rate_mgxs( + mymgxs, xs_type=xs_type, nuclide=[nuclide], subdomain=subdomain + ) # If multiplicity matrix is available, prefer that - if 'multiplicity matrix' in self.mgxs_types: - mymgxs = self.get_mgxs(domain, 'multiplicity matrix') - xsdata.set_multiplicity_matrix_mgxs(mymgxs, xs_type=xs_type, - nuclide=[nuclide], - subdomain=subdomain) + if "multiplicity matrix" in self.mgxs_types: + mymgxs = self.get_mgxs(domain, "multiplicity matrix") + xsdata.set_multiplicity_matrix_mgxs( + mymgxs, xs_type=xs_type, nuclide=[nuclide], subdomain=subdomain + ) using_multiplicity = True # multiplicity will fall back to using scatter and nu-scatter - elif 'scatter matrix' in self.mgxs_types and \ - 'nu-scatter matrix' in self.mgxs_types: - scatt_mgxs = self.get_mgxs(domain, 'scatter matrix') - nuscatt_mgxs = self.get_mgxs(domain, 'nu-scatter matrix') - xsdata.set_multiplicity_matrix_mgxs(nuscatt_mgxs, scatt_mgxs, - xs_type=xs_type, - nuclide=[nuclide], - subdomain=subdomain) + elif ( + "scatter matrix" in self.mgxs_types + and "nu-scatter matrix" in self.mgxs_types + ): + scatt_mgxs = self.get_mgxs(domain, "scatter matrix") + nuscatt_mgxs = self.get_mgxs(domain, "nu-scatter matrix") + xsdata.set_multiplicity_matrix_mgxs( + nuscatt_mgxs, + scatt_mgxs, + xs_type=xs_type, + nuclide=[nuclide], + subdomain=subdomain, + ) using_multiplicity = True # multiplicity will fall back to using scatter and nu-scatter - elif 'consistent scatter matrix' in self.mgxs_types and \ - 'consistent nu-scatter matrix' in self.mgxs_types: - scatt_mgxs = self.get_mgxs(domain, 'consistent scatter matrix') - nuscatt_mgxs = \ - self.get_mgxs(domain, 'consistent nu-scatter matrix') - xsdata.set_multiplicity_matrix_mgxs(nuscatt_mgxs, scatt_mgxs, - xs_type=xs_type, - nuclide=[nuclide], - subdomain=subdomain) + elif ( + "consistent scatter matrix" in self.mgxs_types + and "consistent nu-scatter matrix" in self.mgxs_types + ): + scatt_mgxs = self.get_mgxs(domain, "consistent scatter matrix") + nuscatt_mgxs = self.get_mgxs(domain, "consistent nu-scatter matrix") + xsdata.set_multiplicity_matrix_mgxs( + nuscatt_mgxs, + scatt_mgxs, + xs_type=xs_type, + nuclide=[nuclide], + subdomain=subdomain, + ) using_multiplicity = True else: using_multiplicity = False if using_multiplicity: - if 'nu-scatter matrix' in self.mgxs_types: - nuscatt_mgxs = self.get_mgxs(domain, 'nu-scatter matrix') + if "nu-scatter matrix" in self.mgxs_types: + nuscatt_mgxs = self.get_mgxs(domain, "nu-scatter matrix") else: - nuscatt_mgxs = \ - self.get_mgxs(domain, 'consistent nu-scatter matrix') - xsdata.set_scatter_matrix_mgxs(nuscatt_mgxs, xs_type=xs_type, - nuclide=[nuclide], - subdomain=subdomain) + nuscatt_mgxs = self.get_mgxs(domain, "consistent nu-scatter matrix") + xsdata.set_scatter_matrix_mgxs( + nuscatt_mgxs, xs_type=xs_type, nuclide=[nuclide], subdomain=subdomain + ) else: - if 'nu-scatter matrix' in self.mgxs_types or \ - 'consistent nu-scatter matrix' in self.mgxs_types: - if 'nu-scatter matrix' in self.mgxs_types: - nuscatt_mgxs = self.get_mgxs(domain, 'nu-scatter matrix') + if ( + "nu-scatter matrix" in self.mgxs_types + or "consistent nu-scatter matrix" in self.mgxs_types + ): + if "nu-scatter matrix" in self.mgxs_types: + nuscatt_mgxs = self.get_mgxs(domain, "nu-scatter matrix") else: - nuscatt_mgxs = \ - self.get_mgxs(domain, 'consistent nu-scatter matrix') - xsdata.set_scatter_matrix_mgxs(nuscatt_mgxs, xs_type=xs_type, - nuclide=[nuclide], - subdomain=subdomain) + nuscatt_mgxs = self.get_mgxs(domain, "consistent nu-scatter matrix") + xsdata.set_scatter_matrix_mgxs( + nuscatt_mgxs, + xs_type=xs_type, + nuclide=[nuclide], + subdomain=subdomain, + ) # Since we are not using multiplicity, then # scattering multiplication (nu-scatter) must be # accounted for approximately by using an adjusted # absorption cross section. - if 'total' in self.mgxs_types or 'transport' in self.mgxs_types: - if xsdata.scatter_format == 'legendre': + if "total" in self.mgxs_types or "transport" in self.mgxs_types: + if xsdata.scatter_format == "legendre": for i in range(len(xsdata.temperatures)): - if representation == 'isotropic': - xsdata._absorption[i] = \ - np.subtract(xsdata._total[i], np.sum( - xsdata._scatter_matrix[i][:, :, 0], - axis=1)) - elif representation == 'angle': - xsdata._absorption[i] = \ - np.subtract(xsdata._total[i], np.sum( - xsdata._scatter_matrix[i][:, :, :, :, 0], - axis=3)) - elif xsdata.scatter_format == 'histogram': + if representation == "isotropic": + xsdata._absorption[i] = np.subtract( + xsdata._total[i], + np.sum(xsdata._scatter_matrix[i][:, :, 0], axis=1), + ) + elif representation == "angle": + xsdata._absorption[i] = np.subtract( + xsdata._total[i], + np.sum( + xsdata._scatter_matrix[i][:, :, :, :, 0], axis=3 + ), + ) + elif xsdata.scatter_format == "histogram": for i in range(len(xsdata.temperatures)): - if representation == 'isotropic': - xsdata._absorption[i] = \ - np.subtract(xsdata._total[i], np.sum(np.sum( - xsdata._scatter_matrix[i][:, :, :], - axis=2), axis=1)) - elif representation == 'angle': - xsdata._absorption[i] = \ - np.subtract(xsdata._total[i], np.sum(np.sum( - xsdata._scatter_matrix[i][:, :, :, :, :], - axis=4), axis=3)) + if representation == "isotropic": + xsdata._absorption[i] = np.subtract( + xsdata._total[i], + np.sum( + np.sum( + xsdata._scatter_matrix[i][:, :, :], axis=2 + ), + axis=1, + ), + ) + elif representation == "angle": + xsdata._absorption[i] = np.subtract( + xsdata._total[i], + np.sum( + np.sum( + xsdata._scatter_matrix[i][:, :, :, :, :], + axis=4, + ), + axis=3, + ), + ) # if only scatter matrices have been tallied, multiplicity cannot # be accounted for else: - msg = 'Scatter multiplicity (such as (n,xn) reactions) '\ - 'are ignored since multiplicity or nu-scatter matrices '\ - 'were not tallied for ' + xsdata_name + msg = ( + "Scatter multiplicity (such as (n,xn) reactions) " + "are ignored since multiplicity or nu-scatter matrices " + "were not tallied for " + xsdata_name + ) warn(msg, RuntimeWarning) - xsdata.set_scatter_matrix_mgxs(scatt_mgxs, xs_type=xs_type, - nuclide=[nuclide], - subdomain=subdomain) + xsdata.set_scatter_matrix_mgxs( + scatt_mgxs, xs_type=xs_type, nuclide=[nuclide], subdomain=subdomain + ) return xsdata - def create_mg_library(self, xs_type='macro', xsdata_names=None, - apply_domain_chi=False): + def create_mg_library( + self, xs_type="macro", xsdata_names=None, apply_domain_chi=False + ): """Creates an openmc.MGXSLibrary object to contain the MGXS data for the Multi-Group mode of OpenMC. @@ -1301,35 +1408,36 @@ def create_mg_library(self, xs_type='macro', xsdata_names=None, # multi-group cross section types self.check_library_for_openmc_mgxs() - cv.check_value('xs_type', xs_type, ['macro', 'micro']) + cv.check_value("xs_type", xs_type, ["macro", "micro"]) if xsdata_names is not None: - cv.check_iterable_type('xsdata_names', xsdata_names, str) + cv.check_iterable_type("xsdata_names", xsdata_names, str) # If gathering material-specific data, set the xs_type to macro if not self.by_nuclide: - xs_type = 'macro' + xs_type = "macro" # Initialize file mgxs_file = openmc.MGXSLibrary( - self.energy_groups, num_delayed_groups=self.num_delayed_groups) + self.energy_groups, num_delayed_groups=self.num_delayed_groups + ) - if self.domain_type == 'mesh': + if self.domain_type == "mesh": # Create the xsdata objects and add to the mgxs_file i = 0 for domain in self.domains: if self.by_nuclide: - raise NotImplementedError("Mesh domains do not currently " - "support nuclidic tallies") + raise NotImplementedError( + "Mesh domains do not currently " "support nuclidic tallies" + ) for subdomain in domain.indices: # Build & add metadata to XSdata object if xsdata_names is None: - xsdata_name = 'set' + str(i + 1) + 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, - subdomain=subdomain) + xsdata = self.get_xsdata(domain, xsdata_name, subdomain=subdomain) mgxs_file.add_xsdata(xsdata) i += 1 @@ -1339,24 +1447,29 @@ def create_mg_library(self, xs_type='macro', xsdata_names=None, if self.by_nuclide: nuclides = domain.get_nuclides() else: - nuclides = ['total'] + nuclides = ["total"] for nuclide in nuclides: # Build & add metadata to XSdata object if xsdata_names is None: - xsdata_name = 'set' + str(i + 1) + xsdata_name = "set" + str(i + 1) else: xsdata_name = xsdata_names[i] - xsdata = self.get_xsdata(domain, xsdata_name, - nuclide=nuclide, xs_type=xs_type, - apply_domain_chi=apply_domain_chi) + xsdata = self.get_xsdata( + domain, + xsdata_name, + nuclide=nuclide, + xs_type=xs_type, + apply_domain_chi=apply_domain_chi, + ) mgxs_file.add_xsdata(xsdata) return mgxs_file - def create_mg_mode(self, xsdata_names=None, bc=['reflective'] * 6, - apply_domain_chi=False): + def create_mg_mode( + self, xsdata_names=None, bc=["reflective"] * 6, apply_domain_chi=False + ): """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. @@ -1425,20 +1538,20 @@ def create_mg_mode(self, xsdata_names=None, bc=['reflective'] * 6, # the multiple meshes could be overlapping or in disparate regions # of the continuous energy model. The next step makes sure there is # only one before continuing. - if self.domain_type == 'mesh': + if self.domain_type == "mesh": cv.check_length("domains", self.domains, 1, 1) # Get the MGXS File Data - mgxs_file = self.create_mg_library('macro', xsdata_names, - apply_domain_chi=apply_domain_chi) + mgxs_file = self.create_mg_library( + "macro", xsdata_names, apply_domain_chi=apply_domain_chi + ) # Now move on the creating the geometry and assigning materials - if self.domain_type == 'mesh': - root = openmc.Universe(name='root', universe_id=0) + if self.domain_type == "mesh": + root = openmc.Universe(name="root", universe_id=0) # Add cells representative of the mesh with reflective BC - root_cell, cells = \ - self.domains[0].build_cells(bc) + root_cell, cells = self.domains[0].build_cells(bc) root.add_cell(root_cell) geometry = openmc.Geometry() @@ -1480,18 +1593,23 @@ def create_mg_mode(self, xsdata_names=None, bc=['reflective'] * 6, materials.append(material) # Differentiate Geometry with new Material - if self.domain_type == 'material': + if self.domain_type == "material": # Fill all appropriate Cells with new Material for cell in all_cells: - if isinstance(cell.fill, openmc.Material) and cell.fill.id == domain.id: + if ( + isinstance(cell.fill, openmc.Material) + and cell.fill.id == domain.id + ): cell.fill = material - elif self.domain_type == 'cell': + elif self.domain_type == "cell": for cell in all_cells: if not isinstance(cell.fill, openmc.Material): - warn('If the library domain includes a lattice or universe cell ' - 'in conjunction with a consituent cell of that lattice/universe, ' - 'the multi-group simulation will fail') + warn( + "If the library domain includes a lattice or universe cell " + "in conjunction with a consituent cell of that lattice/universe, " + "the multi-group simulation will fail" + ) if cell.id == domain.id: cell.fill = material @@ -1529,57 +1647,83 @@ def check_library_for_openmc_mgxs(self): # if correction is 'P0', then transport must be provided # otherwise total must be provided - if self.correction == 'P0': - if ('transport' not in self.mgxs_types and - 'nu-transport' not in self.mgxs_types): + if self.correction == "P0": + if ( + "transport" not in self.mgxs_types + and "nu-transport" not in self.mgxs_types + ): error_flag = True - warn('If the "correction" parameter is "P0", then a ' - '"transport" or "nu-transport" MGXS type is required.') + warn( + 'If the "correction" parameter is "P0", then a ' + '"transport" or "nu-transport" MGXS type is required.' + ) else: - if 'total' not in self.mgxs_types: + if "total" not in self.mgxs_types: error_flag = True - warn('If the "correction" parameter is None, then a ' - '"total" MGXS type is required.') + warn( + 'If the "correction" parameter is None, then a ' + '"total" MGXS type is required.' + ) # Check consistency of "nu-transport" and "nu-scatter" - if 'nu-transport' in self.mgxs_types: - if not ('nu-scatter matrix' in self.mgxs_types or - 'consistent nu-scatter matrix' in self.mgxs_types): + if "nu-transport" in self.mgxs_types: + if not ( + "nu-scatter matrix" in self.mgxs_types + or "consistent nu-scatter matrix" in self.mgxs_types + ): error_flag = True - warn('If a "nu-transport" MGXS type is used then a ' - '"nu-scatter matrix" or "consistent nu-scatter matrix" ' - 'must also be used.') - elif 'transport' in self.mgxs_types: - if not ('scatter matrix' in self.mgxs_types or - 'consistent scatter matrix' in self.mgxs_types): + warn( + 'If a "nu-transport" MGXS type is used then a ' + '"nu-scatter matrix" or "consistent nu-scatter matrix" ' + "must also be used." + ) + elif "transport" in self.mgxs_types: + if not ( + "scatter matrix" in self.mgxs_types + or "consistent scatter matrix" in self.mgxs_types + ): error_flag = True - warn('If a "transport" MGXS type is used then a ' - '"scatter matrix" or "consistent scatter matrix" ' - 'must also be used.') + warn( + 'If a "transport" MGXS type is used then a ' + '"scatter matrix" or "consistent scatter matrix" ' + "must also be used." + ) # Make sure there is some kind of a scattering matrix data - if 'nu-scatter matrix' not in self.mgxs_types and \ - 'consistent nu-scatter matrix' not in self.mgxs_types and \ - 'scatter matrix' not in self.mgxs_types and \ - 'consistent scatter matrix' not in self.mgxs_types: + if ( + "nu-scatter matrix" not in self.mgxs_types + and "consistent nu-scatter matrix" not in self.mgxs_types + and "scatter matrix" not in self.mgxs_types + and "consistent scatter matrix" not in self.mgxs_types + ): error_flag = True - warn('A "nu-scatter matrix", "consistent nu-scatter matrix", ' - '"scatter matrix", or "consistent scatter matrix" MGXS ' - 'type is required.') + warn( + 'A "nu-scatter matrix", "consistent nu-scatter matrix", ' + '"scatter matrix", or "consistent scatter matrix" MGXS ' + "type is required." + ) # Make sure there is some kind of a scattering multiplicity matrix data - if 'multiplicity matrix' not in self.mgxs_types and \ - ('scatter matrix' not in self.mgxs_types or - 'nu-scatter matrix' not in self.mgxs_types) and\ - ('consistent scatter matrix' not in self.mgxs_types or - 'consistent nu-scatter matrix' not in self.mgxs_types): - warn('A "multiplicity matrix" or both a "scatter" and "nu-scatter" ' - 'matrix MGXS type(s) should be provided.') + if ( + "multiplicity matrix" not in self.mgxs_types + and ( + "scatter matrix" not in self.mgxs_types + or "nu-scatter matrix" not in self.mgxs_types + ) + and ( + "consistent scatter matrix" not in self.mgxs_types + or "consistent nu-scatter matrix" not in self.mgxs_types + ) + ): + warn( + 'A "multiplicity matrix" or both a "scatter" and "nu-scatter" ' + "matrix MGXS type(s) should be provided." + ) # Ensure absorption is present - if 'absorption' not in self.mgxs_types: + if "absorption" not in self.mgxs_types: error_flag = True warn('An "absorption" MGXS type is required but not provided.') if error_flag: - raise ValueError('Invalid MGXS configuration encountered.') + raise ValueError("Invalid MGXS configuration encountered.") diff --git a/openmc/mgxs/mdgxs.py b/openmc/mgxs/mdgxs.py index b95a4fbc0eb..079c64529d2 100644 --- a/openmc/mgxs/mdgxs.py +++ b/openmc/mgxs/mdgxs.py @@ -13,11 +13,11 @@ # Supported cross section types MDGXS_TYPES = ( - 'delayed-nu-fission', - 'chi-delayed', - 'beta', - 'decay-rate', - 'delayed-nu-fission matrix' + "delayed-nu-fission", + "chi-delayed", + "beta", + "decay-rate", + "delayed-nu-fission matrix", ) # Maximum number of delayed groups, from include/openmc/constants.h @@ -127,11 +127,26 @@ class MDGXS(MGXS): """ - def __init__(self, domain=None, domain_type=None, energy_groups=None, - delayed_groups=None, by_nuclide=False, name='', - num_polar=1, num_azimuthal=1): - super().__init__(domain, domain_type, energy_groups, by_nuclide, name, - num_polar, num_azimuthal) + def __init__( + self, + domain=None, + domain_type=None, + energy_groups=None, + delayed_groups=None, + by_nuclide=False, + name="", + num_polar=1, + num_azimuthal=1, + ): + super().__init__( + domain, + domain_type, + energy_groups, + by_nuclide, + name, + num_polar, + num_azimuthal, + ) self._delayed_groups = None @@ -191,14 +206,15 @@ def delayed_groups(self, delayed_groups): if delayed_groups is not None: - cv.check_type('delayed groups', delayed_groups, list, int) - cv.check_greater_than('num delayed groups', len(delayed_groups), 0) + cv.check_type("delayed groups", delayed_groups, list, int) + cv.check_greater_than("num delayed groups", len(delayed_groups), 0) # Check that the groups are within [1, MAX_DELAYED_GROUPS] for group in delayed_groups: - cv.check_greater_than('delayed group', group, 0) - cv.check_less_than('delayed group', group, MAX_DELAYED_GROUPS, - equality=True) + cv.check_greater_than("delayed group", group, 0) + cv.check_less_than( + "delayed group", group, MAX_DELAYED_GROUPS, equality=True + ) self._delayed_groups = delayed_groups @@ -224,9 +240,17 @@ def filters(self): return self._add_angle_filters(filters) @staticmethod - def get_mgxs(mdgxs_type, domain=None, domain_type=None, energy_groups=None, - delayed_groups=None, by_nuclide=False, name='', - num_polar=1, num_azimuthal=1): + def get_mgxs( + mdgxs_type, + domain=None, + domain_type=None, + energy_groups=None, + delayed_groups=None, + by_nuclide=False, + name="", + num_polar=1, + num_azimuthal=1, + ): """Return a MDGXS subclass object for some energy group structure within some spatial domain for some reaction type. @@ -266,21 +290,22 @@ def get_mgxs(mdgxs_type, domain=None, domain_type=None, energy_groups=None, """ - cv.check_value('mdgxs_type', mdgxs_type, MDGXS_TYPES) + cv.check_value("mdgxs_type", mdgxs_type, MDGXS_TYPES) - if mdgxs_type == 'delayed-nu-fission': - mdgxs = DelayedNuFissionXS(domain, domain_type, energy_groups, - delayed_groups) - elif mdgxs_type == 'chi-delayed': - mdgxs = ChiDelayed(domain, domain_type, energy_groups, - delayed_groups) - elif mdgxs_type == 'beta': + if mdgxs_type == "delayed-nu-fission": + mdgxs = DelayedNuFissionXS( + domain, domain_type, energy_groups, delayed_groups + ) + elif mdgxs_type == "chi-delayed": + mdgxs = ChiDelayed(domain, domain_type, energy_groups, delayed_groups) + elif mdgxs_type == "beta": mdgxs = Beta(domain, domain_type, energy_groups, delayed_groups) - elif mdgxs_type == 'decay-rate': + elif mdgxs_type == "decay-rate": mdgxs = DecayRate(domain, domain_type, energy_groups, delayed_groups) - elif mdgxs_type == 'delayed-nu-fission matrix': - mdgxs = DelayedNuFissionMatrixXS(domain, domain_type, energy_groups, - delayed_groups) + elif mdgxs_type == "delayed-nu-fission matrix": + mdgxs = DelayedNuFissionMatrixXS( + domain, domain_type, energy_groups, delayed_groups + ) mdgxs.by_nuclide = by_nuclide mdgxs.name = name @@ -288,9 +313,18 @@ def get_mgxs(mdgxs_type, domain=None, domain_type=None, energy_groups=None, mdgxs.num_azimuthal = num_azimuthal return mdgxs - def get_xs(self, groups='all', subdomains='all', nuclides='all', - xs_type='macro', order_groups='increasing', - value='mean', delayed_groups='all', squeeze=True, **kwargs): + def get_xs( + self, + groups="all", + subdomains="all", + nuclides="all", + xs_type="macro", + order_groups="increasing", + value="mean", + delayed_groups="all", + squeeze=True, + **kwargs, + ): """Returns an array of multi-delayed-group cross sections. This method constructs a 4D NumPy array for the requested @@ -338,14 +372,16 @@ def get_xs(self, groups='all', subdomains='all', nuclides='all', """ - cv.check_value('value', value, ['mean', 'std_dev', 'rel_err']) - cv.check_value('xs_type', xs_type, ['macro', 'micro']) + cv.check_value("value", value, ["mean", "std_dev", "rel_err"]) + cv.check_value("xs_type", xs_type, ["macro", "micro"]) # FIXME: Unable to get microscopic xs for mesh domain because the mesh # cells do not know the nuclide densities in each mesh cell. - if self.domain_type == 'mesh' and xs_type == 'micro': - msg = 'Unable to get micro xs for mesh domain since the mesh ' \ - 'cells do not know the nuclide densities in each mesh cell.' + if self.domain_type == "mesh" and xs_type == "micro": + msg = ( + "Unable to get micro xs for mesh domain since the mesh " + "cells do not know the nuclide densities in each mesh cell." + ) raise ValueError(msg) filters = [] @@ -353,65 +389,67 @@ def get_xs(self, groups='all', subdomains='all', nuclides='all', # Construct a collection of the domain filter bins if not isinstance(subdomains, str): - cv.check_iterable_type('subdomains', subdomains, Integral, - max_depth=3) + cv.check_iterable_type("subdomains", subdomains, Integral, max_depth=3) for subdomain in subdomains: filters.append(_DOMAIN_TO_FILTER[self.domain_type]) filter_bins.append((subdomain,)) # Construct list of energy group bounds tuples for all requested groups if not isinstance(groups, str): - cv.check_iterable_type('groups', groups, Integral) + cv.check_iterable_type("groups", groups, Integral) for group in groups: filters.append(openmc.EnergyFilter) - filter_bins.append( - (self.energy_groups.get_group_bounds(group),)) + filter_bins.append((self.energy_groups.get_group_bounds(group),)) # Construct list of delayed group tuples for all requested groups if not isinstance(delayed_groups, str): - cv.check_type('delayed groups', delayed_groups, list, int) + cv.check_type("delayed groups", delayed_groups, list, int) for delayed_group in delayed_groups: filters.append(openmc.DelayedGroupFilter) filter_bins.append((delayed_group,)) # Construct a collection of the nuclides to retrieve from the xs tally if self.by_nuclide: - if nuclides == 'all' or nuclides == 'sum' or nuclides == ['sum']: + if nuclides == "all" or nuclides == "sum" or nuclides == ["sum"]: query_nuclides = self.get_nuclides() else: query_nuclides = nuclides else: - query_nuclides = ['total'] + query_nuclides = ["total"] # If user requested the sum for all nuclides, use tally summation - if nuclides == 'sum' or nuclides == ['sum']: + if nuclides == "sum" or nuclides == ["sum"]: xs_tally = self.xs_tally.summation(nuclides=query_nuclides) - xs = xs_tally.get_values(filters=filters, - filter_bins=filter_bins, value=value) + xs = xs_tally.get_values( + filters=filters, filter_bins=filter_bins, value=value + ) else: - xs = self.xs_tally.get_values(filters=filters, - filter_bins=filter_bins, - nuclides=query_nuclides, value=value) + xs = self.xs_tally.get_values( + filters=filters, + filter_bins=filter_bins, + nuclides=query_nuclides, + value=value, + ) # Divide by atom number densities for microscopic cross sections - if xs_type == 'micro' and self._divide_by_density: + if xs_type == "micro" and self._divide_by_density: if self.by_nuclide: densities = self.get_nuclide_densities(nuclides) else: - densities = self.get_nuclide_densities('sum') - if value == 'mean' or value == 'std_dev': + densities = self.get_nuclide_densities("sum") + if value == "mean" or value == "std_dev": xs /= densities[np.newaxis, :, np.newaxis] # Eliminate the trivial score dimension xs = np.squeeze(xs, axis=len(xs.shape) - 1) xs = np.nan_to_num(xs) - if groups == 'all': + if groups == "all": num_groups = self.num_groups else: num_groups = len(groups) - if delayed_groups == 'all': + if delayed_groups == "all": num_delayed_groups = self.num_delayed_groups else: num_delayed_groups = len(delayed_groups) @@ -419,12 +457,18 @@ def get_xs(self, groups='all', subdomains='all', nuclides='all', # Reshape tally data array with separate axes for domain, # energy groups, delayed groups, and nuclides # Accommodate the polar and azimuthal bins if needed - num_subdomains = \ - int(xs.shape[0] / (num_groups * num_delayed_groups * - self.num_polar * self.num_azimuthal)) + num_subdomains = int( + xs.shape[0] + / (num_groups * num_delayed_groups * self.num_polar * self.num_azimuthal) + ) if self.num_polar > 1 or self.num_azimuthal > 1: - new_shape = (self.num_polar, self.num_azimuthal, num_subdomains, - num_delayed_groups, num_groups) + new_shape = ( + self.num_polar, + self.num_azimuthal, + num_subdomains, + num_delayed_groups, + num_groups, + ) else: new_shape = (num_subdomains, num_delayed_groups, num_groups) new_shape += xs.shape[1:] @@ -432,7 +476,7 @@ def get_xs(self, groups='all', subdomains='all', nuclides='all', # Reverse data if user requested increasing energy groups since # tally data is stored in order of increasing energies - if order_groups == 'increasing': + if order_groups == "increasing": xs = xs[..., ::-1, :] if squeeze: @@ -472,9 +516,9 @@ def get_slice(self, nuclides=[], groups=[], delayed_groups=[]): """ - cv.check_iterable_type('nuclides', nuclides, str) - cv.check_iterable_type('energy_groups', groups, Integral) - cv.check_type('delayed groups', delayed_groups, list, int) + cv.check_iterable_type("nuclides", nuclides, str) + cv.check_iterable_type("energy_groups", groups, Integral) + cv.check_type("delayed groups", delayed_groups, list, int) # Build lists of filters and filter bins to slice filters = [] @@ -501,9 +545,9 @@ def get_slice(self, nuclides=[], groups=[], delayed_groups=[]): for tally_type, tally in slice_xs.tallies.items(): slice_nuclides = [nuc for nuc in nuclides if nuc in tally.nuclides] if filters != []: - tally_slice = tally.get_slice(filters=filters, - filter_bins=filter_bins, - nuclides=slice_nuclides) + tally_slice = tally.get_slice( + filters=filters, filter_bins=filter_bins, nuclides=slice_nuclides + ) else: tally_slice = tally.get_slice(nuclides=slice_nuclides) slice_xs.tallies[tally_type] = tally_slice @@ -552,12 +596,13 @@ def merge(self, other): # Merge delayed groups if self.delayed_groups != other.delayed_groups: - merged_mdgxs.delayed_groups = list(set(self.delayed_groups + - other.delayed_groups)) + merged_mdgxs.delayed_groups = list( + set(self.delayed_groups + other.delayed_groups) + ) return merged_mdgxs - def print_xs(self, subdomains='all', nuclides='all', xs_type='macro'): + def print_xs(self, subdomains="all", nuclides="all", xs_type="macro"): """Print a string representation for the multi-group cross section. Parameters @@ -583,10 +628,10 @@ def print_xs(self, subdomains='all', nuclides='all', xs_type='macro'): # Construct a collection of the subdomains to report if not isinstance(subdomains, str): - cv.check_iterable_type('subdomains', subdomains, Integral) - elif self.domain_type == 'distribcell': + cv.check_iterable_type("subdomains", subdomains, Integral) + elif self.domain_type == "distribcell": subdomains = np.arange(self.num_subdomains, dtype=int) - elif self.domain_type == 'mesh': + elif self.domain_type == "mesh": xyz = [range(1, x + 1) for x in self.domain.dimension] subdomains = list(itertools.product(*xyz)) else: @@ -594,25 +639,25 @@ def print_xs(self, subdomains='all', nuclides='all', xs_type='macro'): # Construct a collection of the nuclides to report if self.by_nuclide: - if nuclides == 'all': + if nuclides == "all": nuclides = self.get_nuclides() - elif nuclides == 'sum': - nuclides = ['sum'] + elif nuclides == "sum": + nuclides = ["sum"] else: - cv.check_iterable_type('nuclides', nuclides, str) + cv.check_iterable_type("nuclides", nuclides, str) else: - nuclides = ['sum'] + nuclides = ["sum"] - cv.check_value('xs_type', xs_type, ['macro', 'micro']) + cv.check_value("xs_type", xs_type, ["macro", "micro"]) # Build header for string with type and domain info - string = 'Multi-Delayed-Group XS\n' - string += '{0: <16}=\t{1}\n'.format('\tReaction Type', self.mgxs_type) - string += '{0: <16}=\t{1}\n'.format('\tDomain Type', self.domain_type) - string += '{0: <16}=\t{1}\n'.format('\tDomain ID', self.domain.id) + string = "Multi-Delayed-Group XS\n" + string += "{0: <16}=\t{1}\n".format("\tReaction Type", self.mgxs_type) + string += "{0: <16}=\t{1}\n".format("\tDomain Type", self.domain_type) + string += "{0: <16}=\t{1}\n".format("\tDomain ID", self.domain.id) # Generate the header for an individual XS - xs_header = f'\tCross Sections [{self.get_units(xs_type)}]:' + xs_header = f"\tCross Sections [{self.get_units(xs_type)}]:" # If cross section data has not been computed, only print string header if self.tallies is None: @@ -621,83 +666,100 @@ def print_xs(self, subdomains='all', nuclides='all', xs_type='macro'): # Set polar/azimuthal bins if self.num_polar > 1 or self.num_azimuthal > 1: - polar_bins = np.linspace(0., np.pi, num=self.num_polar + 1, - endpoint=True) - azimuthal_bins = np.linspace(-np.pi, np.pi, - num=self.num_azimuthal + 1, - endpoint=True) + polar_bins = np.linspace(0.0, np.pi, num=self.num_polar + 1, endpoint=True) + azimuthal_bins = np.linspace( + -np.pi, np.pi, num=self.num_azimuthal + 1, endpoint=True + ) # Loop over all subdomains for subdomain in subdomains: - if self.domain_type == 'distribcell' or self.domain_type == 'mesh': - string += '{0: <16}=\t{1}\n'.format('\tSubdomain', subdomain) + if self.domain_type == "distribcell" or self.domain_type == "mesh": + string += "{0: <16}=\t{1}\n".format("\tSubdomain", subdomain) # Loop over all Nuclides for nuclide in nuclides: # Build header for nuclide type - if nuclide != 'sum': - string += '{0: <16}=\t{1}\n'.format('\tNuclide', nuclide) + if nuclide != "sum": + string += "{0: <16}=\t{1}\n".format("\tNuclide", nuclide) # Add the cross section header - string += f'{xs_header: <16}\n' + string += f"{xs_header: <16}\n" for delayed_group in self.delayed_groups: - template = '{0: <12}Delayed Group {1}:\t' - string += template.format('', delayed_group) - string += '\n' - - template = '{0: <12}Group {1} [{2: <10} - {3: <10}eV]:\t' - - average_xs = self.get_xs(nuclides=[nuclide], - subdomains=[subdomain], - xs_type=xs_type, value='mean', - delayed_groups=[delayed_group]) - rel_err_xs = self.get_xs(nuclides=[nuclide], - subdomains=[subdomain], - xs_type=xs_type, value='rel_err', - delayed_groups=[delayed_group]) - rel_err_xs = rel_err_xs * 100. + template = "{0: <12}Delayed Group {1}:\t" + string += template.format("", delayed_group) + string += "\n" + + template = "{0: <12}Group {1} [{2: <10} - {3: <10}eV]:\t" + + average_xs = self.get_xs( + nuclides=[nuclide], + subdomains=[subdomain], + xs_type=xs_type, + value="mean", + delayed_groups=[delayed_group], + ) + rel_err_xs = self.get_xs( + nuclides=[nuclide], + subdomains=[subdomain], + xs_type=xs_type, + value="rel_err", + delayed_groups=[delayed_group], + ) + rel_err_xs = rel_err_xs * 100.0 if self.num_polar > 1 or self.num_azimuthal > 1: # Loop over polar, azimuthal, and energy group ranges for pol in range(len(polar_bins) - 1): - pol_low, pol_high = polar_bins[pol: pol + 2] + pol_low, pol_high = polar_bins[pol : pol + 2] for azi in range(len(azimuthal_bins) - 1): - azi_low, azi_high = azimuthal_bins[azi: azi + 2] - string += '\t\tPolar Angle: [{0:5f} - {1:5f}]'.format( - pol_low, pol_high) + \ - '\tAzimuthal Angle: [{0:5f} - {1:5f}]'.format( - azi_low, azi_high) + '\n' + azi_low, azi_high = azimuthal_bins[azi : azi + 2] + string += ( + "\t\tPolar Angle: [{0:5f} - {1:5f}]".format( + pol_low, pol_high + ) + + "\tAzimuthal Angle: [{0:5f} - {1:5f}]".format( + azi_low, azi_high + ) + + "\n" + ) for group in range(1, self.num_groups + 1): - bounds = \ - self.energy_groups.get_group_bounds(group) - string += '\t' + template.format('', group, - bounds[0], - bounds[1]) - string += '{0:.2e} +/- {1:.2e}%'.format( + bounds = self.energy_groups.get_group_bounds(group) + string += "\t" + template.format( + "", group, bounds[0], bounds[1] + ) + string += "{0:.2e} +/- {1:.2e}%".format( average_xs[pol, azi, group - 1], - rel_err_xs[pol, azi, group - 1]) - string += '\n' - string += '\n' + rel_err_xs[pol, azi, group - 1], + ) + string += "\n" + string += "\n" else: # Loop over energy groups ranges - for group in range(1, self.num_groups+1): + for group in range(1, self.num_groups + 1): bounds = self.energy_groups.get_group_bounds(group) - string += template.format('', group, bounds[0], bounds[1]) - string += '{0:.2e} +/- {1:.2e}%'.format( - average_xs[group - 1], rel_err_xs[group - 1]) - string += '\n' - string += '\n' - string += '\n' + string += template.format("", group, bounds[0], bounds[1]) + string += "{0:.2e} +/- {1:.2e}%".format( + average_xs[group - 1], rel_err_xs[group - 1] + ) + string += "\n" + string += "\n" + string += "\n" print(string) - def export_xs_data(self, filename='mgxs', directory='mgxs', - format='csv', groups='all', xs_type='macro', - delayed_groups='all'): + def export_xs_data( + self, + filename="mgxs", + directory="mgxs", + format="csv", + groups="all", + xs_type="macro", + delayed_groups="all", + ): """Export the multi-delayed-group cross section data to a file. This method leverages the functionality in the Pandas library to export @@ -722,57 +784,65 @@ def export_xs_data(self, filename='mgxs', directory='mgxs', """ - cv.check_type('filename', filename, str) - cv.check_type('directory', directory, str) - cv.check_value('format', format, ['csv', 'excel', 'pickle', 'latex']) - cv.check_value('xs_type', xs_type, ['macro', 'micro']) + cv.check_type("filename", filename, str) + cv.check_type("directory", directory, str) + cv.check_value("format", format, ["csv", "excel", "pickle", "latex"]) + cv.check_value("xs_type", xs_type, ["macro", "micro"]) # Make directory if it does not exist if not os.path.exists(directory): os.makedirs(directory) filename = os.path.join(directory, filename) - filename = filename.replace(' ', '-') + filename = filename.replace(" ", "-") # Get a Pandas DataFrame for the data - df = self.get_pandas_dataframe(groups=groups, xs_type=xs_type, - delayed_groups=delayed_groups) + df = self.get_pandas_dataframe( + groups=groups, xs_type=xs_type, delayed_groups=delayed_groups + ) # Export the data using Pandas IO API - if format == 'csv': - df.to_csv(filename + '.csv', index=False) - elif format == 'excel': - if self.domain_type == 'mesh': - df.to_excel(filename + '.xlsx') + if format == "csv": + df.to_csv(filename + ".csv", index=False) + elif format == "excel": + if self.domain_type == "mesh": + df.to_excel(filename + ".xlsx") else: - df.to_excel(filename + '.xlsx', index=False) - elif format == 'pickle': - df.to_pickle(filename + '.pkl') - elif format == 'latex': - if self.domain_type == 'distribcell': - msg = 'Unable to export distribcell multi-group cross section' \ - 'data to a LaTeX table' + df.to_excel(filename + ".xlsx", index=False) + elif format == "pickle": + df.to_pickle(filename + ".pkl") + elif format == "latex": + if self.domain_type == "distribcell": + msg = ( + "Unable to export distribcell multi-group cross section" + "data to a LaTeX table" + ) raise NotImplementedError(msg) - df.to_latex(filename + '.tex', bold_rows=True, - longtable=True, index=False) + df.to_latex(filename + ".tex", bold_rows=True, longtable=True, index=False) # Surround LaTeX table with code needed to run pdflatex - with open(filename + '.tex','r') as original: + with open(filename + ".tex", "r") as original: data = original.read() - with open(filename + '.tex','w') as modified: + with open(filename + ".tex", "w") as modified: modified.write( - '\\documentclass[preview, 12pt, border=1mm]{standalone}\n') - modified.write('\\usepackage{caption}\n') - modified.write('\\usepackage{longtable}\n') - modified.write('\\usepackage{booktabs}\n') - modified.write('\\begin{document}\n\n') + "\\documentclass[preview, 12pt, border=1mm]{standalone}\n" + ) + modified.write("\\usepackage{caption}\n") + modified.write("\\usepackage{longtable}\n") + modified.write("\\usepackage{booktabs}\n") + modified.write("\\begin{document}\n\n") modified.write(data) - modified.write('\n\\end{document}') - - def get_pandas_dataframe(self, groups='all', nuclides='all', - xs_type='macro', paths=True, - delayed_groups='all'): + modified.write("\n\\end{document}") + + def get_pandas_dataframe( + self, + groups="all", + nuclides="all", + xs_type="macro", + paths=True, + delayed_groups="all", + ): """Build a Pandas DataFrame for the MDGXS data. This method leverages :meth:`openmc.Tally.get_pandas_dataframe`, but @@ -814,29 +884,29 @@ def get_pandas_dataframe(self, groups='all', nuclides='all', """ if not isinstance(groups, str): - cv.check_iterable_type('groups', groups, Integral) - if nuclides != 'all' and nuclides != 'sum': - cv.check_iterable_type('nuclides', nuclides, str) + cv.check_iterable_type("groups", groups, Integral) + if nuclides != "all" and nuclides != "sum": + cv.check_iterable_type("nuclides", nuclides, str) if not isinstance(delayed_groups, str): - cv.check_type('delayed groups', delayed_groups, list, int) + cv.check_type("delayed groups", delayed_groups, list, int) - cv.check_value('xs_type', xs_type, ['macro', 'micro']) + cv.check_value("xs_type", xs_type, ["macro", "micro"]) # Get a Pandas DataFrame from the derived xs tally - if self.by_nuclide and nuclides == 'sum': + if self.by_nuclide and nuclides == "sum": # Use tally summation to sum across all nuclides xs_tally = self.xs_tally.summation(nuclides=self.get_nuclides()) df = xs_tally.get_pandas_dataframe(paths=paths) # Remove nuclide column since it is homogeneous and redundant - if self.domain_type == 'mesh': - df.drop('sum(nuclide)', axis=1, level=0, inplace=True) + if self.domain_type == "mesh": + df.drop("sum(nuclide)", axis=1, level=0, inplace=True) else: - df.drop('sum(nuclide)', axis=1, inplace=True) + df.drop("sum(nuclide)", axis=1, inplace=True) # If the user requested a specific set of nuclides - elif self.by_nuclide and nuclides != 'all': + elif self.by_nuclide and nuclides != "all": xs_tally = self.xs_tally.get_slice(nuclides=nuclides) df = xs_tally.get_pandas_dataframe(paths=paths) @@ -845,10 +915,10 @@ def get_pandas_dataframe(self, groups='all', nuclides='all', df = self.xs_tally.get_pandas_dataframe(paths=paths) # Remove the score column since it is homogeneous and redundant - if self.domain_type == 'mesh': - df = df.drop('score', axis=1, level=0) + if self.domain_type == "mesh": + df = df.drop("score", axis=1, level=0) else: - df = df.drop('score', axis=1) + df = df.drop("score", axis=1) # Convert azimuthal, polar, energy in and energy out bin values in to # bin indices @@ -856,28 +926,30 @@ def get_pandas_dataframe(self, groups='all', nuclides='all', # Select out those groups the user requested if not isinstance(groups, str): - if 'group in' in df: - df = df[df['group in'].isin(groups)] - if 'group out' in df: - df = df[df['group out'].isin(groups)] + if "group in" in df: + df = df[df["group in"].isin(groups)] + if "group out" in df: + df = df[df["group out"].isin(groups)] # If user requested micro cross sections, divide out the atom densities - if xs_type == 'micro' and self._divide_by_density: + if xs_type == "micro" and self._divide_by_density: if self.by_nuclide: densities = self.get_nuclide_densities(nuclides) else: - densities = self.get_nuclide_densities('sum') + densities = self.get_nuclide_densities("sum") densities = np.repeat(densities, len(self.rxn_rate_tally.scores)) tile_factor = int(df.shape[0] / len(densities)) - df['mean'] /= np.tile(densities, tile_factor) - df['std. dev.'] /= np.tile(densities, tile_factor) + df["mean"] /= np.tile(densities, tile_factor) + df["std. dev."] /= np.tile(densities, tile_factor) # Sort the dataframe by domain type id (e.g., distribcell id) and # energy groups such that data is from fast to thermal - if self.domain_type == 'mesh': - mesh_str = f'mesh {self.domain.id}' - df.sort_values(by=[(mesh_str, 'x'), (mesh_str, 'y'), - (mesh_str, 'z')] + columns, inplace=True) + if self.domain_type == "mesh": + mesh_str = f"mesh {self.domain.id}" + df.sort_values( + by=[(mesh_str, "x"), (mesh_str, "y"), (mesh_str, "z")] + columns, + inplace=True, + ) else: df.sort_values(by=[self.domain_type] + columns, inplace=True) @@ -1015,17 +1087,33 @@ class ChiDelayed(MDGXS): # data should not be divided by the number density _divide_by_density = False - def __init__(self, domain=None, domain_type=None, energy_groups=None, - delayed_groups=None, by_nuclide=False, name='', - num_polar=1, num_azimuthal=1): - super().__init__(domain, domain_type, energy_groups, delayed_groups, - by_nuclide, name, num_polar, num_azimuthal) - self._rxn_type = 'chi-delayed' - self._estimator = 'analog' + def __init__( + self, + domain=None, + domain_type=None, + energy_groups=None, + delayed_groups=None, + by_nuclide=False, + name="", + num_polar=1, + num_azimuthal=1, + ): + super().__init__( + domain, + domain_type, + energy_groups, + delayed_groups, + by_nuclide, + name, + num_polar, + num_azimuthal, + ) + self._rxn_type = "chi-delayed" + self._estimator = "analog" @property def scores(self): - return ['delayed-nu-fission', 'delayed-nu-fission'] + return ["delayed-nu-fission", "delayed-nu-fission"] @property def filters(self): @@ -1043,12 +1131,12 @@ def filters(self): @property def tally_keys(self): - return ['delayed-nu-fission-in', 'delayed-nu-fission-out'] + return ["delayed-nu-fission-in", "delayed-nu-fission-out"] @property def rxn_rate_tally(self): if self._rxn_rate_tally is None: - self._rxn_rate_tally = self.tallies['delayed-nu-fission-out'] + self._rxn_rate_tally = self.tallies["delayed-nu-fission-out"] self._rxn_rate_tally.sparse = self.sparse return self._rxn_rate_tally @@ -1056,11 +1144,10 @@ def rxn_rate_tally(self): def xs_tally(self): if self._xs_tally is None: - delayed_nu_fission_in = self.tallies['delayed-nu-fission-in'] + delayed_nu_fission_in = self.tallies["delayed-nu-fission-in"] # Remove coarse energy filter to keep it out of tally arithmetic - energy_filter = delayed_nu_fission_in.find_filter( - openmc.EnergyFilter) + energy_filter = delayed_nu_fission_in.find_filter(openmc.EnergyFilter) delayed_nu_fission_in.remove_filter(energy_filter) # Compute chi @@ -1097,7 +1184,7 @@ def get_homogenized_mgxs(self, other_mgxs): """ - return self._get_homogenized_mgxs(other_mgxs, 'delayed-nu-fission-in') + return self._get_homogenized_mgxs(other_mgxs, "delayed-nu-fission-in") def get_slice(self, nuclides=[], groups=[], delayed_groups=[]): """Build a sliced ChiDelayed for the specified nuclides and energy @@ -1131,7 +1218,7 @@ def get_slice(self, nuclides=[], groups=[], delayed_groups=[]): # Temporarily remove energy filter from delayed-nu-fission-in since its # group structure will work in super MGXS.get_slice(...) method - delayed_nu_fission_in = self.tallies['delayed-nu-fission-in'] + delayed_nu_fission_in = self.tallies["delayed-nu-fission-in"] energy_filter = delayed_nu_fission_in.find_filter(openmc.EnergyFilter) delayed_nu_fission_in.remove_filter(energy_filter) @@ -1159,14 +1246,15 @@ def get_slice(self, nuclides=[], groups=[], delayed_groups=[]): if filters != []: # Slice nu-fission-out tally along energyout filter - delayed_nu_fission_out = slice_xs.tallies['delayed-nu-fission-out'] - tally_slice = delayed_nu_fission_out.get_slice \ - (filters=filters, filter_bins=filter_bins) - slice_xs._tallies['delayed-nu-fission-out'] = tally_slice + delayed_nu_fission_out = slice_xs.tallies["delayed-nu-fission-out"] + tally_slice = delayed_nu_fission_out.get_slice( + filters=filters, filter_bins=filter_bins + ) + slice_xs._tallies["delayed-nu-fission-out"] = tally_slice # Add energy filter back to nu-fission-in tallies - self.tallies['delayed-nu-fission-in'].add_filter(energy_filter) - slice_xs._tallies['delayed-nu-fission-in'].add_filter(energy_filter) + self.tallies["delayed-nu-fission-in"].add_filter(energy_filter) + slice_xs._tallies["delayed-nu-fission-in"].add_filter(energy_filter) slice_xs.sparse = self.sparse return slice_xs @@ -1189,7 +1277,7 @@ def merge(self, other): """ if not self.can_merge(other): - raise ValueError('Unable to merge ChiDelayed') + raise ValueError("Unable to merge ChiDelayed") # Create deep copy of tally to return as merged tally merged_mdgxs = copy.deepcopy(self) @@ -1204,8 +1292,9 @@ def merge(self, other): # Merge delayed groups if self.delayed_groups != other.delayed_groups: - merged_mdgxs.delayed_groups = list(set(self.delayed_groups + - other.delayed_groups)) + merged_mdgxs.delayed_groups = list( + set(self.delayed_groups + other.delayed_groups) + ) # Merge nuclides if self.nuclides != other.nuclides: @@ -1213,7 +1302,7 @@ def merge(self, other): # The nuclides must be mutually exclusive for nuclide in self.nuclides: if nuclide in other.nuclides: - msg = 'Unable to merge Chi Delayed with shared nuclides' + msg = "Unable to merge Chi Delayed with shared nuclides" raise ValueError(msg) # Concatenate lists of nuclides for the merged MGXS @@ -1221,15 +1310,23 @@ def merge(self, other): # Merge tallies for tally_key in self.tallies: - merged_tally = self.tallies[tally_key].merge\ - (other.tallies[tally_key]) + merged_tally = self.tallies[tally_key].merge(other.tallies[tally_key]) merged_mdgxs.tallies[tally_key] = merged_tally return merged_mdgxs - def get_xs(self, groups='all', subdomains='all', nuclides='all', - xs_type='macro', order_groups='increasing', - value='mean', delayed_groups='all', squeeze=True, **kwargs): + def get_xs( + self, + groups="all", + subdomains="all", + nuclides="all", + xs_type="macro", + order_groups="increasing", + value="mean", + delayed_groups="all", + squeeze=True, + **kwargs, + ): """Returns an array of the delayed fission spectrum. This method constructs a 4D NumPy array for the requested @@ -1278,14 +1375,16 @@ def get_xs(self, groups='all', subdomains='all', nuclides='all', """ - cv.check_value('value', value, ['mean', 'std_dev', 'rel_err']) - cv.check_value('xs_type', xs_type, ['macro', 'micro']) + cv.check_value("value", value, ["mean", "std_dev", "rel_err"]) + cv.check_value("xs_type", xs_type, ["macro", "micro"]) # FIXME: Unable to get microscopic xs for mesh domain because the mesh # cells do not know the nuclide densities in each mesh cell. - if self.domain_type == 'mesh' and xs_type == 'micro': - msg = 'Unable to get micro xs for mesh domain since the mesh ' \ - 'cells do not know the nuclide densities in each mesh cell.' + if self.domain_type == "mesh" and xs_type == "micro": + msg = ( + "Unable to get micro xs for mesh domain since the mesh " + "cells do not know the nuclide densities in each mesh cell." + ) raise ValueError(msg) filters = [] @@ -1293,23 +1392,21 @@ def get_xs(self, groups='all', subdomains='all', nuclides='all', # Construct a collection of the domain filter bins if not isinstance(subdomains, str): - cv.check_iterable_type('subdomains', subdomains, Integral, - max_depth=3) + cv.check_iterable_type("subdomains", subdomains, Integral, max_depth=3) for subdomain in subdomains: filters.append(_DOMAIN_TO_FILTER[self.domain_type]) filter_bins.append((subdomain,)) # Construct list of energy group bounds tuples for all requested groups if not isinstance(groups, str): - cv.check_iterable_type('groups', groups, Integral) + cv.check_iterable_type("groups", groups, Integral) for group in groups: filters.append(openmc.EnergyoutFilter) - filter_bins.append( - (self.energy_groups.get_group_bounds(group),)) + filter_bins.append((self.energy_groups.get_group_bounds(group),)) # Construct list of delayed group tuples for all requested groups if not isinstance(delayed_groups, str): - cv.check_type('delayed groups', delayed_groups, list, int) + cv.check_type("delayed groups", delayed_groups, list, int) for delayed_group in delayed_groups: filters.append(openmc.DelayedGroupFilter) filter_bins.append((delayed_group,)) @@ -1319,22 +1416,23 @@ def get_xs(self, groups='all', subdomains='all', nuclides='all', # Get the sum as the fission source weighted average chi for all # nuclides in the domain - if nuclides == 'sum' or nuclides == ['sum']: + if nuclides == "sum" or nuclides == ["sum"]: # Retrieve the fission production tallies - delayed_nu_fission_in = self.tallies['delayed-nu-fission-in'] - delayed_nu_fission_out = self.tallies['delayed-nu-fission-out'] + delayed_nu_fission_in = self.tallies["delayed-nu-fission-in"] + delayed_nu_fission_out = self.tallies["delayed-nu-fission-out"] # Sum out all nuclides nuclides = self.get_nuclides() delayed_nu_fission_in = delayed_nu_fission_in.summation( - nuclides=nuclides) + nuclides=nuclides + ) delayed_nu_fission_out = delayed_nu_fission_out.summation( - nuclides=nuclides) + nuclides=nuclides + ) # Remove coarse energy filter to keep it out of tally arithmetic - energy_filter = delayed_nu_fission_in.find_filter( - openmc.EnergyFilter) + energy_filter = delayed_nu_fission_in.find_filter(openmc.EnergyFilter) delayed_nu_fission_in.remove_filter(energy_filter) # Compute chi and store it as the xs_tally attribute so we can @@ -1344,51 +1442,65 @@ def get_xs(self, groups='all', subdomains='all', nuclides='all', # Add the coarse energy filter back to the nu-fission tally delayed_nu_fission_in.filters.append(energy_filter) - xs = xs_tally.get_values(filters=filters, - filter_bins=filter_bins, value=value) + xs = xs_tally.get_values( + filters=filters, filter_bins=filter_bins, value=value + ) # Get chi delayed for all nuclides in the domain - elif nuclides == 'all': + elif nuclides == "all": nuclides = self.get_nuclides() - xs = self.xs_tally.get_values(filters=filters, - filter_bins=filter_bins, - nuclides=nuclides, value=value) + xs = self.xs_tally.get_values( + filters=filters, + filter_bins=filter_bins, + nuclides=nuclides, + value=value, + ) # Get chi delayed for user-specified nuclides in the domain else: - cv.check_iterable_type('nuclides', nuclides, str) - xs = self.xs_tally.get_values(filters=filters, - filter_bins=filter_bins, - nuclides=nuclides, value=value) + cv.check_iterable_type("nuclides", nuclides, str) + xs = self.xs_tally.get_values( + filters=filters, + filter_bins=filter_bins, + nuclides=nuclides, + value=value, + ) # If chi delayed was computed as an average of nuclides in the domain else: - xs = self.xs_tally.get_values(filters=filters, - filter_bins=filter_bins, value=value) + xs = self.xs_tally.get_values( + filters=filters, filter_bins=filter_bins, value=value + ) # Eliminate the trivial score dimension xs = np.squeeze(xs, axis=len(xs.shape) - 1) xs = np.nan_to_num(xs) # Reshape tally data array with separate axes for domain and energy - if groups == 'all': + if groups == "all": num_groups = self.num_groups else: num_groups = len(groups) - if delayed_groups == 'all': + if delayed_groups == "all": num_delayed_groups = self.num_delayed_groups else: num_delayed_groups = len(delayed_groups) # Reshape tally data array with separate axes for domain, energy # groups, and accomodate the polar and azimuthal bins if needed - num_subdomains = int(xs.shape[0] / (num_delayed_groups * - num_groups * self.num_polar * - self.num_azimuthal)) + num_subdomains = int( + xs.shape[0] + / (num_delayed_groups * num_groups * self.num_polar * self.num_azimuthal) + ) if self.num_polar > 1 or self.num_azimuthal > 1: - new_shape = (self.num_polar, self.num_azimuthal, num_subdomains, - num_delayed_groups, num_groups) + new_shape = ( + self.num_polar, + self.num_azimuthal, + num_subdomains, + num_delayed_groups, + num_groups, + ) else: new_shape = (num_subdomains, num_delayed_groups, num_groups) new_shape += xs.shape[1:] @@ -1396,7 +1508,7 @@ def get_xs(self, groups='all', subdomains='all', nuclides='all', # Reverse data if user requested increasing energy groups since # tally data is stored in order of increasing energies - if order_groups == 'increasing': + if order_groups == "increasing": xs = xs[..., ::-1, :] if squeeze: @@ -1529,12 +1641,28 @@ class DelayedNuFissionXS(MDGXS): """ - def __init__(self, domain=None, domain_type=None, energy_groups=None, - delayed_groups=None, by_nuclide=False, name='', - num_polar=1, num_azimuthal=1): - super().__init__(domain, domain_type, energy_groups, delayed_groups, - by_nuclide, name, num_polar, num_azimuthal) - self._rxn_type = 'delayed-nu-fission' + def __init__( + self, + domain=None, + domain_type=None, + energy_groups=None, + delayed_groups=None, + by_nuclide=False, + name="", + num_polar=1, + num_azimuthal=1, + ): + super().__init__( + domain, + domain_type, + energy_groups, + delayed_groups, + by_nuclide, + name, + num_polar, + num_azimuthal, + ) + self._rxn_type = "delayed-nu-fission" class Beta(MDGXS): @@ -1673,25 +1801,41 @@ class Beta(MDGXS): # quantity, it should not be divided by the number density _divide_by_density = False - def __init__(self, domain=None, domain_type=None, energy_groups=None, - delayed_groups=None, by_nuclide=False, name='', - num_polar=1, num_azimuthal=1): - super().__init__(domain, domain_type, energy_groups, delayed_groups, - by_nuclide, name, num_polar, num_azimuthal) - self._rxn_type = 'beta' + def __init__( + self, + domain=None, + domain_type=None, + energy_groups=None, + delayed_groups=None, + by_nuclide=False, + name="", + num_polar=1, + num_azimuthal=1, + ): + super().__init__( + domain, + domain_type, + energy_groups, + delayed_groups, + by_nuclide, + name, + num_polar, + num_azimuthal, + ) + self._rxn_type = "beta" @property def scores(self): - return ['nu-fission', 'delayed-nu-fission'] + return ["nu-fission", "delayed-nu-fission"] @property def tally_keys(self): - return ['nu-fission', 'delayed-nu-fission'] + return ["nu-fission", "delayed-nu-fission"] @property def rxn_rate_tally(self): if self._rxn_rate_tally is None: - self._rxn_rate_tally = self.tallies['delayed-nu-fission'] + self._rxn_rate_tally = self.tallies["delayed-nu-fission"] self._rxn_rate_tally.sparse = self.sparse return self._rxn_rate_tally @@ -1699,7 +1843,7 @@ def rxn_rate_tally(self): def xs_tally(self): if self._xs_tally is None: - nu_fission = self.tallies['nu-fission'] + nu_fission = self.tallies["nu-fission"] # Compute beta self._xs_tally = self.rxn_rate_tally / nu_fission @@ -1732,7 +1876,7 @@ def get_homogenized_mgxs(self, other_mgxs): """ - return self._get_homogenized_mgxs(other_mgxs, 'nu-fission') + return self._get_homogenized_mgxs(other_mgxs, "nu-fission") class DecayRate(MDGXS): @@ -1866,20 +2010,36 @@ class DecayRate(MDGXS): # macroscopic quantities, it should not be divided by the number density. _divide_by_density = False - def __init__(self, domain=None, domain_type=None, energy_groups=None, - delayed_groups=None, by_nuclide=False, name='', - num_polar=1, num_azimuthal=1): - super().__init__(domain, domain_type, energy_groups, delayed_groups, - by_nuclide, name, num_polar, num_azimuthal) - self._rxn_type = 'decay-rate' + def __init__( + self, + domain=None, + domain_type=None, + energy_groups=None, + delayed_groups=None, + by_nuclide=False, + name="", + num_polar=1, + num_azimuthal=1, + ): + super().__init__( + domain, + domain_type, + energy_groups, + delayed_groups, + by_nuclide, + name, + num_polar, + num_azimuthal, + ) + self._rxn_type = "decay-rate" @property def scores(self): - return ['delayed-nu-fission', 'decay-rate'] + return ["delayed-nu-fission", "decay-rate"] @property def tally_keys(self): - return ['delayed-nu-fission', 'decay-rate'] + return ["delayed-nu-fission", "decay-rate"] @property def filters(self): @@ -1896,7 +2056,7 @@ def filters(self): def xs_tally(self): if self._xs_tally is None: - delayed_nu_fission = self.tallies['delayed-nu-fission'] + delayed_nu_fission = self.tallies["delayed-nu-fission"] # Compute the decay rate self._xs_tally = self.rxn_rate_tally / delayed_nu_fission @@ -1929,11 +2089,19 @@ def get_homogenized_mgxs(self, other_mgxs): """ - return self._get_homogenized_mgxs(other_mgxs, 'delayed-nu-fission') - - def get_xs(self, subdomains='all', nuclides='all', - xs_type='macro', order_groups='increasing', - value='mean', delayed_groups='all', squeeze=True, **kwargs): + return self._get_homogenized_mgxs(other_mgxs, "delayed-nu-fission") + + def get_xs( + self, + subdomains="all", + nuclides="all", + xs_type="macro", + order_groups="increasing", + value="mean", + delayed_groups="all", + squeeze=True, + **kwargs, + ): """Returns an array of multi-delayed-group cross sections. This method constructs a 4D NumPy array for the requested @@ -1979,14 +2147,16 @@ def get_xs(self, subdomains='all', nuclides='all', """ - cv.check_value('value', value, ['mean', 'std_dev', 'rel_err']) - cv.check_value('xs_type', xs_type, ['macro', 'micro']) + cv.check_value("value", value, ["mean", "std_dev", "rel_err"]) + cv.check_value("xs_type", xs_type, ["macro", "micro"]) # FIXME: Unable to get microscopic xs for mesh domain because the mesh # cells do not know the nuclide densities in each mesh cell. - if self.domain_type == 'mesh' and xs_type == 'micro': - msg = 'Unable to get micro xs for mesh domain since the mesh ' \ - 'cells do not know the nuclide densities in each mesh cell.' + if self.domain_type == "mesh" and xs_type == "micro": + msg = ( + "Unable to get micro xs for mesh domain since the mesh " + "cells do not know the nuclide densities in each mesh cell." + ) raise ValueError(msg) filters = [] @@ -1994,52 +2164,55 @@ def get_xs(self, subdomains='all', nuclides='all', # Construct a collection of the domain filter bins if not isinstance(subdomains, str): - cv.check_iterable_type('subdomains', subdomains, Integral, - max_depth=3) + cv.check_iterable_type("subdomains", subdomains, Integral, max_depth=3) for subdomain in subdomains: filters.append(_DOMAIN_TO_FILTER[self.domain_type]) filter_bins.append((subdomain,)) # Construct list of delayed group tuples for all requested groups if not isinstance(delayed_groups, str): - cv.check_type('delayed groups', delayed_groups, list, int) + cv.check_type("delayed groups", delayed_groups, list, int) for delayed_group in delayed_groups: filters.append(openmc.DelayedGroupFilter) filter_bins.append((delayed_group,)) # Construct a collection of the nuclides to retrieve from the xs tally if self.by_nuclide: - if nuclides == 'all' or nuclides == 'sum' or nuclides == ['sum']: + if nuclides == "all" or nuclides == "sum" or nuclides == ["sum"]: query_nuclides = self.get_nuclides() else: query_nuclides = nuclides else: - query_nuclides = ['total'] + query_nuclides = ["total"] # If user requested the sum for all nuclides, use tally summation - if nuclides == 'sum' or nuclides == ['sum']: + if nuclides == "sum" or nuclides == ["sum"]: xs_tally = self.xs_tally.summation(nuclides=query_nuclides) - xs = xs_tally.get_values(filters=filters, - filter_bins=filter_bins, value=value) + xs = xs_tally.get_values( + filters=filters, filter_bins=filter_bins, value=value + ) else: - xs = self.xs_tally.get_values(filters=filters, - filter_bins=filter_bins, - nuclides=query_nuclides, value=value) + xs = self.xs_tally.get_values( + filters=filters, + filter_bins=filter_bins, + nuclides=query_nuclides, + value=value, + ) # Divide by atom number densities for microscopic cross sections - if xs_type == 'micro' and self._divide_by_density: + if xs_type == "micro" and self._divide_by_density: if self.by_nuclide: densities = self.get_nuclide_densities(nuclides) else: - densities = self.get_nuclide_densities('sum') - if value == 'mean' or value == 'std_dev': + densities = self.get_nuclide_densities("sum") + if value == "mean" or value == "std_dev": xs /= densities[np.newaxis, :, np.newaxis] # Eliminate the trivial score dimension xs = np.squeeze(xs, axis=len(xs.shape) - 1) xs = np.nan_to_num(xs) - if delayed_groups == 'all': + if delayed_groups == "all": num_delayed_groups = self.num_delayed_groups else: num_delayed_groups = len(delayed_groups) @@ -2047,12 +2220,16 @@ def get_xs(self, subdomains='all', nuclides='all', # Reshape tally data array with separate axes for domain, # energy groups, delayed groups, and nuclides # Accommodate the polar and azimuthal bins if needed - num_subdomains = \ - int(xs.shape[0] / (num_delayed_groups * - self.num_polar * self.num_azimuthal)) + num_subdomains = int( + xs.shape[0] / (num_delayed_groups * self.num_polar * self.num_azimuthal) + ) if self.num_polar > 1 or self.num_azimuthal > 1: - new_shape = (self.num_polar, self.num_azimuthal, num_subdomains, - num_delayed_groups) + new_shape = ( + self.num_polar, + self.num_azimuthal, + num_subdomains, + num_delayed_groups, + ) else: new_shape = (num_subdomains, num_delayed_groups) xs = np.reshape(xs, new_shape) @@ -2196,11 +2373,20 @@ def filters(self): return self._add_angle_filters(filters) - def get_xs(self, in_groups='all', out_groups='all', - subdomains='all', nuclides='all', - xs_type='macro', order_groups='increasing', - row_column='inout', value='mean', delayed_groups='all', - squeeze=True, **kwargs): + def get_xs( + self, + in_groups="all", + out_groups="all", + subdomains="all", + nuclides="all", + xs_type="macro", + order_groups="increasing", + row_column="inout", + value="mean", + delayed_groups="all", + squeeze=True, + **kwargs, + ): """Returns an array of multi-group cross sections. This method constructs a 4D NumPy array for the requested @@ -2256,14 +2442,16 @@ def get_xs(self, in_groups='all', out_groups='all', """ - cv.check_value('value', value, ['mean', 'std_dev', 'rel_err']) - cv.check_value('xs_type', xs_type, ['macro', 'micro']) + cv.check_value("value", value, ["mean", "std_dev", "rel_err"]) + cv.check_value("xs_type", xs_type, ["macro", "micro"]) # FIXME: Unable to get microscopic xs for mesh domain because the mesh # cells do not know the nuclide densities in each mesh cell. - if self.domain_type == 'mesh' and xs_type == 'micro': - msg = 'Unable to get micro xs for mesh domain since the mesh ' \ - 'cells do not know the nuclide densities in each mesh cell.' + if self.domain_type == "mesh" and xs_type == "micro": + msg = ( + "Unable to get micro xs for mesh domain since the mesh " + "cells do not know the nuclide densities in each mesh cell." + ) raise ValueError(msg) filters = [] @@ -2271,61 +2459,62 @@ def get_xs(self, in_groups='all', out_groups='all', # Construct a collection of the domain filter bins if not isinstance(subdomains, str): - cv.check_iterable_type('subdomains', subdomains, Integral, - max_depth=3) + cv.check_iterable_type("subdomains", subdomains, Integral, max_depth=3) for subdomain in subdomains: filters.append(_DOMAIN_TO_FILTER[self.domain_type]) filter_bins.append((subdomain,)) # Construct list of energy group bounds tuples for all requested groups if not isinstance(in_groups, str): - cv.check_iterable_type('groups', in_groups, Integral) + cv.check_iterable_type("groups", in_groups, Integral) for group in in_groups: filters.append(openmc.EnergyFilter) - filter_bins.append(( - self.energy_groups.get_group_bounds(group),)) + filter_bins.append((self.energy_groups.get_group_bounds(group),)) # Construct list of energy group bounds tuples for all requested groups if not isinstance(out_groups, str): - cv.check_iterable_type('groups', out_groups, Integral) + cv.check_iterable_type("groups", out_groups, Integral) for group in out_groups: filters.append(openmc.EnergyoutFilter) - filter_bins.append(( - self.energy_groups.get_group_bounds(group),)) + filter_bins.append((self.energy_groups.get_group_bounds(group),)) # Construct list of delayed group tuples for all requested groups if not isinstance(delayed_groups, str): - cv.check_type('delayed groups', delayed_groups, list, int) + cv.check_type("delayed groups", delayed_groups, list, int) for delayed_group in delayed_groups: filters.append(openmc.DelayedGroupFilter) filter_bins.append((delayed_group,)) # Construct a collection of the nuclides to retrieve from the xs tally if self.by_nuclide: - if nuclides == 'all' or nuclides == 'sum' or nuclides == ['sum']: + if nuclides == "all" or nuclides == "sum" or nuclides == ["sum"]: query_nuclides = self.get_nuclides() else: query_nuclides = nuclides else: - query_nuclides = ['total'] + query_nuclides = ["total"] # Use tally summation if user requested the sum for all nuclides - if nuclides == 'sum' or nuclides == ['sum']: + if nuclides == "sum" or nuclides == ["sum"]: xs_tally = self.xs_tally.summation(nuclides=query_nuclides) - xs = xs_tally.get_values(filters=filters, filter_bins=filter_bins, - value=value) + xs = xs_tally.get_values( + filters=filters, filter_bins=filter_bins, value=value + ) else: - xs = self.xs_tally.get_values(filters=filters, - filter_bins=filter_bins, - nuclides=query_nuclides, value=value) + xs = self.xs_tally.get_values( + filters=filters, + filter_bins=filter_bins, + nuclides=query_nuclides, + value=value, + ) # Divide by atom number densities for microscopic cross sections - if xs_type == 'micro' and self._divide_by_density: + if xs_type == "micro" and self._divide_by_density: if self.by_nuclide: densities = self.get_nuclide_densities(nuclides) else: - densities = self.get_nuclide_densities('sum') - if value == 'mean' or value == 'std_dev': + densities = self.get_nuclide_densities("sum") + if value == "mean" or value == "std_dev": xs /= densities[np.newaxis, :, np.newaxis] # Eliminate the trivial score dimension @@ -2334,49 +2523,65 @@ def get_xs(self, in_groups='all', out_groups='all', # Eliminate NaNs which may have been produced by dividing by density xs = np.nan_to_num(xs) - if in_groups == 'all': + if in_groups == "all": num_in_groups = self.num_groups else: num_in_groups = len(in_groups) - if out_groups == 'all': + if out_groups == "all": num_out_groups = self.num_groups else: num_out_groups = len(out_groups) - if delayed_groups == 'all': + if delayed_groups == "all": num_delayed_groups = self.num_delayed_groups else: num_delayed_groups = len(delayed_groups) # Reshape tally data array with separate axes for domain and energy # Accomodate the polar and azimuthal bins if needed - num_subdomains = int(xs.shape[0] / (num_delayed_groups * - num_in_groups * num_out_groups * - self.num_polar * - self.num_azimuthal)) + num_subdomains = int( + xs.shape[0] + / ( + num_delayed_groups + * num_in_groups + * num_out_groups + * self.num_polar + * self.num_azimuthal + ) + ) if self.num_polar > 1 or self.num_azimuthal > 1: - new_shape = (self.num_polar, self.num_azimuthal, num_subdomains, - num_delayed_groups, num_in_groups, num_out_groups) + new_shape = ( + self.num_polar, + self.num_azimuthal, + num_subdomains, + num_delayed_groups, + num_in_groups, + num_out_groups, + ) new_shape += xs.shape[1:] xs = np.reshape(xs, new_shape) # Transpose the matrix if requested by user - if row_column == 'outin': + if row_column == "outin": xs = np.swapaxes(xs, 4, 5) else: - new_shape = (num_subdomains, num_delayed_groups, num_in_groups, - num_out_groups) + new_shape = ( + num_subdomains, + num_delayed_groups, + num_in_groups, + num_out_groups, + ) new_shape += xs.shape[1:] xs = np.reshape(xs, new_shape) # Transpose the matrix if requested by user - if row_column == 'outin': + if row_column == "outin": xs = np.swapaxes(xs, 2, 3) # Reverse data if user requested increasing energy groups since # tally data is stored in order of increasing energies - if order_groups == 'increasing': + if order_groups == "increasing": xs = xs[..., ::-1, ::-1, :] if squeeze: @@ -2386,8 +2591,7 @@ def get_xs(self, in_groups='all', out_groups='all', return xs - def get_slice(self, nuclides=[], in_groups=[], out_groups=[], - delayed_groups=[]): + def get_slice(self, nuclides=[], in_groups=[], out_groups=[], delayed_groups=[]): """Build a sliced MatrixMDGXS object for the specified nuclides and energy groups. @@ -2437,14 +2641,14 @@ def get_slice(self, nuclides=[], in_groups=[], out_groups=[], for tally_type, tally in slice_xs.tallies.items(): if tally.contains_filter(openmc.EnergyoutFilter): tally_slice = tally.get_slice( - filters=[openmc.EnergyoutFilter], - filter_bins=filter_bins) + filters=[openmc.EnergyoutFilter], filter_bins=filter_bins + ) slice_xs.tallies[tally_type] = tally_slice slice_xs.sparse = self.sparse return slice_xs - def print_xs(self, subdomains='all', nuclides='all', xs_type='macro'): + def print_xs(self, subdomains="all", nuclides="all", xs_type="macro"): """Prints a string representation for the multi-group cross section. Parameters @@ -2467,10 +2671,10 @@ def print_xs(self, subdomains='all', nuclides='all', xs_type='macro'): # Construct a collection of the subdomains to report if not isinstance(subdomains, str): - cv.check_iterable_type('subdomains', subdomains, Integral) - elif self.domain_type == 'distribcell': + cv.check_iterable_type("subdomains", subdomains, Integral) + elif self.domain_type == "distribcell": subdomains = np.arange(self.num_subdomains, dtype=int) - elif self.domain_type == 'mesh': + elif self.domain_type == "mesh": xyz = [range(1, x + 1) for x in self.domain.dimension] subdomains = list(itertools.product(*xyz)) else: @@ -2478,165 +2682,200 @@ def print_xs(self, subdomains='all', nuclides='all', xs_type='macro'): # Construct a collection of the nuclides to report if self.by_nuclide: - if nuclides == 'all': + if nuclides == "all": nuclides = self.get_nuclides() - if nuclides == 'sum': - nuclides = ['sum'] + if nuclides == "sum": + nuclides = ["sum"] else: - cv.check_iterable_type('nuclides', nuclides, str) + cv.check_iterable_type("nuclides", nuclides, str) else: - nuclides = ['sum'] + nuclides = ["sum"] - cv.check_value('xs_type', xs_type, ['macro', 'micro']) + cv.check_value("xs_type", xs_type, ["macro", "micro"]) # Build header for string with type and domain info - string = 'Multi-Delayed-Group XS\n' - string += '{0: <16}=\t{1}\n'.format('\tReaction Type', self.mgxs_type) - string += '{0: <16}=\t{1}\n'.format('\tDomain Type', self.domain_type) - string += '{0: <16}=\t{1}\n'.format('\tDomain ID', self.domain.id) + string = "Multi-Delayed-Group XS\n" + string += "{0: <16}=\t{1}\n".format("\tReaction Type", self.mgxs_type) + string += "{0: <16}=\t{1}\n".format("\tDomain Type", self.domain_type) + string += "{0: <16}=\t{1}\n".format("\tDomain ID", self.domain.id) # Generate the header for an individual XS - xs_header = f'\tCross Sections [{self.get_units(xs_type)}]:' + xs_header = f"\tCross Sections [{self.get_units(xs_type)}]:" # If cross section data has not been computed, only print string header if self.tallies is None: print(string) return - string += '{0: <16}\n'.format('\tEnergy Groups:') - template = '{0: <12}Group {1} [{2: <10} - {3: <10}eV]\n' + string += "{0: <16}\n".format("\tEnergy Groups:") + template = "{0: <12}Group {1} [{2: <10} - {3: <10}eV]\n" # Loop over energy groups ranges for group in range(1, self.num_groups + 1): bounds = self.energy_groups.get_group_bounds(group) - string += template.format('', group, bounds[0], bounds[1]) + string += template.format("", group, bounds[0], bounds[1]) # Set polar and azimuthal bins if necessary if self.num_polar > 1 or self.num_azimuthal > 1: - pol_bins = np.linspace(0., np.pi, num=self.num_polar + 1, - endpoint=True) - azi_bins = np.linspace(-np.pi, np.pi, num=self.num_azimuthal + 1, - endpoint=True) + pol_bins = np.linspace(0.0, np.pi, num=self.num_polar + 1, endpoint=True) + azi_bins = np.linspace( + -np.pi, np.pi, num=self.num_azimuthal + 1, endpoint=True + ) # Loop over all subdomains for subdomain in subdomains: - if self.domain_type == 'distribcell': - string += '{: <16}=\t{}\n'.format('\tSubdomain', subdomain) + if self.domain_type == "distribcell": + string += "{: <16}=\t{}\n".format("\tSubdomain", subdomain) # Loop over all Nuclides for nuclide in nuclides: # Build header for nuclide type - if xs_type != 'sum': - string += '{: <16}=\t{}\n'.format('\tNuclide', nuclide) + if xs_type != "sum": + string += "{: <16}=\t{}\n".format("\tNuclide", nuclide) # Build header for cross section type - string += f'{xs_header: <16}\n' + string += f"{xs_header: <16}\n" if self.delayed_groups is not None: for delayed_group in self.delayed_groups: - template = '{0: <12}Delayed Group {1}:\t' - string += template.format('', delayed_group) - string += '\n' - - template = '{0: <12}Group {1} -> Group {2}:\t\t' - - average_xs = self.get_xs(nuclides=[nuclide], - subdomains=[subdomain], - xs_type=xs_type, value='mean', - delayed_groups=[delayed_group]) - rel_err_xs = self.get_xs(nuclides=[nuclide], - subdomains=[subdomain], - xs_type=xs_type, - value='rel_err', - delayed_groups=[delayed_group]) - rel_err_xs = rel_err_xs * 100. + template = "{0: <12}Delayed Group {1}:\t" + string += template.format("", delayed_group) + string += "\n" + + template = "{0: <12}Group {1} -> Group {2}:\t\t" + + average_xs = self.get_xs( + nuclides=[nuclide], + subdomains=[subdomain], + xs_type=xs_type, + value="mean", + delayed_groups=[delayed_group], + ) + rel_err_xs = self.get_xs( + nuclides=[nuclide], + subdomains=[subdomain], + xs_type=xs_type, + value="rel_err", + delayed_groups=[delayed_group], + ) + rel_err_xs = rel_err_xs * 100.0 if self.num_polar > 1 or self.num_azimuthal > 1: # Loop over polar, azi, and in/out group ranges for pol in range(len(pol_bins) - 1): - pol_low, pol_high = pol_bins[pol: pol + 2] + pol_low, pol_high = pol_bins[pol : pol + 2] for azi in range(len(azi_bins) - 1): - azi_low, azi_high = azi_bins[azi: azi + 2] - string += '\t\tPolar Angle: [{0:5f} - {1:5f}]'.format( - pol_low, pol_high) + \ - '\tAzimuthal Angle: [{0:5f} - {1:5f}]'.format( - azi_low, azi_high) + '\n' + azi_low, azi_high = azi_bins[azi : azi + 2] + string += ( + "\t\tPolar Angle: [{0:5f} - {1:5f}]".format( + pol_low, pol_high + ) + + "\tAzimuthal Angle: [{0:5f} - {1:5f}]".format( + azi_low, azi_high + ) + + "\n" + ) for in_group in range(1, self.num_groups + 1): for out_group in range(1, self.num_groups + 1): - string += '\t' + template.format( - '', in_group, out_group) - string += '{0:.2e} +/- {1:.2e}%'.format( - average_xs[pol, azi, in_group - 1, - out_group - 1], - rel_err_xs[pol, azi, in_group - 1, - out_group - 1]) - string += '\n' - string += '\n' - string += '\n' + string += "\t" + template.format( + "", in_group, out_group + ) + string += "{0:.2e} +/- {1:.2e}%".format( + average_xs[ + pol, + azi, + in_group - 1, + out_group - 1, + ], + rel_err_xs[ + pol, + azi, + in_group - 1, + out_group - 1, + ], + ) + string += "\n" + string += "\n" + string += "\n" else: # Loop over incoming/outgoing energy groups ranges for in_group in range(1, self.num_groups + 1): for out_group in range(1, self.num_groups + 1): - string += template.format( - '', in_group, out_group) - string += '{:.2e} +/- {:.2e}%'.format( - average_xs[in_group-1, out_group-1], - rel_err_xs[in_group-1, out_group-1]) - string += '\n' - string += '\n' - string += '\n' + string += template.format("", in_group, out_group) + string += "{:.2e} +/- {:.2e}%".format( + average_xs[in_group - 1, out_group - 1], + rel_err_xs[in_group - 1, out_group - 1], + ) + string += "\n" + string += "\n" + string += "\n" else: - template = '{0: <12}Group {1} -> Group {2}:\t\t' - - average_xs = self.get_xs(nuclides=[nuclide], - subdomains=[subdomain], - xs_type=xs_type, value='mean') - rel_err_xs = self.get_xs(nuclides=[nuclide], - subdomains=[subdomain], - xs_type=xs_type, value='rel_err') - rel_err_xs = rel_err_xs * 100. + template = "{0: <12}Group {1} -> Group {2}:\t\t" + + average_xs = self.get_xs( + nuclides=[nuclide], + subdomains=[subdomain], + xs_type=xs_type, + value="mean", + ) + rel_err_xs = self.get_xs( + nuclides=[nuclide], + subdomains=[subdomain], + xs_type=xs_type, + value="rel_err", + ) + rel_err_xs = rel_err_xs * 100.0 if self.num_polar > 1 or self.num_azimuthal > 1: # Loop over polar, azi, and in/out energy group ranges for pol in range(len(pol_bins) - 1): - pol_low, pol_high = pol_bins[pol: pol + 2] + pol_low, pol_high = pol_bins[pol : pol + 2] for azi in range(len(azi_bins) - 1): - azi_low, azi_high = azi_bins[azi: azi + 2] - string += '\t\tPolar Angle: [{0:5f} - {1:5f}]'.format( - pol_low, pol_high) + \ - '\tAzimuthal Angle: [{0:5f} - {1:5f}]'.format( - azi_low, azi_high) + '\n' + azi_low, azi_high = azi_bins[azi : azi + 2] + string += ( + "\t\tPolar Angle: [{0:5f} - {1:5f}]".format( + pol_low, pol_high + ) + + "\tAzimuthal Angle: [{0:5f} - {1:5f}]".format( + azi_low, azi_high + ) + + "\n" + ) for in_group in range(1, self.num_groups + 1): for out_group in range(1, self.num_groups + 1): - string += '\t' + template.format( - '', in_group, out_group) - string += '{0:.2e} +/- {1:.2e}%'.format( - average_xs[pol, azi, in_group - 1, - out_group - 1], - rel_err_xs[pol, azi, in_group - 1, - out_group - 1]) - string += '\n' - string += '\n' - string += '\n' + string += "\t" + template.format( + "", in_group, out_group + ) + string += "{0:.2e} +/- {1:.2e}%".format( + average_xs[ + pol, azi, in_group - 1, out_group - 1 + ], + rel_err_xs[ + pol, azi, in_group - 1, out_group - 1 + ], + ) + string += "\n" + string += "\n" + string += "\n" else: # Loop over incoming/outgoing energy groups ranges for in_group in range(1, self.num_groups + 1): for out_group in range(1, self.num_groups + 1): - string += template.format('', in_group, - out_group) - string += '{0:.2e} +/- {1:.2e}%'.format( + string += template.format("", in_group, out_group) + string += "{0:.2e} +/- {1:.2e}%".format( average_xs[in_group - 1, out_group - 1], - rel_err_xs[in_group - 1, out_group - 1]) - string += '\n' - string += '\n' - string += '\n' - string += '\n' - string += '\n' + rel_err_xs[in_group - 1, out_group - 1], + ) + string += "\n" + string += "\n" + string += "\n" + string += "\n" + string += "\n" print(string) @@ -2770,12 +3009,28 @@ class DelayedNuFissionMatrixXS(MatrixMDGXS): """ - def __init__(self, domain=None, domain_type=None, energy_groups=None, - delayed_groups=None, by_nuclide=False, name='', - num_polar=1, num_azimuthal=1): - super().__init__(domain, domain_type, energy_groups, delayed_groups, - by_nuclide, name, num_polar, num_azimuthal) - self._rxn_type = 'delayed-nu-fission' - self._mgxs_type = 'delayed-nu-fission matrix' - self._estimator = 'analog' - self._valid_estimators = ['analog'] + def __init__( + self, + domain=None, + domain_type=None, + energy_groups=None, + delayed_groups=None, + by_nuclide=False, + name="", + num_polar=1, + num_azimuthal=1, + ): + super().__init__( + domain, + domain_type, + energy_groups, + delayed_groups, + by_nuclide, + name, + num_polar, + num_azimuthal, + ) + self._rxn_type = "delayed-nu-fission" + self._mgxs_type = "delayed-nu-fission matrix" + self._estimator = "analog" + self._valid_estimators = ["analog"] diff --git a/openmc/mgxs/mgxs.py b/openmc/mgxs/mgxs.py index 588b6f64e2a..4c93c06b00b 100644 --- a/openmc/mgxs/mgxs.py +++ b/openmc/mgxs/mgxs.py @@ -15,40 +15,39 @@ # Supported cross section types MGXS_TYPES = ( - 'total', - 'transport', - 'nu-transport', - 'absorption', - 'reduced absorption', - 'capture', - 'fission', - 'nu-fission', - 'kappa-fission', - 'scatter', - 'nu-scatter', - 'scatter matrix', - 'nu-scatter matrix', - 'multiplicity matrix', - 'nu-fission matrix', - 'scatter probability matrix', - 'consistent scatter matrix', - 'consistent nu-scatter matrix', - 'chi', - 'chi-prompt', - 'inverse-velocity', - 'prompt-nu-fission', - 'prompt-nu-fission matrix', - 'current', - 'diffusion-coefficient', - 'nu-diffusion-coefficient' + "total", + "transport", + "nu-transport", + "absorption", + "reduced absorption", + "capture", + "fission", + "nu-fission", + "kappa-fission", + "scatter", + "nu-scatter", + "scatter matrix", + "nu-scatter matrix", + "multiplicity matrix", + "nu-fission matrix", + "scatter probability matrix", + "consistent scatter matrix", + "consistent nu-scatter matrix", + "chi", + "chi-prompt", + "inverse-velocity", + "prompt-nu-fission", + "prompt-nu-fission matrix", + "current", + "diffusion-coefficient", + "nu-diffusion-coefficient", ) # Some scores from REACTION_MT are not supported, or are simply overkill to # support and test (like inelastic levels), remoev those from consideration _BAD_SCORES = ["(n,misc)", "(n,absorption)", "(n,total)", "fission"] _BAD_SCORES += [REACTION_NAME[mt] for mt in FISSION_MTS] -ARBITRARY_VECTOR_TYPES = tuple(k for k in REACTION_MT.keys() - if k not in _BAD_SCORES) +ARBITRARY_VECTOR_TYPES = tuple(k for k in REACTION_MT.keys() if k not in _BAD_SCORES) ARBITRARY_MATRIX_TYPES = [] for rxn in ARBITRARY_VECTOR_TYPES: # Preclude the fission channels from being treated as a matrix @@ -60,48 +59,35 @@ ARBITRARY_MATRIX_TYPES = tuple(ARBITRARY_MATRIX_TYPES) # Supported domain types -DOMAIN_TYPES = ( - 'cell', - 'distribcell', - 'universe', - 'material', - 'mesh' -) +DOMAIN_TYPES = ("cell", "distribcell", "universe", "material", "mesh") # Filter types corresponding to each domain _DOMAIN_TO_FILTER = { - 'cell': openmc.CellFilter, - 'distribcell': openmc.DistribcellFilter, - 'universe': openmc.UniverseFilter, - 'material': openmc.MaterialFilter, - 'mesh': openmc.MeshFilter + "cell": openmc.CellFilter, + "distribcell": openmc.DistribcellFilter, + "universe": openmc.UniverseFilter, + "material": openmc.MaterialFilter, + "mesh": openmc.MeshFilter, } # Supported domain classes -_DOMAINS = ( - openmc.Cell, - openmc.Universe, - openmc.Material, - openmc.RegularMesh -) +_DOMAINS = (openmc.Cell, openmc.Universe, openmc.Material, openmc.RegularMesh) # Supported ScatterMatrixXS angular distribution types. Note that 'histogram' is # defined here and used in mgxs_library.py, but it is not used for the current # module -SCATTER_TABULAR = 'tabular' -SCATTER_LEGENDRE = 'legendre' -SCATTER_HISTOGRAM = 'histogram' -MU_TREATMENTS = ( - SCATTER_LEGENDRE, - SCATTER_HISTOGRAM -) +SCATTER_TABULAR = "tabular" +SCATTER_LEGENDRE = "legendre" +SCATTER_HISTOGRAM = "histogram" +MU_TREATMENTS = (SCATTER_LEGENDRE, SCATTER_HISTOGRAM) # Maximum Legendre order supported by OpenMC _MAX_LEGENDRE = 10 -def _df_column_convert_to_bin(df, current_name, new_name, values_to_bin, - reverse_order=False): +def _df_column_convert_to_bin( + df, current_name, new_name, values_to_bin, reverse_order=False +): """Convert a Pandas DataFrame column from the bin edges to an index for each bin. This method operates on the DataFrame, df, in-place. @@ -151,6 +137,7 @@ def add_params(cls): cls.__doc__ += cls._params return cls + @add_params class MGXS: """An abstract multi-group cross section for some energy group structure @@ -257,14 +244,21 @@ class MGXS: # values of this data _divide_by_density = True - def __init__(self, domain=None, domain_type=None, - energy_groups=None, by_nuclide=False, name='', num_polar=1, - num_azimuthal=1): - self._name = '' + def __init__( + self, + domain=None, + domain_type=None, + energy_groups=None, + by_nuclide=False, + name="", + num_polar=1, + num_azimuthal=1, + ): + self._name = "" self._rxn_type = None self._by_nuclide = None self._nuclides = None - self._estimator = 'tracklength' + self._estimator = "tracklength" self._domain = None self._domain_type = None self._energy_groups = None @@ -346,10 +340,10 @@ def _add_angle_filters(self, filters): if self.num_polar > 1 or self.num_azimuthal > 1: # Then the user has requested angular data, so create the bins - pol_bins = np.linspace(0., np.pi, num=self.num_polar + 1, - endpoint=True) - azi_bins = np.linspace(-np.pi, np.pi, num=self.num_azimuthal + 1, - endpoint=True) + pol_bins = np.linspace(0.0, np.pi, num=self.num_polar + 1, endpoint=True) + azi_bins = np.linspace( + -np.pi, np.pi, num=self.num_azimuthal + 1, endpoint=True + ) for filt in filters: filt.insert(0, openmc.PolarFilter(pol_bins)) @@ -402,39 +396,46 @@ def _df_convert_columns_to_bins(self, df): # Override polar and azimuthal bounds with indices if self.num_polar > 1 or self.num_azimuthal > 1: # First for polar - bins = np.linspace(0., np.pi, self.num_polar + 1, True) - _df_column_convert_to_bin(df, 'polar low', 'polar bin', bins) - del df['polar high'] + bins = np.linspace(0.0, np.pi, self.num_polar + 1, True) + _df_column_convert_to_bin(df, "polar low", "polar bin", bins) + del df["polar high"] # Second for azimuthal bins = np.linspace(-np.pi, np.pi, self.num_azimuthal + 1, True) - _df_column_convert_to_bin(df, 'azimuthal low', 'azimuthal bin', - bins) - del df['azimuthal high'] - columns = ['polar bin', 'azimuthal bin'] + _df_column_convert_to_bin(df, "azimuthal low", "azimuthal bin", bins) + del df["azimuthal high"] + columns = ["polar bin", "azimuthal bin"] else: columns = [] # Override energy groups bounds with indices - if 'energy low [eV]' in df: - _df_column_convert_to_bin(df, 'energy low [eV]', 'group in', - self.energy_groups.group_edges, - reverse_order=True) - del df['energy high [eV]'] - columns += ['group in'] - if 'energyout low [eV]' in df: - _df_column_convert_to_bin(df, 'energyout low [eV]', 'group out', - self.energy_groups.group_edges, - reverse_order=True) - del df['energyout high [eV]'] - columns += ['group out'] - - if 'mu low' in df and hasattr(self, 'histogram_bins'): + if "energy low [eV]" in df: + _df_column_convert_to_bin( + df, + "energy low [eV]", + "group in", + self.energy_groups.group_edges, + reverse_order=True, + ) + del df["energy high [eV]"] + columns += ["group in"] + if "energyout low [eV]" in df: + _df_column_convert_to_bin( + df, + "energyout low [eV]", + "group out", + self.energy_groups.group_edges, + reverse_order=True, + ) + del df["energyout high [eV]"] + columns += ["group out"] + + if "mu low" in df and hasattr(self, "histogram_bins"): # Only the ScatterMatrix class has the histogram_bins attribute - bins = np.linspace(-1., 1., self.histogram_bins + 1, True) - _df_column_convert_to_bin(df, 'mu low', 'mu bin', bins) - del df['mu high'] - columns += ['mu bin'] + bins = np.linspace(-1.0, 1.0, self.histogram_bins + 1, True) + _df_column_convert_to_bin(df, "mu low", "mu bin", bins) + del df["mu high"] + columns += ["mu bin"] return columns @@ -446,7 +447,7 @@ def _dont_squeeze(self): if self.num_polar > 1 or self.num_azimuthal > 1: return (0, 1, 3) else: - return (1, ) + return (1,) @property def name(self): @@ -454,7 +455,7 @@ def name(self): @name.setter def name(self, name): - cv.check_type('name', name, str) + cv.check_type("name", name, str) self._name = name @property @@ -467,7 +468,7 @@ def by_nuclide(self): @by_nuclide.setter def by_nuclide(self, by_nuclide): - cv.check_type('by_nuclide', by_nuclide, bool) + cv.check_type("by_nuclide", by_nuclide, bool) self._by_nuclide = by_nuclide @property @@ -476,19 +477,19 @@ def domain(self): @domain.setter def domain(self, domain): - cv.check_type('domain', domain, _DOMAINS) + cv.check_type("domain", domain, _DOMAINS) self._domain = domain # Assign a domain type if self.domain_type is None: if isinstance(domain, openmc.Material): - self._domain_type = 'material' + self._domain_type = "material" elif isinstance(domain, openmc.Cell): - self._domain_type = 'cell' + self._domain_type = "cell" elif isinstance(domain, openmc.Universe): - self._domain_type = 'universe' + self._domain_type = "universe" elif isinstance(domain, openmc.RegularMesh): - self._domain_type = 'mesh' + self._domain_type = "mesh" @property def domain_type(self): @@ -496,7 +497,7 @@ def domain_type(self): @domain_type.setter def domain_type(self, domain_type): - cv.check_value('domain type', domain_type, DOMAIN_TYPES) + cv.check_value("domain type", domain_type, DOMAIN_TYPES) self._domain_type = domain_type @property @@ -505,7 +506,7 @@ def energy_groups(self): @energy_groups.setter def energy_groups(self, energy_groups): - cv.check_type('energy groups', energy_groups, openmc.mgxs.EnergyGroups) + cv.check_type("energy groups", energy_groups, openmc.mgxs.EnergyGroups) self._energy_groups = energy_groups @property @@ -514,8 +515,8 @@ def num_polar(self): @num_polar.setter def num_polar(self, num_polar): - cv.check_type('num_polar', num_polar, Integral) - cv.check_greater_than('num_polar', num_polar, 0) + cv.check_type("num_polar", num_polar, Integral) + cv.check_greater_than("num_polar", num_polar, 0) self._num_polar = num_polar @property @@ -524,8 +525,8 @@ def num_azimuthal(self): @num_azimuthal.setter def num_azimuthal(self, num_azimuthal): - cv.check_type('num_azimuthal', num_azimuthal, Integral) - cv.check_greater_than('num_azimuthal', num_azimuthal, 0) + cv.check_type("num_azimuthal", num_azimuthal, Integral) + cv.check_greater_than("num_azimuthal", num_azimuthal, 0) self._num_azimuthal = num_azimuthal @property @@ -534,7 +535,7 @@ def tally_trigger(self): @tally_trigger.setter def tally_trigger(self, tally_trigger): - cv.check_type('tally trigger', tally_trigger, openmc.Trigger) + cv.check_type("tally trigger", tally_trigger, openmc.Trigger) self._tally_trigger = tally_trigger @property @@ -543,7 +544,7 @@ def num_groups(self): @property def scores(self): - return ['flux', self.rxn_type] + return ["flux", self.rxn_type] @property def filters(self): @@ -565,7 +566,7 @@ def estimator(self): @estimator.setter def estimator(self, estimator): - cv.check_value('estimator', estimator, self._valid_estimators) + cv.check_value("estimator", estimator, self._valid_estimators) self._estimator = estimator @property @@ -575,11 +576,11 @@ def tallies(self): if self._tallies is None: # Initialize a collection of Tallies - self._tallies ={} + self._tallies = {} # Create a domain Filter object filter_type = _DOMAIN_TO_FILTER[self.domain_type] - if self.domain_type == 'mesh': + if self.domain_type == "mesh": domain_filter = filter_type(self.domain) else: domain_filter = filter_type(self.domain.id) @@ -590,13 +591,12 @@ def tallies(self): estimators = self.estimator # Create each Tally needed to compute the multi group cross section - tally_metadata = \ - zip(self.scores, self.tally_keys, self.filters, estimators) + tally_metadata = zip(self.scores, self.tally_keys, self.filters, estimators) for score, key, filters, estimator in tally_metadata: self._tallies[key] = openmc.Tally(name=self.name) self._tallies[key].scores = [score] self._tallies[key].estimator = estimator - if score != 'current': + if score != "current": self._tallies[key].filters = [domain_filter] # If a tally trigger was specified, add it to each tally @@ -610,10 +610,10 @@ def tallies(self): self._tallies[key].filters.append(add_filter) # If this is a by-nuclide cross-section, add nuclides to Tally - if self.by_nuclide and score != 'flux': + if self.by_nuclide and score != "flux": self._tallies[key].nuclides += self.get_nuclides() else: - self._tallies[key].nuclides.append('total') + self._tallies[key].nuclides.append("total") return self._tallies @@ -629,11 +629,13 @@ def rxn_rate_tally(self): def xs_tally(self): if self._xs_tally is None: if self.tallies is None: - msg = 'Unable to get xs_tally since tallies have ' \ - 'not been loaded from a statepoint' + msg = ( + "Unable to get xs_tally since tallies have " + "not been loaded from a statepoint" + ) raise ValueError(msg) - self._xs_tally = self.rxn_rate_tally / self.tallies['flux'] + self._xs_tally = self.rxn_rate_tally / self.tallies["flux"] self._compute_xs() return self._xs_tally @@ -654,7 +656,7 @@ def sparse(self, sparse): """ - cv.check_type('sparse', sparse, bool) + cv.check_type("sparse", sparse, bool) # Sparsify or densify the derived MGXS tallies and the base tallies if self._xs_tally: @@ -669,11 +671,11 @@ def sparse(self, sparse): @property def num_subdomains(self): - if self.domain_type.startswith('sum('): + if self.domain_type.startswith("sum("): domain_type = self.domain_type[4:-1] else: domain_type = self.domain_type - if self._rxn_type == 'current': + if self._rxn_type == "current": filter_type = openmc.MeshSurfaceFilter else: filter_type = _DOMAIN_TO_FILTER[domain_type] @@ -692,11 +694,11 @@ def nuclides(self): if self.by_nuclide: return self.get_nuclides() else: - return ['sum'] + return ["sum"] @nuclides.setter def nuclides(self, nuclides): - cv.check_iterable_type('nuclides', nuclides, str) + cv.check_iterable_type("nuclides", nuclides, str) self._nuclides = nuclides @property @@ -715,9 +717,16 @@ def mgxs_type(self): return self._rxn_type @staticmethod - def get_mgxs(mgxs_type, domain=None, domain_type=None, - energy_groups=None, by_nuclide=False, name='', num_polar=1, - num_azimuthal=1): + def get_mgxs( + mgxs_type, + domain=None, + domain_type=None, + energy_groups=None, + by_nuclide=False, + name="", + num_polar=1, + num_azimuthal=1, + ): """Return a MGXS subclass object for some energy group structure within some spatial domain for some reaction type. @@ -761,71 +770,71 @@ def get_mgxs(mgxs_type, domain=None, domain_type=None, """ cv.check_value( - "mgxs_type", mgxs_type, - MGXS_TYPES + ARBITRARY_VECTOR_TYPES + ARBITRARY_MATRIX_TYPES) + "mgxs_type", + mgxs_type, + MGXS_TYPES + ARBITRARY_VECTOR_TYPES + ARBITRARY_MATRIX_TYPES, + ) - if mgxs_type == 'total': + if mgxs_type == "total": mgxs = TotalXS(domain, domain_type, energy_groups) - elif mgxs_type == 'transport': + elif mgxs_type == "transport": mgxs = TransportXS(domain, domain_type, energy_groups) - elif mgxs_type == 'nu-transport': + elif mgxs_type == "nu-transport": mgxs = TransportXS(domain, domain_type, energy_groups, nu=True) - elif mgxs_type == 'absorption': + elif mgxs_type == "absorption": mgxs = AbsorptionXS(domain, domain_type, energy_groups) - elif mgxs_type == 'reduced absorption': + elif mgxs_type == "reduced absorption": mgxs = ReducedAbsorptionXS(domain, domain_type, energy_groups) - elif mgxs_type == 'capture': + elif mgxs_type == "capture": mgxs = CaptureXS(domain, domain_type, energy_groups) - elif mgxs_type == 'fission': + elif mgxs_type == "fission": mgxs = FissionXS(domain, domain_type, energy_groups) - elif mgxs_type == 'nu-fission': + elif mgxs_type == "nu-fission": mgxs = FissionXS(domain, domain_type, energy_groups, nu=True) - elif mgxs_type == 'kappa-fission': + elif mgxs_type == "kappa-fission": mgxs = KappaFissionXS(domain, domain_type, energy_groups) - elif mgxs_type == 'scatter': + elif mgxs_type == "scatter": mgxs = ScatterXS(domain, domain_type, energy_groups) - elif mgxs_type == 'nu-scatter': + elif mgxs_type == "nu-scatter": mgxs = ScatterXS(domain, domain_type, energy_groups, nu=True) - elif mgxs_type == 'scatter matrix': + elif mgxs_type == "scatter matrix": mgxs = ScatterMatrixXS(domain, domain_type, energy_groups) - elif mgxs_type == 'nu-scatter matrix': + elif mgxs_type == "nu-scatter matrix": mgxs = ScatterMatrixXS(domain, domain_type, energy_groups, nu=True) - elif mgxs_type == 'multiplicity matrix': + elif mgxs_type == "multiplicity matrix": mgxs = MultiplicityMatrixXS(domain, domain_type, energy_groups) - elif mgxs_type == 'scatter probability matrix': + elif mgxs_type == "scatter probability matrix": mgxs = ScatterProbabilityMatrix(domain, domain_type, energy_groups) - elif mgxs_type == 'consistent scatter matrix': + elif mgxs_type == "consistent scatter matrix": mgxs = ScatterMatrixXS(domain, domain_type, energy_groups) - mgxs.formulation = 'consistent' - elif mgxs_type == 'consistent nu-scatter matrix': + mgxs.formulation = "consistent" + elif mgxs_type == "consistent nu-scatter matrix": mgxs = ScatterMatrixXS(domain, domain_type, energy_groups, nu=True) - mgxs.formulation = 'consistent' - elif mgxs_type == 'nu-fission matrix': + mgxs.formulation = "consistent" + elif mgxs_type == "nu-fission matrix": mgxs = NuFissionMatrixXS(domain, domain_type, energy_groups) - elif mgxs_type == 'chi': + elif mgxs_type == "chi": mgxs = Chi(domain, domain_type, energy_groups) - elif mgxs_type == 'chi-prompt': + elif mgxs_type == "chi-prompt": mgxs = Chi(domain, domain_type, energy_groups, prompt=True) - elif mgxs_type == 'inverse-velocity': + elif mgxs_type == "inverse-velocity": mgxs = InverseVelocity(domain, domain_type, energy_groups) - elif mgxs_type == 'prompt-nu-fission': + elif mgxs_type == "prompt-nu-fission": mgxs = FissionXS(domain, domain_type, energy_groups, prompt=True) - elif mgxs_type == 'prompt-nu-fission matrix': - mgxs = NuFissionMatrixXS(domain, domain_type, energy_groups, - prompt=True) - elif mgxs_type == 'current': + elif mgxs_type == "prompt-nu-fission matrix": + mgxs = NuFissionMatrixXS(domain, domain_type, energy_groups, prompt=True) + elif mgxs_type == "current": mgxs = Current(domain, domain_type, energy_groups) - elif mgxs_type == 'diffusion-coefficient': + elif mgxs_type == "diffusion-coefficient": mgxs = DiffusionCoefficient(domain, domain_type, energy_groups) - elif mgxs_type == 'nu-diffusion-coefficient': + elif mgxs_type == "nu-diffusion-coefficient": mgxs = DiffusionCoefficient(domain, domain_type, energy_groups, nu=True) elif mgxs_type in ARBITRARY_VECTOR_TYPES: # Then it is a reaction not covered by the above that is # supported by the ArbitraryXS Class mgxs = ArbitraryXS(mgxs_type, domain, domain_type, energy_groups) elif mgxs_type in ARBITRARY_MATRIX_TYPES: - mgxs = ArbitraryMatrixXS(mgxs_type, domain, domain_type, - energy_groups) + mgxs = ArbitraryMatrixXS(mgxs_type, domain, domain_type, energy_groups) else: raise ValueError(f"Unknown MGXS type: {mgxs_type}") @@ -852,7 +861,7 @@ def get_nuclides(self): """ if self.domain is None: - raise ValueError('Unable to get all nuclides without a domain') + raise ValueError("Unable to get all nuclides without a domain") # If the user defined nuclides, return them if self._nuclides: @@ -878,14 +887,14 @@ def get_nuclide_density(self, nuclide): """ - cv.check_type('nuclide', nuclide, str) + cv.check_type("nuclide", nuclide, str) # Get list of all nuclides in the spatial domain nuclides = self.domain.get_nuclide_densities() return nuclides[nuclide][1] if nuclide in nuclides else 0.0 - def get_nuclide_densities(self, nuclides='all'): + def get_nuclide_densities(self, nuclides="all"): """Get an array of atomic number densities in units of atom/b-cm for all nuclides in the cross section's spatial domain. @@ -912,17 +921,17 @@ def get_nuclide_densities(self, nuclides='all'): """ if self.domain is None: - raise ValueError('Unable to get nuclide densities without a domain') + raise ValueError("Unable to get nuclide densities without a domain") # Sum the atomic number densities for all nuclides - if nuclides == 'sum': + if nuclides == "sum": nuclides = self.get_nuclides() densities = np.zeros(1, dtype=float) for nuclide in nuclides: densities[0] += self.get_nuclide_density(nuclide) # Tabulate the atomic number densities for all nuclides - elif nuclides == 'all': + elif nuclides == "all": nuclides = self.get_nuclides() densities = np.zeros(self.num_nuclides, dtype=float) for i, nuclide in enumerate(nuclides): @@ -985,36 +994,40 @@ def load_from_statepoint(self, statepoint): """ - cv.check_type('statepoint', statepoint, openmc.StatePoint) + cv.check_type("statepoint", statepoint, openmc.StatePoint) if statepoint.summary is None: - msg = 'Unable to load data from a statepoint which has not been ' \ - 'linked with a summary file' + msg = ( + "Unable to load data from a statepoint which has not been " + "linked with a summary file" + ) raise ValueError(msg) # Override the domain object that loaded from an OpenMC summary file # NOTE: This is necessary for micro cross-sections which require # the isotopic number densities as computed by OpenMC su = statepoint.summary - if self.domain_type in ('cell', 'distribcell'): + if self.domain_type in ("cell", "distribcell"): self.domain = su._fast_cells[self.domain.id] - elif self.domain_type == 'universe': + elif self.domain_type == "universe": self.domain = su._fast_universes[self.domain.id] - elif self.domain_type == 'material': + elif self.domain_type == "material": self.domain = su._fast_materials[self.domain.id] - elif self.domain_type == 'mesh': + elif self.domain_type == "mesh": self.domain = statepoint.meshes[self.domain.id] else: - msg = 'Unable to load data from a statepoint for domain type {0} ' \ - 'which is not yet supported'.format(self.domain_type) + msg = ( + "Unable to load data from a statepoint for domain type {0} " + "which is not yet supported".format(self.domain_type) + ) raise ValueError(msg) # Use tally "slicing" to ensure that tallies correspond to our domain # NOTE: This is important if tally merging was used - if self.domain_type == 'mesh': + if self.domain_type == "mesh": filters = [_DOMAIN_TO_FILTER[self.domain_type]] filter_bins = [tuple(self.domain.indices)] - elif self.domain_type != 'distribcell': + elif self.domain_type != "distribcell": filters = [_DOMAIN_TO_FILTER[self.domain_type]] filter_bins = [(self.domain.id,)] # Distribcell filters only accept single cell - neglect it when slicing @@ -1033,18 +1046,31 @@ def load_from_statepoint(self, statepoint): # The tally slicing is needed if tally merging was used for tally_type, tally in self.tallies.items(): sp_tally = statepoint.get_tally( - tally.scores, tally.filters, tally.nuclides, - estimator=tally.estimator, exact_filters=True) + tally.scores, + tally.filters, + tally.nuclides, + estimator=tally.estimator, + exact_filters=True, + ) sp_tally = sp_tally.get_slice( - tally.scores, filters, filter_bins, tally.nuclides) + tally.scores, filters, filter_bins, tally.nuclides + ) sp_tally.sparse = self.sparse self.tallies[tally_type] = sp_tally self._loaded_sp = True - def get_xs(self, groups='all', subdomains='all', nuclides='all', - xs_type='macro', order_groups='increasing', - value='mean', squeeze=True, **kwargs): + def get_xs( + self, + groups="all", + subdomains="all", + nuclides="all", + xs_type="macro", + order_groups="increasing", + value="mean", + squeeze=True, + **kwargs, + ): r"""Returns an array of multi-group cross sections. This method constructs a 3D NumPy array for the requested @@ -1090,14 +1116,16 @@ def get_xs(self, groups='all', subdomains='all', nuclides='all', """ - cv.check_value('value', value, ['mean', 'std_dev', 'rel_err']) - cv.check_value('xs_type', xs_type, ['macro', 'micro']) + cv.check_value("value", value, ["mean", "std_dev", "rel_err"]) + cv.check_value("xs_type", xs_type, ["macro", "micro"]) # FIXME: Unable to get microscopic xs for mesh domain because the mesh # cells do not know the nuclide densities in each mesh cell. - if self.domain_type == 'mesh' and xs_type == 'micro': - msg = 'Unable to get micro xs for mesh domain since the mesh ' \ - 'cells do not know the nuclide densities in each mesh cell.' + if self.domain_type == "mesh" and xs_type == "micro": + msg = ( + "Unable to get micro xs for mesh domain since the mesh " + "cells do not know the nuclide densities in each mesh cell." + ) raise ValueError(msg) filters = [] @@ -1105,8 +1133,7 @@ def get_xs(self, groups='all', subdomains='all', nuclides='all', # Construct a collection of the domain filter bins if not isinstance(subdomains, str): - cv.check_iterable_type('subdomains', subdomains, Integral, - max_depth=3) + cv.check_iterable_type("subdomains", subdomains, Integral, max_depth=3) filters.append(_DOMAIN_TO_FILTER[self.domain_type]) subdomain_bins = [] @@ -1116,58 +1143,61 @@ def get_xs(self, groups='all', subdomains='all', nuclides='all', # Construct list of energy group bounds tuples for all requested groups if not isinstance(groups, str): - cv.check_iterable_type('groups', groups, Integral) + cv.check_iterable_type("groups", groups, Integral) filters.append(openmc.EnergyFilter) energy_bins = [] for group in groups: - energy_bins.append( - (self.energy_groups.get_group_bounds(group),)) + energy_bins.append((self.energy_groups.get_group_bounds(group),)) filter_bins.append(tuple(energy_bins)) # Construct a collection of the nuclides to retrieve from the xs tally if self.by_nuclide: - if nuclides == 'all' or nuclides == 'sum' or nuclides == ['sum']: + if nuclides == "all" or nuclides == "sum" or nuclides == ["sum"]: query_nuclides = self.get_nuclides() else: query_nuclides = nuclides else: - query_nuclides = ['total'] + query_nuclides = ["total"] # If user requested the sum for all nuclides, use tally summation - if nuclides == 'sum' or nuclides == ['sum']: + if nuclides == "sum" or nuclides == ["sum"]: xs_tally = self.xs_tally.summation(nuclides=query_nuclides) - xs = xs_tally.get_values(filters=filters, - filter_bins=filter_bins, value=value) + xs = xs_tally.get_values( + filters=filters, filter_bins=filter_bins, value=value + ) else: - xs = self.xs_tally.get_values(filters=filters, - filter_bins=filter_bins, - nuclides=query_nuclides, value=value) + xs = self.xs_tally.get_values( + filters=filters, + filter_bins=filter_bins, + nuclides=query_nuclides, + value=value, + ) # Divide by atom number densities for microscopic cross sections - if xs_type == 'micro' and self._divide_by_density: + if xs_type == "micro" and self._divide_by_density: if self.by_nuclide: densities = self.get_nuclide_densities(nuclides) else: - densities = self.get_nuclide_densities('sum') - if value == 'mean' or value == 'std_dev': + densities = self.get_nuclide_densities("sum") + if value == "mean" or value == "std_dev": xs /= densities[np.newaxis, :, np.newaxis] # Eliminate the trivial score dimension xs = np.squeeze(xs, axis=len(xs.shape) - 1) xs = np.nan_to_num(xs) - if groups == 'all': + if groups == "all": num_groups = self.num_groups else: num_groups = len(groups) # Reshape tally data array with separate axes for domain and energy # Accomodate the polar and azimuthal bins if needed - num_subdomains = int(xs.shape[0] / (num_groups * self.num_polar * - self.num_azimuthal)) + num_subdomains = int( + xs.shape[0] / (num_groups * self.num_polar * self.num_azimuthal) + ) if self.num_polar > 1 or self.num_azimuthal > 1: - new_shape = (self.num_polar, self.num_azimuthal, num_subdomains, - num_groups) + new_shape = (self.num_polar, self.num_azimuthal, num_subdomains, num_groups) else: new_shape = (num_subdomains, num_groups) new_shape += xs.shape[1:] @@ -1175,7 +1205,7 @@ def get_xs(self, groups='all', subdomains='all', nuclides='all', # Reverse data if user requested increasing energy groups since # tally data is stored in order of increasing energies - if order_groups == 'increasing': + if order_groups == "increasing": xs = xs[..., ::-1, :] if squeeze: @@ -1185,9 +1215,15 @@ def get_xs(self, groups='all', subdomains='all', nuclides='all', return xs - def get_flux(self, groups='all', subdomains='all', - order_groups='increasing', value='mean', - squeeze=True, **kwargs): + def get_flux( + self, + groups="all", + subdomains="all", + order_groups="increasing", + value="mean", + squeeze=True, + **kwargs, + ): r"""Returns an array of the fluxes used to weight the MGXS. This method constructs a 2D NumPy array for the requested @@ -1224,15 +1260,14 @@ def get_flux(self, groups='all', subdomains='all', """ - cv.check_value('value', value, ['mean', 'std_dev', 'rel_err']) + cv.check_value("value", value, ["mean", "std_dev", "rel_err"]) filters = [] filter_bins = [] # Construct a collection of the domain filter bins if not isinstance(subdomains, str): - cv.check_iterable_type('subdomains', subdomains, Integral, - max_depth=3) + cv.check_iterable_type("subdomains", subdomains, Integral, max_depth=3) filters.append(_DOMAIN_TO_FILTER[self.domain_type]) subdomain_bins = [] @@ -1242,17 +1277,16 @@ def get_flux(self, groups='all', subdomains='all', # Construct list of energy group bounds tuples for all requested groups if not isinstance(groups, str): - cv.check_iterable_type('groups', groups, Integral) + cv.check_iterable_type("groups", groups, Integral) filters.append(openmc.EnergyFilter) energy_bins = [] for group in groups: - energy_bins.append( - (self.energy_groups.get_group_bounds(group),)) + energy_bins.append((self.energy_groups.get_group_bounds(group),)) filter_bins.append(tuple(energy_bins)) # Determine which flux to obtain # Step through in order of usefulness - for key in ['flux', 'flux (tracklength)', 'flux (analog)']: + for key in ["flux", "flux (tracklength)", "flux (analog)"]: if key in self.tally_keys: tally = self.tallies[key] break @@ -1260,8 +1294,9 @@ def get_flux(self, groups='all', subdomains='all', msg = "MGXS of Type {} do not have an explicit weighting flux!" raise ValueError(msg.format(self.__name__)) - flux = tally.get_values(filters=filters, filter_bins=filter_bins, - nuclides=['total'], value=value) + flux = tally.get_values( + filters=filters, filter_bins=filter_bins, nuclides=["total"], value=value + ) # Eliminate the trivial score dimension flux = np.squeeze(flux, axis=len(flux.shape) - 1) @@ -1269,18 +1304,18 @@ def get_flux(self, groups='all', subdomains='all', flux = np.squeeze(flux, axis=len(flux.shape) - 1) flux = np.nan_to_num(flux) - if groups == 'all': + if groups == "all": num_groups = self.num_groups else: num_groups = len(groups) # Reshape tally data array with separate axes for domain and energy # Accomodate the polar and azimuthal bins if needed - num_subdomains = int(flux.shape[0] / (num_groups * self.num_polar * - self.num_azimuthal)) + num_subdomains = int( + flux.shape[0] / (num_groups * self.num_polar * self.num_azimuthal) + ) if self.num_polar > 1 or self.num_azimuthal > 1: - new_shape = (self.num_polar, self.num_azimuthal, num_subdomains, - num_groups) + new_shape = (self.num_polar, self.num_azimuthal, num_subdomains, num_groups) else: new_shape = (num_subdomains, num_groups) new_shape += flux.shape[1:] @@ -1288,7 +1323,7 @@ def get_flux(self, groups='all', subdomains='all', # Reverse data if user requested increasing energy groups since # tally data is stored in order of increasing energies - if order_groups == 'increasing': + if order_groups == "increasing": flux = flux[..., ::-1] if squeeze: @@ -1313,13 +1348,20 @@ def get_condensed_xs(self, coarse_groups): """ - cv.check_type('coarse_groups', coarse_groups, EnergyGroups) - cv.check_less_than('coarse groups', coarse_groups.num_groups, - self.num_groups, equality=True) - cv.check_value('upper coarse energy', coarse_groups.group_edges[-1], - [self.energy_groups.group_edges[-1]]) - cv.check_value('lower coarse energy', coarse_groups.group_edges[0], - [self.energy_groups.group_edges[0]]) + cv.check_type("coarse_groups", coarse_groups, EnergyGroups) + cv.check_less_than( + "coarse groups", coarse_groups.num_groups, self.num_groups, equality=True + ) + cv.check_value( + "upper coarse energy", + coarse_groups.group_edges[-1], + [self.energy_groups.group_edges[-1]], + ) + cv.check_value( + "lower coarse energy", + coarse_groups.group_edges[0], + [self.energy_groups.group_edges[0]], + ) # Clone this MGXS to initialize the condensed version condensed_xs = copy.deepcopy(self) @@ -1346,13 +1388,14 @@ def get_condensed_xs(self, coarse_groups): tally._sum_sq = None # Get tally data arrays reshaped with one dimension per filter - mean = tally.get_reshaped_data(value='mean') - std_dev = tally.get_reshaped_data(value='std_dev') + mean = tally.get_reshaped_data(value="mean") + std_dev = tally.get_reshaped_data(value="std_dev") # Sum across all applicable fine energy group filters for i, tally_filter in enumerate(tally.filters): - if not isinstance(tally_filter, (openmc.EnergyFilter, - openmc.EnergyoutFilter)): + if not isinstance( + tally_filter, (openmc.EnergyFilter, openmc.EnergyoutFilter) + ): continue elif len(tally_filter.bins) != len(fine_edges) - 1: continue @@ -1363,8 +1406,7 @@ def get_condensed_xs(self, coarse_groups): tally_filter.values = cedge tally_filter.bins = np.vstack((cedge[:-1], cedge[1:])).T mean = np.add.reduceat(mean, energy_indices, axis=i) - std_dev = np.add.reduceat(std_dev**2, energy_indices, - axis=i) + std_dev = np.add.reduceat(std_dev**2, energy_indices, axis=i) std_dev = np.sqrt(std_dev) # Reshape condensed data arrays with one dimension for all filters @@ -1379,7 +1421,7 @@ def get_condensed_xs(self, coarse_groups): condensed_xs.sparse = self.sparse return condensed_xs - def get_subdomain_avg_xs(self, subdomains='all'): + def get_subdomain_avg_xs(self, subdomains="all"): """Construct a subdomain-averaged version of this cross section. This method is useful for averaging cross sections across distribcell @@ -1406,10 +1448,10 @@ def get_subdomain_avg_xs(self, subdomains='all'): # Construct a collection of the subdomain filter bins to average across if not isinstance(subdomains, str): - cv.check_iterable_type('subdomains', subdomains, Integral) + cv.check_iterable_type("subdomains", subdomains, Integral) subdomains = [(subdomain,) for subdomain in subdomains] subdomains = [tuple(subdomains)] - elif self.domain_type == 'distribcell': + elif self.domain_type == "distribcell": subdomains = [i for i in range(self.num_subdomains)] subdomains = [tuple(subdomains)] else: @@ -1423,15 +1465,14 @@ def get_subdomain_avg_xs(self, subdomains='all'): # Average each of the tallies across subdomains for tally_type, tally in avg_xs.tallies.items(): filt_type = _DOMAIN_TO_FILTER[self.domain_type] - tally_avg = tally.summation(filter_type=filt_type, - filter_bins=subdomains) + tally_avg = tally.summation(filter_type=filt_type, filter_bins=subdomains) avg_xs.tallies[tally_type] = tally_avg - avg_xs._domain_type = f'sum({self.domain_type})' + avg_xs._domain_type = f"sum({self.domain_type})" avg_xs.sparse = self.sparse return avg_xs - def _get_homogenized_mgxs(self, other_mgxs, denom_score='flux'): + def _get_homogenized_mgxs(self, other_mgxs, denom_score="flux"): """Construct a homogenized MGXS with other MGXS objects. This method constructs a new MGXS object that is the flux-weighted @@ -1462,23 +1503,23 @@ def _get_homogenized_mgxs(self, other_mgxs, denom_score='flux'): """ # Check type of denom score - cv.check_type('denom_score', denom_score, str) + cv.check_type("denom_score", denom_score, str) # Construct a collection of the subdomain filter bins to homogenize # across if isinstance(other_mgxs, openmc.mgxs.MGXS): other_mgxs = [other_mgxs] - cv.check_iterable_type('other_mgxs', other_mgxs, openmc.mgxs.MGXS) + cv.check_iterable_type("other_mgxs", other_mgxs, openmc.mgxs.MGXS) for mgxs in other_mgxs: if mgxs.rxn_type != self.rxn_type: - msg = 'Not able to homogenize two MGXS with different rxn types' + msg = "Not able to homogenize two MGXS with different rxn types" raise ValueError(msg) # Clone this MGXS to initialize the homogenized version homogenized_mgxs = copy.deepcopy(self) homogenized_mgxs._derived = True - name = f'hom({self.domain.name}, ' + name = f"hom({self.domain.name}, " # Get the domain filter filter_type = _DOMAIN_TO_FILTER[self.domain_type] @@ -1505,12 +1546,12 @@ def _get_homogenized_mgxs(self, other_mgxs, denom_score='flux'): denom_tally += other_denom_tally # Update the name for the homogenzied MGXS - name += f'{mgxs.domain.name}, ' + name += f"{mgxs.domain.name}, " # Set the properties of the homogenized MGXS homogenized_mgxs._rxn_rate_tally = rxn_rate_tally homogenized_mgxs.tallies[denom_score] = denom_tally - homogenized_mgxs._domain.name = name[:-2] + ')' + homogenized_mgxs._domain.name = name[:-2] + ")" return homogenized_mgxs @@ -1534,7 +1575,7 @@ def get_homogenized_mgxs(self, other_mgxs): """ - return self._get_homogenized_mgxs(other_mgxs, 'flux') + return self._get_homogenized_mgxs(other_mgxs, "flux") def get_slice(self, nuclides=[], groups=[]): """Build a sliced MGXS for the specified nuclides and energy groups. @@ -1562,8 +1603,8 @@ def get_slice(self, nuclides=[], groups=[]): """ - cv.check_iterable_type('nuclides', nuclides, str) - cv.check_iterable_type('energy_groups', groups, Integral) + cv.check_iterable_type("nuclides", nuclides, str) + cv.check_iterable_type("energy_groups", groups, Integral) # Build lists of filters and filter bins to slice filters = [] @@ -1586,9 +1627,9 @@ def get_slice(self, nuclides=[], groups=[]): for tally_type, tally in slice_xs.tallies.items(): slice_nuclides = [nuc for nuc in nuclides if nuc in tally.nuclides] if len(groups) != 0 and tally.contains_filter(openmc.EnergyFilter): - tally_slice = tally.get_slice(filters=filters, - filter_bins=filter_bins, - nuclides=slice_nuclides) + tally_slice = tally.get_slice( + filters=filters, filter_bins=filter_bins, nuclides=slice_nuclides + ) else: tally_slice = tally.get_slice(nuclides=slice_nuclides) slice_xs.tallies[tally_type] = tally_slice @@ -1634,7 +1675,7 @@ def can_merge(self, other): return False elif self.domain_type != other.domain_type: return False - elif 'distribcell' not in self.domain_type and self.domain != other.domain: + elif "distribcell" not in self.domain_type and self.domain != other.domain: return False elif not self.xs_tally.can_merge(other.xs_tally): return False @@ -1665,7 +1706,7 @@ def merge(self, other): """ if not self.can_merge(other): - raise ValueError('Unable to merge MGXS') + raise ValueError("Unable to merge MGXS") # Create deep copy of tally to return as merged tally merged_mgxs = copy.deepcopy(self) @@ -1682,20 +1723,20 @@ def merge(self, other): # The nuclides must be mutually exclusive for nuclide in self.nuclides: if nuclide in other.nuclides: - msg = 'Unable to merge MGXS with shared nuclides' + msg = "Unable to merge MGXS with shared nuclides" raise ValueError(msg) # Concatenate lists of nuclides for the merged MGXS merged_mgxs.nuclides = self.nuclides + other.nuclides # Null base tallies but merge reaction rate and cross section tallies - merged_mgxs._tallies ={} + merged_mgxs._tallies = {} merged_mgxs._rxn_rate_tally = self.rxn_rate_tally.merge(other.rxn_rate_tally) merged_mgxs._xs_tally = self.xs_tally.merge(other.xs_tally) return merged_mgxs - def print_xs(self, subdomains='all', nuclides='all', xs_type='macro'): + def print_xs(self, subdomains="all", nuclides="all", xs_type="macro"): """Print a string representation for the multi-group cross section. Parameters @@ -1717,35 +1758,35 @@ def print_xs(self, subdomains='all', nuclides='all', xs_type='macro'): # Construct a collection of the subdomains to report if not isinstance(subdomains, str): - cv.check_iterable_type('subdomains', subdomains, Integral) - elif self.domain_type == 'distribcell': + cv.check_iterable_type("subdomains", subdomains, Integral) + elif self.domain_type == "distribcell": subdomains = np.arange(self.num_subdomains, dtype=int) - elif self.domain_type == 'mesh': + elif self.domain_type == "mesh": subdomains = list(self.domain.indices) else: subdomains = [self.domain.id] # Construct a collection of the nuclides to report if self.by_nuclide: - if nuclides == 'all': + if nuclides == "all": nuclides = self.get_nuclides() - elif nuclides == 'sum': - nuclides = ['sum'] + elif nuclides == "sum": + nuclides = ["sum"] else: - cv.check_iterable_type('nuclides', nuclides, str) + cv.check_iterable_type("nuclides", nuclides, str) else: - nuclides = ['sum'] + nuclides = ["sum"] - cv.check_value('xs_type', xs_type, ['macro', 'micro']) + cv.check_value("xs_type", xs_type, ["macro", "micro"]) # Build header for string with type and domain info - string = 'Multi-Group XS\n' - string += '{0: <16}=\t{1}\n'.format('\tReaction Type', self.mgxs_type) - string += '{0: <16}=\t{1}\n'.format('\tDomain Type', self.domain_type) - string += '{0: <16}=\t{1}\n'.format('\tDomain ID', self.domain.id) + string = "Multi-Group XS\n" + string += "{0: <16}=\t{1}\n".format("\tReaction Type", self.mgxs_type) + string += "{0: <16}=\t{1}\n".format("\tDomain Type", self.domain_type) + string += "{0: <16}=\t{1}\n".format("\tDomain ID", self.domain.id) # Generate the header for an individual XS - xs_header = f'\tCross Sections [{self.get_units(xs_type)}]:' + xs_header = f"\tCross Sections [{self.get_units(xs_type)}]:" # If cross section data has not been computed, only print string header if self.tallies is None: @@ -1754,76 +1795,94 @@ def print_xs(self, subdomains='all', nuclides='all', xs_type='macro'): # Set polar/azimuthal bins if self.num_polar > 1 or self.num_azimuthal > 1: - pol_bins = np.linspace(0., np.pi, num=self.num_polar + 1, - endpoint=True) - azi_bins = np.linspace(-np.pi, np.pi, num=self.num_azimuthal + 1, - endpoint=True) + pol_bins = np.linspace(0.0, np.pi, num=self.num_polar + 1, endpoint=True) + azi_bins = np.linspace( + -np.pi, np.pi, num=self.num_azimuthal + 1, endpoint=True + ) # Loop over all subdomains for subdomain in subdomains: - if self.domain_type == 'distribcell' or self.domain_type == 'mesh': - string += '{0: <16}=\t{1}\n'.format('\tSubdomain', subdomain) + if self.domain_type == "distribcell" or self.domain_type == "mesh": + string += "{0: <16}=\t{1}\n".format("\tSubdomain", subdomain) # Loop over all Nuclides for nuclide in nuclides: # Build header for nuclide type - if nuclide != 'sum': - string += '{0: <16}=\t{1}\n'.format('\tNuclide', nuclide) + if nuclide != "sum": + string += "{0: <16}=\t{1}\n".format("\tNuclide", nuclide) # Build header for cross section type - string += f'{xs_header: <16}\n' - template = '{0: <12}Group {1} [{2: <10} - {3: <10}eV]:\t' - - average_xs = self.get_xs(nuclides=[nuclide], - subdomains=[subdomain], - xs_type=xs_type, value='mean') - rel_err_xs = self.get_xs(nuclides=[nuclide], - subdomains=[subdomain], - xs_type=xs_type, value='rel_err') - rel_err_xs = rel_err_xs * 100. + string += f"{xs_header: <16}\n" + template = "{0: <12}Group {1} [{2: <10} - {3: <10}eV]:\t" + + average_xs = self.get_xs( + nuclides=[nuclide], + subdomains=[subdomain], + xs_type=xs_type, + value="mean", + ) + rel_err_xs = self.get_xs( + nuclides=[nuclide], + subdomains=[subdomain], + xs_type=xs_type, + value="rel_err", + ) + rel_err_xs = rel_err_xs * 100.0 if self.num_polar > 1 or self.num_azimuthal > 1: # Loop over polar, azimuthal, and energy group ranges for pol in range(len(pol_bins) - 1): - pol_low, pol_high = pol_bins[pol: pol + 2] + pol_low, pol_high = pol_bins[pol : pol + 2] for azi in range(len(azi_bins) - 1): - azi_low, azi_high = azi_bins[azi: azi + 2] - string += '\t\tPolar Angle: [{0:5f} - {1:5f}]'.format( - pol_low, pol_high) + \ - '\tAzimuthal Angle: [{0:5f} - {1:5f}]'.format( - azi_low, azi_high) + '\n' + azi_low, azi_high = azi_bins[azi : azi + 2] + string += ( + "\t\tPolar Angle: [{0:5f} - {1:5f}]".format( + pol_low, pol_high + ) + + "\tAzimuthal Angle: [{0:5f} - {1:5f}]".format( + azi_low, azi_high + ) + + "\n" + ) for group in range(1, self.num_groups + 1): - bounds = \ - self.energy_groups.get_group_bounds(group) - string += '\t' + template.format('', group, - bounds[0], - bounds[1]) + bounds = self.energy_groups.get_group_bounds(group) + string += "\t" + template.format( + "", group, bounds[0], bounds[1] + ) - string += '{0:.2e} +/- {1:.2e}%'.format( + string += "{0:.2e} +/- {1:.2e}%".format( average_xs[pol, azi, group - 1], - rel_err_xs[pol, azi, group - 1]) - string += '\n' - string += '\n' + rel_err_xs[pol, azi, group - 1], + ) + string += "\n" + string += "\n" else: # Loop over energy groups for group in range(1, self.num_groups + 1): bounds = self.energy_groups.get_group_bounds(group) - string += template.format('', group, bounds[0], - bounds[1]) - string += '{0:.2e} +/- {1:.2e}%'.format( - average_xs[group - 1], rel_err_xs[group - 1]) - string += '\n' - string += '\n' - string += '\n' + string += template.format("", group, bounds[0], bounds[1]) + string += "{0:.2e} +/- {1:.2e}%".format( + average_xs[group - 1], rel_err_xs[group - 1] + ) + string += "\n" + string += "\n" + string += "\n" print(string) - def build_hdf5_store(self, filename='mgxs.h5', directory='mgxs', - subdomains='all', nuclides='all', - xs_type='macro', row_column='inout', append=True, - libver='earliest'): + def build_hdf5_store( + self, + filename="mgxs.h5", + directory="mgxs", + subdomains="all", + nuclides="all", + xs_type="macro", + row_column="inout", + append=True, + libver="earliest", + ): """Export the multi-group cross section data to an HDF5 binary file. This method constructs an HDF5 file which stores the multi-group @@ -1875,39 +1934,39 @@ def build_hdf5_store(self, filename='mgxs.h5', directory='mgxs', os.makedirs(directory) filename = os.path.join(directory, filename) - filename = filename.replace(' ', '-') + filename = filename.replace(" ", "-") if append and os.path.isfile(filename): - xs_results = h5py.File(filename, 'a') + xs_results = h5py.File(filename, "a") else: - xs_results = h5py.File(filename, 'w', libver=libver) + xs_results = h5py.File(filename, "w", libver=libver) # Construct a collection of the subdomains to report if not isinstance(subdomains, str): - cv.check_iterable_type('subdomains', subdomains, Integral) - elif self.domain_type == 'distribcell': + cv.check_iterable_type("subdomains", subdomains, Integral) + elif self.domain_type == "distribcell": subdomains = np.arange(self.num_subdomains, dtype=int) - elif self.domain_type == 'sum(distribcell)': - domain_filter = self.xs_tally.find_filter('sum(distribcell)') + elif self.domain_type == "sum(distribcell)": + domain_filter = self.xs_tally.find_filter("sum(distribcell)") subdomains = domain_filter.bins - elif self.domain_type == 'mesh': + elif self.domain_type == "mesh": subdomains = list(self.domain.indices) else: subdomains = [self.domain.id] # Construct a collection of the nuclides to report if self.by_nuclide: - if nuclides == 'all': + if nuclides == "all": nuclides = self.get_nuclides() densities = np.zeros(len(nuclides), dtype=float) - elif nuclides == 'sum': - nuclides = ['sum'] + elif nuclides == "sum": + nuclides = ["sum"] else: - cv.check_iterable_type('nuclides', nuclides, str) + cv.check_iterable_type("nuclides", nuclides, str) else: - nuclides = ['sum'] + nuclides = ["sum"] - cv.check_value('xs_type', xs_type, ['macro', 'micro']) + cv.check_value("xs_type", xs_type, ["macro", "micro"]) # Create an HDF5 group within the file for the domain domain_type_group = xs_results.require_group(self.domain_type) @@ -1920,8 +1979,8 @@ def build_hdf5_store(self, filename='mgxs.h5', directory='mgxs', for subdomain in subdomains: # Create an HDF5 group for the subdomain - if self.domain_type == 'distribcell': - group_name = ''.zfill(num_digits) + if self.domain_type == "distribcell": + group_name = "".zfill(num_digits) subdomain_group = domain_group.require_group(group_name) else: subdomain_group = domain_group @@ -1932,33 +1991,50 @@ def build_hdf5_store(self, filename='mgxs.h5', directory='mgxs', # Create a separate HDF5 group for each nuclide for j, nuclide in enumerate(nuclides): - if nuclide != 'sum': + if nuclide != "sum": density = densities[j] nuclide_group = rxn_group.require_group(nuclide) - nuclide_group.require_dataset('density', dtype=np.float64, - data=[density], shape=(1,)) + nuclide_group.require_dataset( + "density", dtype=np.float64, data=[density], shape=(1,) + ) else: nuclide_group = rxn_group # Extract the cross section for this subdomain and nuclide - average = self.get_xs(subdomains=[subdomain], nuclides=[nuclide], - xs_type=xs_type, value='mean', - row_column=row_column) - std_dev = self.get_xs(subdomains=[subdomain], nuclides=[nuclide], - xs_type=xs_type, value='std_dev', - row_column=row_column) + average = self.get_xs( + subdomains=[subdomain], + nuclides=[nuclide], + xs_type=xs_type, + value="mean", + row_column=row_column, + ) + std_dev = self.get_xs( + subdomains=[subdomain], + nuclides=[nuclide], + xs_type=xs_type, + value="std_dev", + row_column=row_column, + ) # Add MGXS results data to the HDF5 group - nuclide_group.require_dataset('average', dtype=np.float64, - shape=average.shape, data=average) - nuclide_group.require_dataset('std. dev.', dtype=np.float64, - shape=std_dev.shape, data=std_dev) + nuclide_group.require_dataset( + "average", dtype=np.float64, shape=average.shape, data=average + ) + nuclide_group.require_dataset( + "std. dev.", dtype=np.float64, shape=std_dev.shape, data=std_dev + ) # Close the results HDF5 file xs_results.close() - def export_xs_data(self, filename='mgxs', directory='mgxs', - format='csv', groups='all', xs_type='macro'): + def export_xs_data( + self, + filename="mgxs", + directory="mgxs", + format="csv", + groups="all", + xs_type="macro", + ): """Export the multi-group cross section data to a file. This method leverages the functionality in the Pandas library to export @@ -1981,55 +2057,58 @@ def export_xs_data(self, filename='mgxs', directory='mgxs', """ - cv.check_type('filename', filename, str) - cv.check_type('directory', directory, str) - cv.check_value('format', format, ['csv', 'excel', 'pickle', 'latex']) - cv.check_value('xs_type', xs_type, ['macro', 'micro']) + cv.check_type("filename", filename, str) + cv.check_type("directory", directory, str) + cv.check_value("format", format, ["csv", "excel", "pickle", "latex"]) + cv.check_value("xs_type", xs_type, ["macro", "micro"]) # Make directory if it does not exist if not os.path.exists(directory): os.makedirs(directory) filename = os.path.join(directory, filename) - filename = filename.replace(' ', '-') + filename = filename.replace(" ", "-") # Get a Pandas DataFrame for the data df = self.get_pandas_dataframe(groups=groups, xs_type=xs_type) # Export the data using Pandas IO API - if format == 'csv': - df.to_csv(filename + '.csv', index=False) - elif format == 'excel': - if self.domain_type == 'mesh': - df.to_excel(filename + '.xlsx') + if format == "csv": + df.to_csv(filename + ".csv", index=False) + elif format == "excel": + if self.domain_type == "mesh": + df.to_excel(filename + ".xlsx") else: - df.to_excel(filename + '.xlsx', index=False) - elif format == 'pickle': - df.to_pickle(filename + '.pkl') - elif format == 'latex': - if self.domain_type == 'distribcell': - msg = 'Unable to export distribcell multi-group cross section' \ - 'data to a LaTeX table' + df.to_excel(filename + ".xlsx", index=False) + elif format == "pickle": + df.to_pickle(filename + ".pkl") + elif format == "latex": + if self.domain_type == "distribcell": + msg = ( + "Unable to export distribcell multi-group cross section" + "data to a LaTeX table" + ) raise NotImplementedError(msg) - df.to_latex(filename + '.tex', bold_rows=True, - longtable=True, index=False) + df.to_latex(filename + ".tex", bold_rows=True, longtable=True, index=False) # Surround LaTeX table with code needed to run pdflatex - with open(filename + '.tex', 'r') as original: + with open(filename + ".tex", "r") as original: data = original.read() - with open(filename + '.tex', 'w') as modified: + with open(filename + ".tex", "w") as modified: modified.write( - '\\documentclass[preview, 12pt, border=1mm]{standalone}\n') - modified.write('\\usepackage{caption}\n') - modified.write('\\usepackage{longtable}\n') - modified.write('\\usepackage{booktabs}\n') - modified.write('\\begin{document}\n\n') + "\\documentclass[preview, 12pt, border=1mm]{standalone}\n" + ) + modified.write("\\usepackage{caption}\n") + modified.write("\\usepackage{longtable}\n") + modified.write("\\usepackage{booktabs}\n") + modified.write("\\begin{document}\n\n") modified.write(data) - modified.write('\n\\end{document}') + modified.write("\n\\end{document}") - def get_pandas_dataframe(self, groups='all', nuclides='all', - xs_type='macro', paths=True): + def get_pandas_dataframe( + self, groups="all", nuclides="all", xs_type="macro", paths=True + ): """Build a Pandas DataFrame for the MGXS data. This method leverages :meth:`openmc.Tally.get_pandas_dataframe`, but @@ -2069,26 +2148,26 @@ def get_pandas_dataframe(self, groups='all', nuclides='all', """ if not isinstance(groups, str): - cv.check_iterable_type('groups', groups, Integral) - if nuclides != 'all' and nuclides != 'sum': - cv.check_iterable_type('nuclides', nuclides, str) - cv.check_value('xs_type', xs_type, ['macro', 'micro']) + cv.check_iterable_type("groups", groups, Integral) + if nuclides != "all" and nuclides != "sum": + cv.check_iterable_type("nuclides", nuclides, str) + cv.check_value("xs_type", xs_type, ["macro", "micro"]) # Get a Pandas DataFrame from the derived xs tally - if self.by_nuclide and nuclides == 'sum': + if self.by_nuclide and nuclides == "sum": # Use tally summation to sum across all nuclides xs_tally = self.xs_tally.summation(nuclides=self.get_nuclides()) df = xs_tally.get_pandas_dataframe(paths=paths) # Remove nuclide column since it is homogeneous and redundant - if self.domain_type == 'mesh': - df.drop('sum(nuclide)', axis=1, level=0, inplace=True) + if self.domain_type == "mesh": + df.drop("sum(nuclide)", axis=1, level=0, inplace=True) else: - df.drop('sum(nuclide)', axis=1, inplace=True) + df.drop("sum(nuclide)", axis=1, inplace=True) # If the user requested a specific set of nuclides - elif self.by_nuclide and nuclides != 'all': + elif self.by_nuclide and nuclides != "all": xs_tally = self.xs_tally.get_slice(nuclides=nuclides) df = xs_tally.get_pandas_dataframe(paths=paths) @@ -2097,10 +2176,10 @@ def get_pandas_dataframe(self, groups='all', nuclides='all', df = self.xs_tally.get_pandas_dataframe(paths=paths) # Remove the score column since it is homogeneous and redundant - if self.domain_type == 'mesh': - df = df.drop('score', axis=1, level=0) + if self.domain_type == "mesh": + df = df.drop("score", axis=1, level=0) else: - df = df.drop('score', axis=1) + df = df.drop("score", axis=1) # Convert azimuthal, polar, energy in and energy out bin values in to # bin indices @@ -2108,38 +2187,40 @@ def get_pandas_dataframe(self, groups='all', nuclides='all', # Select out those groups the user requested if not isinstance(groups, str): - if 'group in' in df: - df = df[df['group in'].isin(groups)] - if 'group out' in df: - df = df[df['group out'].isin(groups)] + if "group in" in df: + df = df[df["group in"].isin(groups)] + if "group out" in df: + df = df[df["group out"].isin(groups)] # If user requested micro cross sections, divide out the atom densities - if xs_type == 'micro' and self._divide_by_density: + if xs_type == "micro" and self._divide_by_density: if self.by_nuclide: densities = self.get_nuclide_densities(nuclides) else: - densities = self.get_nuclide_densities('sum') + densities = self.get_nuclide_densities("sum") densities = np.repeat(densities, len(self.rxn_rate_tally.scores)) tile_factor = int(df.shape[0] / len(densities)) - df['mean'] /= np.tile(densities, tile_factor) - df['std. dev.'] /= np.tile(densities, tile_factor) + df["mean"] /= np.tile(densities, tile_factor) + df["std. dev."] /= np.tile(densities, tile_factor) # Replace NaNs by zeros (happens if nuclide density is zero) - df['mean'].replace(np.nan, 0.0, inplace=True) - df['std. dev.'].replace(np.nan, 0.0, inplace=True) + df["mean"].replace(np.nan, 0.0, inplace=True) + df["std. dev."].replace(np.nan, 0.0, inplace=True) # Sort the dataframe by domain type id (e.g., distribcell id) and # energy groups such that data is from fast to thermal - if self.domain_type == 'mesh': - mesh_str = f'mesh {self.domain.id}' - df.sort_values(by=[(mesh_str, 'x'), (mesh_str, 'y'), - (mesh_str, 'z')] + columns, inplace=True) + if self.domain_type == "mesh": + mesh_str = f"mesh {self.domain.id}" + df.sort_values( + by=[(mesh_str, "x"), (mesh_str, "y"), (mesh_str, "z")] + columns, + inplace=True, + ) else: df.sort_values(by=[self.domain_type] + columns, inplace=True) return df - def get_units(self, xs_type='macro'): + def get_units(self, xs_type="macro"): """This method returns the units of a MGXS based on a desired xs_type. Parameters @@ -2155,9 +2236,9 @@ def get_units(self, xs_type='macro'): """ - cv.check_value('xs_type', xs_type, ['macro', 'micro']) + cv.check_value("xs_type", xs_type, ["macro", "micro"]) - return 'cm^-1' if xs_type == 'macro' else 'barns' + return "cm^-1" if xs_type == "macro" else "barns" @add_params @@ -2175,6 +2256,7 @@ class MatrixMGXS(MGXS): .. note:: Users should instantiate the subclasses of this abstract class. """ + @property def _dont_squeeze(self): """Create a tuple of axes which should not be removed during the get_xs @@ -2195,9 +2277,19 @@ def filters(self): return self._add_angle_filters(filters) - def get_xs(self, in_groups='all', out_groups='all', subdomains='all', - nuclides='all', xs_type='macro', order_groups='increasing', - row_column='inout', value='mean', squeeze=True, **kwargs): + def get_xs( + self, + in_groups="all", + out_groups="all", + subdomains="all", + nuclides="all", + xs_type="macro", + order_groups="increasing", + row_column="inout", + value="mean", + squeeze=True, + **kwargs, + ): """Returns an array of multi-group cross sections. This method constructs a 4D NumPy array for the requested @@ -2250,14 +2342,16 @@ def get_xs(self, in_groups='all', out_groups='all', subdomains='all', """ - cv.check_value('value', value, ['mean', 'std_dev', 'rel_err']) - cv.check_value('xs_type', xs_type, ['macro', 'micro']) + cv.check_value("value", value, ["mean", "std_dev", "rel_err"]) + cv.check_value("xs_type", xs_type, ["macro", "micro"]) # FIXME: Unable to get microscopic xs for mesh domain because the mesh # cells do not know the nuclide densities in each mesh cell. - if self.domain_type == 'mesh' and xs_type == 'micro': - msg = 'Unable to get micro xs for mesh domain since the mesh ' \ - 'cells do not know the nuclide densities in each mesh cell.' + if self.domain_type == "mesh" and xs_type == "micro": + msg = ( + "Unable to get micro xs for mesh domain since the mesh " + "cells do not know the nuclide densities in each mesh cell." + ) raise ValueError(msg) filters = [] @@ -2265,8 +2359,7 @@ def get_xs(self, in_groups='all', out_groups='all', subdomains='all', # Construct a collection of the domain filter bins if not isinstance(subdomains, str): - cv.check_iterable_type('subdomains', subdomains, Integral, - max_depth=3) + cv.check_iterable_type("subdomains", subdomains, Integral, max_depth=3) filters.append(_DOMAIN_TO_FILTER[self.domain_type]) subdomain_bins = [] for subdomain in subdomains: @@ -2275,7 +2368,7 @@ def get_xs(self, in_groups='all', out_groups='all', subdomains='all', # Construct list of energy group bounds tuples for all requested groups if not isinstance(in_groups, str): - cv.check_iterable_type('groups', in_groups, Integral) + cv.check_iterable_type("groups", in_groups, Integral) filters.append(openmc.EnergyFilter) energy_bins = [] for group in in_groups: @@ -2284,67 +2377,76 @@ def get_xs(self, in_groups='all', out_groups='all', subdomains='all', # Construct list of energy group bounds tuples for all requested groups if not isinstance(out_groups, str): - cv.check_iterable_type('groups', out_groups, Integral) + cv.check_iterable_type("groups", out_groups, Integral) for group in out_groups: filters.append(openmc.EnergyoutFilter) - filter_bins.append(( - self.energy_groups.get_group_bounds(group),)) + filter_bins.append((self.energy_groups.get_group_bounds(group),)) # Construct a collection of the nuclides to retrieve from the xs tally if self.by_nuclide: - if nuclides == 'all' or nuclides == 'sum' or nuclides == ['sum']: + if nuclides == "all" or nuclides == "sum" or nuclides == ["sum"]: query_nuclides = self.get_nuclides() else: query_nuclides = nuclides else: - query_nuclides = ['total'] + query_nuclides = ["total"] # Use tally summation if user requested the sum for all nuclides - if nuclides == 'sum' or nuclides == ['sum']: + if nuclides == "sum" or nuclides == ["sum"]: xs_tally = self.xs_tally.summation(nuclides=query_nuclides) - xs = xs_tally.get_values(filters=filters, filter_bins=filter_bins, - value=value) + xs = xs_tally.get_values( + filters=filters, filter_bins=filter_bins, value=value + ) else: - xs = self.xs_tally.get_values(filters=filters, - filter_bins=filter_bins, - nuclides=query_nuclides, value=value) + xs = self.xs_tally.get_values( + filters=filters, + filter_bins=filter_bins, + nuclides=query_nuclides, + value=value, + ) # Divide by atom number densities for microscopic cross sections - if xs_type == 'micro' and self._divide_by_density: + if xs_type == "micro" and self._divide_by_density: if self.by_nuclide: densities = self.get_nuclide_densities(nuclides) else: - densities = self.get_nuclide_densities('sum') - if value == 'mean' or value == 'std_dev': + densities = self.get_nuclide_densities("sum") + if value == "mean" or value == "std_dev": xs /= densities[np.newaxis, :, np.newaxis] # Eliminate the trivial score dimension xs = np.squeeze(xs, axis=len(xs.shape) - 1) xs = np.nan_to_num(xs) - if in_groups == 'all': + if in_groups == "all": num_in_groups = self.num_groups else: num_in_groups = len(in_groups) - if out_groups == 'all': + if out_groups == "all": num_out_groups = self.num_groups else: num_out_groups = len(out_groups) # Reshape tally data array with separate axes for domain and energy # Accomodate the polar and azimuthal bins if needed - num_subdomains = int(xs.shape[0] / (num_in_groups * num_out_groups * - self.num_polar * - self.num_azimuthal)) + num_subdomains = int( + xs.shape[0] + / (num_in_groups * num_out_groups * self.num_polar * self.num_azimuthal) + ) if self.num_polar > 1 or self.num_azimuthal > 1: - new_shape = (self.num_polar, self.num_azimuthal, num_subdomains, - num_in_groups, num_out_groups) + new_shape = ( + self.num_polar, + self.num_azimuthal, + num_subdomains, + num_in_groups, + num_out_groups, + ) new_shape += xs.shape[1:] xs = np.reshape(xs, new_shape) # Transpose the matrix if requested by user - if row_column == 'outin': + if row_column == "outin": xs = np.swapaxes(xs, 3, 4) else: new_shape = (num_subdomains, num_in_groups, num_out_groups) @@ -2352,12 +2454,12 @@ def get_xs(self, in_groups='all', out_groups='all', subdomains='all', xs = np.reshape(xs, new_shape) # Transpose the matrix if requested by user - if row_column == 'outin': + if row_column == "outin": xs = np.swapaxes(xs, 1, 2) # Reverse data if user requested increasing energy groups since # tally data is stored in order of increasing energies - if order_groups == 'increasing': + if order_groups == "increasing": xs = xs[..., ::-1, ::-1, :] if squeeze: @@ -2414,14 +2516,14 @@ def get_slice(self, nuclides=[], in_groups=[], out_groups=[]): for tally_type, tally in slice_xs.tallies.items(): if tally.contains_filter(openmc.EnergyoutFilter): tally_slice = tally.get_slice( - filters=[openmc.EnergyoutFilter], - filter_bins=filter_bins) + filters=[openmc.EnergyoutFilter], filter_bins=filter_bins + ) slice_xs.tallies[tally_type] = tally_slice slice_xs.sparse = self.sparse return slice_xs - def print_xs(self, subdomains='all', nuclides='all', xs_type='macro'): + def print_xs(self, subdomains="all", nuclides="all", xs_type="macro"): """Prints a string representation for the multi-group cross section. Parameters @@ -2444,116 +2546,131 @@ def print_xs(self, subdomains='all', nuclides='all', xs_type='macro'): # Construct a collection of the subdomains to report if not isinstance(subdomains, str): - cv.check_iterable_type('subdomains', subdomains, Integral) - elif self.domain_type == 'distribcell': + cv.check_iterable_type("subdomains", subdomains, Integral) + elif self.domain_type == "distribcell": subdomains = np.arange(self.num_subdomains, dtype=int) - elif self.domain_type == 'mesh': + elif self.domain_type == "mesh": subdomains = list(self.domain.indices) else: subdomains = [self.domain.id] # Construct a collection of the nuclides to report if self.by_nuclide: - if nuclides == 'all': + if nuclides == "all": nuclides = self.get_nuclides() - if nuclides == 'sum': - nuclides = ['sum'] + if nuclides == "sum": + nuclides = ["sum"] else: - cv.check_iterable_type('nuclides', nuclides, str) + cv.check_iterable_type("nuclides", nuclides, str) else: - nuclides = ['sum'] + nuclides = ["sum"] - cv.check_value('xs_type', xs_type, ['macro', 'micro']) + cv.check_value("xs_type", xs_type, ["macro", "micro"]) # Build header for string with type and domain info - string = 'Multi-Group XS\n' - string += '{0: <16}=\t{1}\n'.format('\tReaction Type', self.mgxs_type) - string += '{0: <16}=\t{1}\n'.format('\tDomain Type', self.domain_type) - string += '{0: <16}=\t{1}\n'.format('\tDomain ID', self.domain.id) + string = "Multi-Group XS\n" + string += "{0: <16}=\t{1}\n".format("\tReaction Type", self.mgxs_type) + string += "{0: <16}=\t{1}\n".format("\tDomain Type", self.domain_type) + string += "{0: <16}=\t{1}\n".format("\tDomain ID", self.domain.id) # Generate the header for an individual XS - xs_header = f'\tCross Sections [{self.get_units(xs_type)}]:' + xs_header = f"\tCross Sections [{self.get_units(xs_type)}]:" # If cross section data has not been computed, only print string header if self.tallies is None: print(string) return - string += '{0: <16}\n'.format('\tEnergy Groups:') - template = '{0: <12}Group {1} [{2: <10} - {3: <10}eV]\n' + string += "{0: <16}\n".format("\tEnergy Groups:") + template = "{0: <12}Group {1} [{2: <10} - {3: <10}eV]\n" # Loop over energy groups ranges for group in range(1, self.num_groups + 1): bounds = self.energy_groups.get_group_bounds(group) - string += template.format('', group, bounds[0], bounds[1]) + string += template.format("", group, bounds[0], bounds[1]) # Set polar and azimuthal bins if necessary if self.num_polar > 1 or self.num_azimuthal > 1: - pol_bins = np.linspace(0., np.pi, num=self.num_polar + 1, - endpoint=True) - azi_bins = np.linspace(-np.pi, np.pi, num=self.num_azimuthal + 1, - endpoint=True) + pol_bins = np.linspace(0.0, np.pi, num=self.num_polar + 1, endpoint=True) + azi_bins = np.linspace( + -np.pi, np.pi, num=self.num_azimuthal + 1, endpoint=True + ) # Loop over all subdomains for subdomain in subdomains: - if self.domain_type == 'distribcell' or self.domain_type == 'mesh': - string += '{0: <16}=\t{1}\n'.format('\tSubdomain', subdomain) + if self.domain_type == "distribcell" or self.domain_type == "mesh": + string += "{0: <16}=\t{1}\n".format("\tSubdomain", subdomain) # Loop over all Nuclides for nuclide in nuclides: # Build header for nuclide type - if xs_type != 'sum': - string += '{0: <16}=\t{1}\n'.format('\tNuclide', nuclide) + if xs_type != "sum": + string += "{0: <16}=\t{1}\n".format("\tNuclide", nuclide) # Build header for cross section type - string += f'{xs_header: <16}\n' - template = '{0: <12}Group {1} -> Group {2}:\t\t' - - average_xs = self.get_xs(nuclides=[nuclide], - subdomains=[subdomain], - xs_type=xs_type, value='mean') - rel_err_xs = self.get_xs(nuclides=[nuclide], - subdomains=[subdomain], - xs_type=xs_type, value='rel_err') - rel_err_xs = rel_err_xs * 100. + string += f"{xs_header: <16}\n" + template = "{0: <12}Group {1} -> Group {2}:\t\t" + + average_xs = self.get_xs( + nuclides=[nuclide], + subdomains=[subdomain], + xs_type=xs_type, + value="mean", + ) + rel_err_xs = self.get_xs( + nuclides=[nuclide], + subdomains=[subdomain], + xs_type=xs_type, + value="rel_err", + ) + rel_err_xs = rel_err_xs * 100.0 if self.num_polar > 1 or self.num_azimuthal > 1: # Loop over polar, azi, and in/out energy group ranges for pol in range(len(pol_bins) - 1): - pol_low, pol_high = pol_bins[pol: pol + 2] + pol_low, pol_high = pol_bins[pol : pol + 2] for azi in range(len(azi_bins) - 1): - azi_low, azi_high = azi_bins[azi: azi + 2] - string += '\t\tPolar Angle: [{0:5f} - {1:5f}]'.format( - pol_low, pol_high) + \ - '\tAzimuthal Angle: [{0:5f} - {1:5f}]'.format( - azi_low, azi_high) + '\n' + azi_low, azi_high = azi_bins[azi : azi + 2] + string += ( + "\t\tPolar Angle: [{0:5f} - {1:5f}]".format( + pol_low, pol_high + ) + + "\tAzimuthal Angle: [{0:5f} - {1:5f}]".format( + azi_low, azi_high + ) + + "\n" + ) for in_group in range(1, self.num_groups + 1): for out_group in range(1, self.num_groups + 1): - string += '\t' + template.format('', - in_group, - out_group) - string += '{0:.2e} +/- {1:.2e}%'.format( - average_xs[pol, azi, in_group - 1, - out_group - 1], - rel_err_xs[pol, azi, in_group - 1, - out_group - 1]) - string += '\n' - string += '\n' - string += '\n' + string += "\t" + template.format( + "", in_group, out_group + ) + string += "{0:.2e} +/- {1:.2e}%".format( + average_xs[ + pol, azi, in_group - 1, out_group - 1 + ], + rel_err_xs[ + pol, azi, in_group - 1, out_group - 1 + ], + ) + string += "\n" + string += "\n" + string += "\n" else: # Loop over incoming/outgoing energy groups ranges for in_group in range(1, self.num_groups + 1): for out_group in range(1, self.num_groups + 1): - string += template.format('', in_group, out_group) - string += '{0:.2e} +/- {1:.2e}%'.format( + string += template.format("", in_group, out_group) + string += "{0:.2e} +/- {1:.2e}%".format( average_xs[in_group - 1, out_group - 1], - rel_err_xs[in_group - 1, out_group - 1]) - string += '\n' - string += '\n' - string += '\n' - string += '\n' + rel_err_xs[in_group - 1, out_group - 1], + ) + string += "\n" + string += "\n" + string += "\n" + string += "\n" print(string) @@ -2587,11 +2704,26 @@ class TotalXS(MGXS): """ - def __init__(self, domain=None, domain_type=None, energy_groups=None, - by_nuclide=False, name='', num_polar=1, num_azimuthal=1): - super().__init__(domain, domain_type, energy_groups, by_nuclide, name, - num_polar, num_azimuthal) - self._rxn_type = 'total' + def __init__( + self, + domain=None, + domain_type=None, + energy_groups=None, + by_nuclide=False, + name="", + num_polar=1, + num_azimuthal=1, + ): + super().__init__( + domain, + domain_type, + energy_groups, + by_nuclide, + name, + num_polar, + num_azimuthal, + ) + self._rxn_type = "total" class TransportXS(MGXS): @@ -2728,15 +2860,31 @@ class TransportXS(MGXS): """ - def __init__(self, domain=None, domain_type=None, energy_groups=None, nu=False, - by_nuclide=False, name='', num_polar=1, num_azimuthal=1): - super().__init__(domain, domain_type, energy_groups, by_nuclide, name, - num_polar, num_azimuthal) + def __init__( + self, + domain=None, + domain_type=None, + energy_groups=None, + nu=False, + by_nuclide=False, + name="", + num_polar=1, + num_azimuthal=1, + ): + super().__init__( + domain, + domain_type, + energy_groups, + by_nuclide, + name, + num_polar, + num_azimuthal, + ) # Use tracklength estimators for the total MGXS term, and # analog estimators for the transport correction term - self._estimator = ['tracklength', 'tracklength', 'analog', 'analog'] - self._valid_estimators = ['analog'] + self._estimator = ["tracklength", "tracklength", "analog", "analog"] + self._valid_estimators = ["analog"] self.nu = nu def __deepcopy__(self, memo): @@ -2747,13 +2895,13 @@ def __deepcopy__(self, memo): @property def scores(self): if not self.nu: - return ['flux', 'total', 'flux', 'scatter'] + return ["flux", "total", "flux", "scatter"] else: - return ['flux', 'total', 'flux', 'nu-scatter'] + return ["flux", "total", "flux", "nu-scatter"] @property def tally_keys(self): - return ['flux (tracklength)', 'total', 'flux (analog)', 'scatter-1'] + return ["flux (tracklength)", "total", "flux (analog)", "scatter-1"] @property def filters(self): @@ -2761,8 +2909,12 @@ def filters(self): energy_filter = openmc.EnergyFilter(group_edges) energyout_filter = openmc.EnergyoutFilter(group_edges) p1_filter = openmc.LegendreFilter(1) - filters = [[energy_filter], [energy_filter], - [energy_filter], [energyout_filter, p1_filter]] + filters = [ + [energy_filter], + [energy_filter], + [energy_filter], + [energyout_filter, p1_filter], + ] return self._add_angle_filters(filters) @@ -2770,18 +2922,18 @@ def filters(self): def rxn_rate_tally(self): if self._rxn_rate_tally is None: # Switch EnergyoutFilter to EnergyFilter. - p1_tally = self.tallies['scatter-1'] + p1_tally = self.tallies["scatter-1"] old_filt = p1_tally.filters[-2] new_filt = openmc.EnergyFilter(old_filt.values) p1_tally.filters[-2] = new_filt # Slice Legendre expansion filter and change name of score - p1_tally = p1_tally.get_slice(filters=[openmc.LegendreFilter], - filter_bins=[('P1',)], - squeeze=True) - p1_tally._scores = ['scatter-1'] + p1_tally = p1_tally.get_slice( + filters=[openmc.LegendreFilter], filter_bins=[("P1",)], squeeze=True + ) + p1_tally._scores = ["scatter-1"] - self._rxn_rate_tally = self.tallies['total'] - p1_tally + self._rxn_rate_tally = self.tallies["total"] - p1_tally self._rxn_rate_tally.sparse = self.sparse return self._rxn_rate_tally @@ -2790,27 +2942,29 @@ def rxn_rate_tally(self): def xs_tally(self): if self._xs_tally is None: if self.tallies is None: - msg = 'Unable to get xs_tally since tallies have ' \ - 'not been loaded from a statepoint' + msg = ( + "Unable to get xs_tally since tallies have " + "not been loaded from a statepoint" + ) raise ValueError(msg) # Switch EnergyoutFilter to EnergyFilter. - p1_tally = self.tallies['scatter-1'] + p1_tally = self.tallies["scatter-1"] old_filt = p1_tally.filters[-2] new_filt = openmc.EnergyFilter(old_filt.values) p1_tally.filters[-2] = new_filt # Slice Legendre expansion filter and change name of score - p1_tally = p1_tally.get_slice(filters=[openmc.LegendreFilter], - filter_bins=[('P1',)], - squeeze=True) - p1_tally._scores = ['scatter-1'] + p1_tally = p1_tally.get_slice( + filters=[openmc.LegendreFilter], filter_bins=[("P1",)], squeeze=True + ) + p1_tally._scores = ["scatter-1"] # Compute total cross section - total_xs = self.tallies['total'] / self.tallies['flux (tracklength)'] + total_xs = self.tallies["total"] / self.tallies["flux (tracklength)"] # Compute transport correction term - trans_corr = p1_tally / self.tallies['flux (analog)'] + trans_corr = p1_tally / self.tallies["flux (analog)"] # Compute the transport-corrected total cross section self._xs_tally = total_xs - trans_corr @@ -2824,12 +2978,12 @@ def nu(self): @nu.setter def nu(self, nu): - cv.check_type('nu', nu, bool) + cv.check_type("nu", nu, bool) self._nu = nu if not nu: - self._rxn_type = 'transport' + self._rxn_type = "transport" else: - self._rxn_type = 'nu-transport' + self._rxn_type = "nu-transport" class DiffusionCoefficient(TransportXS): @@ -2969,34 +3123,49 @@ class DiffusionCoefficient(TransportXS): """ - def __init__(self, domain=None, domain_type=None, energy_groups=None, nu=False, - by_nuclide=False, name='', num_polar=1, num_azimuthal=1): - super(DiffusionCoefficient, self).__init__(domain, domain_type, energy_groups, - nu, by_nuclide, name, - num_polar, num_azimuthal) + def __init__( + self, + domain=None, + domain_type=None, + energy_groups=None, + nu=False, + by_nuclide=False, + name="", + num_polar=1, + num_azimuthal=1, + ): + super(DiffusionCoefficient, self).__init__( + domain, + domain_type, + energy_groups, + nu, + by_nuclide, + name, + num_polar, + num_azimuthal, + ) if not nu: - self._rxn_type = 'diffusion-coefficient' + self._rxn_type = "diffusion-coefficient" else: - self._rxn_type = 'nu-diffusion-coefficient' - + self._rxn_type = "nu-diffusion-coefficient" @property def rxn_rate_tally(self): if self._rxn_rate_tally is None: # Switch EnergyoutFilter to EnergyFilter. - p1_tally = self.tallies['scatter-1'] + p1_tally = self.tallies["scatter-1"] old_filt = p1_tally.filters[-2] new_filt = openmc.EnergyFilter(old_filt.values) p1_tally.filters[-2] = new_filt # Slice Legendre expansion filter and change name of score - p1_tally = p1_tally.get_slice(filters=[openmc.LegendreFilter], - filter_bins=[('P1',)], - squeeze=True) - p1_tally._scores = ['scatter-1'] + p1_tally = p1_tally.get_slice( + filters=[openmc.LegendreFilter], filter_bins=[("P1",)], squeeze=True + ) + p1_tally._scores = ["scatter-1"] - transport = self.tallies['total'] - p1_tally - self._rxn_rate_tally = transport**(-1) / 3.0 + transport = self.tallies["total"] - p1_tally + self._rxn_rate_tally = transport ** (-1) / 3.0 self._rxn_rate_tally.sparse = self.sparse return self._rxn_rate_tally @@ -3005,31 +3174,36 @@ def rxn_rate_tally(self): def xs_tally(self): if self._xs_tally is None: if self.tallies is None: - msg = 'Unable to get xs_tally since tallies have ' \ - 'not been loaded from a statepoint' + msg = ( + "Unable to get xs_tally since tallies have " + "not been loaded from a statepoint" + ) raise ValueError(msg) # Switch EnergyoutFilter to EnergyFilter # If 'scatter-1' is not in tallies, it is because the transport correction has # already occurred on this MGXS in another function or in a previous call to this function. - if 'scatter-1' in self.tallies: - p1_tally = self.tallies['scatter-1'] + if "scatter-1" in self.tallies: + p1_tally = self.tallies["scatter-1"] old_filt = p1_tally.filters[-2] new_filt = openmc.EnergyFilter(old_filt.values) p1_tally.filters[-2] = new_filt - p1_tally = p1_tally.get_slice(filters=[openmc.LegendreFilter], - filter_bins=[('P1',)],squeeze=True) - p1_tally._scores = ['scatter-1'] - total_xs = self.tallies['total'] / self.tallies['flux (tracklength)'] - trans_corr = p1_tally / self.tallies['flux (analog)'] + p1_tally = p1_tally.get_slice( + filters=[openmc.LegendreFilter], filter_bins=[("P1",)], squeeze=True + ) + p1_tally._scores = ["scatter-1"] + total_xs = self.tallies["total"] / self.tallies["flux (tracklength)"] + trans_corr = p1_tally / self.tallies["flux (analog)"] transport = total_xs - trans_corr - diff_coef = transport**(-1) / 3.0 + diff_coef = transport ** (-1) / 3.0 self._xs_tally = diff_coef self._compute_xs() else: - self._xs_tally = self.tallies[self._rxn_type] / self.tallies['flux (tracklength)'] + self._xs_tally = ( + self.tallies[self._rxn_type] / self.tallies["flux (tracklength)"] + ) self._compute_xs() return self._xs_tally @@ -3049,37 +3223,44 @@ def get_condensed_xs(self, coarse_groups): """ - cv.check_type('coarse_groups', coarse_groups, EnergyGroups) - cv.check_less_than('coarse groups', coarse_groups.num_groups, - self.num_groups, equality=True) - cv.check_value('upper coarse energy', coarse_groups.group_edges[-1], - [self.energy_groups.group_edges[-1]]) - cv.check_value('lower coarse energy', coarse_groups.group_edges[0], - [self.energy_groups.group_edges[0]]) + cv.check_type("coarse_groups", coarse_groups, EnergyGroups) + cv.check_less_than( + "coarse groups", coarse_groups.num_groups, self.num_groups, equality=True + ) + cv.check_value( + "upper coarse energy", + coarse_groups.group_edges[-1], + [self.energy_groups.group_edges[-1]], + ) + cv.check_value( + "lower coarse energy", + coarse_groups.group_edges[0], + [self.energy_groups.group_edges[0]], + ) # Clone this MGXS to initialize the condensed version condensed_xs = copy.deepcopy(self) # If 'scatter-1' is not in tallies, it is because the transport correction has # already occurred on this MGXS in another function or in a previous call to this function. - if 'scatter-1' in self.tallies: - p1_tally = self.tallies['scatter-1'] + if "scatter-1" in self.tallies: + p1_tally = self.tallies["scatter-1"] old_filt = p1_tally.filters[-2] new_filt = openmc.EnergyFilter(old_filt.values) p1_tally.filters[-2] = new_filt - p1_tally = p1_tally.get_slice(filters=[openmc.LegendreFilter], - filter_bins=[('P1',)], - squeeze=True) - p1_tally._scores = ['scatter-1'] - total_xs = self.tallies['total'] / self.tallies['flux (tracklength)'] - trans_corr = p1_tally / self.tallies['flux (analog)'] + p1_tally = p1_tally.get_slice( + filters=[openmc.LegendreFilter], filter_bins=[("P1",)], squeeze=True + ) + p1_tally._scores = ["scatter-1"] + total_xs = self.tallies["total"] / self.tallies["flux (tracklength)"] + trans_corr = p1_tally / self.tallies["flux (analog)"] transport = total_xs - trans_corr - diff_coef = transport**(-1) / 3.0 - diff_coef *= self.tallies['flux (tracklength)'] - flux_tally = condensed_xs.tallies['flux (tracklength)'] + diff_coef = transport ** (-1) / 3.0 + diff_coef *= self.tallies["flux (tracklength)"] + flux_tally = condensed_xs.tallies["flux (tracklength)"] condensed_xs._tallies = {} condensed_xs._tallies[self._rxn_type] = diff_coef - condensed_xs._tallies['flux (tracklength)'] = flux_tally + condensed_xs._tallies["flux (tracklength)"] = flux_tally condensed_xs._rxn_rate_tally = diff_coef condensed_xs._xs_tally = None condensed_xs._sparse = False @@ -3109,13 +3290,14 @@ def get_condensed_xs(self, coarse_groups): tally._sum_sq = None # Get tally data arrays reshaped with one dimension per filter - mean = tally.get_reshaped_data(value='mean') - std_dev = tally.get_reshaped_data(value='std_dev') + mean = tally.get_reshaped_data(value="mean") + std_dev = tally.get_reshaped_data(value="std_dev") # Sum across all applicable fine energy group filters for i, tally_filter in enumerate(tally.filters): - if not isinstance(tally_filter, (openmc.EnergyFilter, - openmc.EnergyoutFilter)): + if not isinstance( + tally_filter, (openmc.EnergyFilter, openmc.EnergyoutFilter) + ): continue elif len(tally_filter.bins) != len(fine_edges) - 1: continue @@ -3126,8 +3308,7 @@ def get_condensed_xs(self, coarse_groups): tally_filter.values = cedge tally_filter.bins = np.vstack((cedge[:-1], cedge[1:])).T mean = np.add.reduceat(mean, energy_indices, axis=i) - std_dev = np.add.reduceat(std_dev**2, energy_indices, - axis=i) + std_dev = np.add.reduceat(std_dev**2, energy_indices, axis=i) std_dev = np.sqrt(std_dev) # Reshape condensed data arrays with one dimension for all filters @@ -3142,6 +3323,7 @@ def get_condensed_xs(self, coarse_groups): condensed_xs.sparse = self.sparse return condensed_xs + @add_params class AbsorptionXS(MGXS): r"""An absorption multi-group cross section. @@ -3175,11 +3357,26 @@ class AbsorptionXS(MGXS): """ - def __init__(self, domain=None, domain_type=None, energy_groups=None, - by_nuclide=False, name='', num_polar=1, num_azimuthal=1): - super().__init__(domain, domain_type, energy_groups, by_nuclide, name, - num_polar, num_azimuthal) - self._rxn_type = 'absorption' + def __init__( + self, + domain=None, + domain_type=None, + energy_groups=None, + by_nuclide=False, + name="", + num_polar=1, + num_azimuthal=1, + ): + super().__init__( + domain, + domain_type, + energy_groups, + by_nuclide, + name, + num_polar, + num_azimuthal, + ) + self._rxn_type = "absorption" @add_params @@ -3217,24 +3414,39 @@ class ReducedAbsorptionXS(MGXS): """ - def __init__(self, domain=None, domain_type=None, energy_groups=None, - by_nuclide=False, name='', num_polar=1, num_azimuthal=1): - super().__init__(domain, domain_type, energy_groups, by_nuclide, name, - num_polar, num_azimuthal) - self._rxn_type = 'reduced absorption' + def __init__( + self, + domain=None, + domain_type=None, + energy_groups=None, + by_nuclide=False, + name="", + num_polar=1, + num_azimuthal=1, + ): + super().__init__( + domain, + domain_type, + energy_groups, + by_nuclide, + name, + num_polar, + num_azimuthal, + ) + self._rxn_type = "reduced absorption" @property def scores(self): - return ['flux', 'absorption', '(n,2n)', '(n,3n)', '(n,4n)'] + return ["flux", "absorption", "(n,2n)", "(n,3n)", "(n,4n)"] @property def rxn_rate_tally(self): if self._rxn_rate_tally is None: self._rxn_rate_tally = ( - self.tallies['absorption'] - - self.tallies['(n,2n)'] - - 2*self.tallies['(n,3n)'] - - 3*self.tallies['(n,4n)'] + self.tallies["absorption"] + - self.tallies["(n,2n)"] + - 2 * self.tallies["(n,3n)"] + - 3 * self.tallies["(n,4n)"] ) self._rxn_rate_tally.sparse = self.sparse return self._rxn_rate_tally @@ -3276,21 +3488,35 @@ class CaptureXS(MGXS): """ - def __init__(self, domain=None, domain_type=None, energy_groups=None, - by_nuclide=False, name='', num_polar=1, num_azimuthal=1): - super().__init__(domain, domain_type, energy_groups, by_nuclide, name, - num_polar, num_azimuthal) - self._rxn_type = 'capture' + def __init__( + self, + domain=None, + domain_type=None, + energy_groups=None, + by_nuclide=False, + name="", + num_polar=1, + num_azimuthal=1, + ): + super().__init__( + domain, + domain_type, + energy_groups, + by_nuclide, + name, + num_polar, + num_azimuthal, + ) + self._rxn_type = "capture" @property def scores(self): - return ['flux', 'absorption', 'fission'] + return ["flux", "absorption", "fission"] @property def rxn_rate_tally(self): if self._rxn_rate_tally is None: - self._rxn_rate_tally = \ - self.tallies['absorption'] - self.tallies['fission'] + self._rxn_rate_tally = self.tallies["absorption"] - self.tallies["fission"] self._rxn_rate_tally.sparse = self.sparse return self._rxn_rate_tally @@ -3432,11 +3658,27 @@ class FissionXS(MGXS): """ - def __init__(self, domain=None, domain_type=None, energy_groups=None, nu=False, - prompt=False, by_nuclide=False, name='', num_polar=1, - num_azimuthal=1): - super().__init__(domain, domain_type, energy_groups, by_nuclide, name, - num_polar, num_azimuthal) + def __init__( + self, + domain=None, + domain_type=None, + energy_groups=None, + nu=False, + prompt=False, + by_nuclide=False, + name="", + num_polar=1, + num_azimuthal=1, + ): + super().__init__( + domain, + domain_type, + energy_groups, + by_nuclide, + name, + num_polar, + num_azimuthal, + ) self._nu = False self._prompt = False self.nu = nu @@ -3454,15 +3696,15 @@ def nu(self): @nu.setter def nu(self, nu): - cv.check_type('nu', nu, bool) + cv.check_type("nu", nu, bool) self._nu = nu if not self.prompt: if not self.nu: - self._rxn_type = 'fission' + self._rxn_type = "fission" else: - self._rxn_type = 'nu-fission' + self._rxn_type = "nu-fission" else: - self._rxn_type = 'prompt-nu-fission' + self._rxn_type = "prompt-nu-fission" @property def prompt(self): @@ -3470,15 +3712,15 @@ def prompt(self): @prompt.setter def prompt(self, prompt): - cv.check_type('prompt', prompt, bool) + cv.check_type("prompt", prompt, bool) self._prompt = prompt if not self.prompt: if not self.nu: - self._rxn_type = 'fission' + self._rxn_type = "fission" else: - self._rxn_type = 'nu-fission' + self._rxn_type = "nu-fission" else: - self._rxn_type = 'prompt-nu-fission' + self._rxn_type = "prompt-nu-fission" @add_params @@ -3517,11 +3759,26 @@ class KappaFissionXS(MGXS): """ - def __init__(self, domain=None, domain_type=None, energy_groups=None, - by_nuclide=False, name='', num_polar=1, num_azimuthal=1): - super().__init__(domain, domain_type, energy_groups, by_nuclide, name, - num_polar, num_azimuthal) - self._rxn_type = 'kappa-fission' + def __init__( + self, + domain=None, + domain_type=None, + energy_groups=None, + by_nuclide=False, + name="", + num_polar=1, + num_azimuthal=1, + ): + super().__init__( + domain, + domain_type, + energy_groups, + by_nuclide, + name, + num_polar, + num_azimuthal, + ) + self._rxn_type = "kappa-fission" class ScatterXS(MGXS): @@ -3651,11 +3908,26 @@ class ScatterXS(MGXS): """ - def __init__(self, domain=None, domain_type=None, energy_groups=None, - by_nuclide=False, name='', num_polar=1, - num_azimuthal=1, nu=False): - super().__init__(domain, domain_type, energy_groups, by_nuclide, name, - num_polar, num_azimuthal) + def __init__( + self, + domain=None, + domain_type=None, + energy_groups=None, + by_nuclide=False, + name="", + num_polar=1, + num_azimuthal=1, + nu=False, + ): + super().__init__( + domain, + domain_type, + energy_groups, + by_nuclide, + name, + num_polar, + num_azimuthal, + ) self.nu = nu def __deepcopy__(self, memo): @@ -3669,14 +3941,14 @@ def nu(self): @nu.setter def nu(self, nu): - cv.check_type('nu', nu, bool) + cv.check_type("nu", nu, bool) self._nu = nu if not nu: - self._rxn_type = 'scatter' + self._rxn_type = "scatter" else: - self._rxn_type = 'nu-scatter' - self._estimator = 'analog' - self._valid_estimators = ['analog'] + self._rxn_type = "nu-scatter" + self._estimator = "analog" + self._valid_estimators = ["analog"] @add_params @@ -3710,11 +3982,27 @@ class ArbitraryXS(MGXS): """ - def __init__(self, rxn_type, domain=None, domain_type=None, energy_groups=None, - by_nuclide=False, name='', num_polar=1, num_azimuthal=1): + def __init__( + self, + rxn_type, + domain=None, + domain_type=None, + energy_groups=None, + by_nuclide=False, + name="", + num_polar=1, + num_azimuthal=1, + ): cv.check_value("rxn_type", rxn_type, ARBITRARY_VECTOR_TYPES) - super().__init__(domain, domain_type, energy_groups, by_nuclide, name, - num_polar, num_azimuthal) + super().__init__( + domain, + domain_type, + energy_groups, + by_nuclide, + name, + num_polar, + num_azimuthal, + ) self._rxn_type = rxn_type @@ -3756,15 +4044,30 @@ class ArbitraryMatrixXS(MatrixMGXS): """ - def __init__(self, rxn_type, domain=None, domain_type=None, energy_groups=None, - by_nuclide=False, name='', num_polar=1, - num_azimuthal=1): + def __init__( + self, + rxn_type, + domain=None, + domain_type=None, + energy_groups=None, + by_nuclide=False, + name="", + num_polar=1, + num_azimuthal=1, + ): cv.check_value("rxn_type", rxn_type, ARBITRARY_MATRIX_TYPES) - super().__init__(domain, domain_type, energy_groups, by_nuclide, name, - num_polar, num_azimuthal) + super().__init__( + domain, + domain_type, + energy_groups, + by_nuclide, + name, + num_polar, + num_azimuthal, + ) self._rxn_type = rxn_type.split(" ")[0] - self._estimator = 'analog' - self._valid_estimators = ['analog'] + self._estimator = "analog" + self._valid_estimators = ["analog"] class ScatterMatrixXS(MatrixMGXS): @@ -3961,18 +4264,33 @@ class ScatterMatrixXS(MatrixMGXS): """ - def __init__(self, domain=None, domain_type=None, energy_groups=None, - by_nuclide=False, name='', num_polar=1, - num_azimuthal=1, nu=False): - super().__init__(domain, domain_type, energy_groups, by_nuclide, name, - num_polar, num_azimuthal) - self._formulation = 'simple' - self._correction = 'P0' + def __init__( + self, + domain=None, + domain_type=None, + energy_groups=None, + by_nuclide=False, + name="", + num_polar=1, + num_azimuthal=1, + nu=False, + ): + super().__init__( + domain, + domain_type, + energy_groups, + by_nuclide, + name, + num_polar, + num_azimuthal, + ) + self._formulation = "simple" + self._correction = "P0" self._scatter_format = SCATTER_LEGENDRE self._legendre_order = 0 self._histogram_bins = 16 - self._estimator = 'analog' - self._valid_estimators = ['analog'] + self._estimator = "analog" + self._valid_estimators = ["analog"] self.nu = nu def __deepcopy__(self, memo): @@ -4007,21 +4325,21 @@ def formulation(self): @formulation.setter def formulation(self, formulation): - cv.check_value('formulation', formulation, ('simple', 'consistent')) + cv.check_value("formulation", formulation, ("simple", "consistent")) self._formulation = formulation - if self.formulation == 'simple': - self._valid_estimators = ['analog'] + if self.formulation == "simple": + self._valid_estimators = ["analog"] if not self.nu: - self._mgxs_type = 'scatter matrix' + self._mgxs_type = "scatter matrix" else: - self._mgxs_type = 'nu-scatter matrix' + self._mgxs_type = "nu-scatter matrix" else: - self._valid_estimators = ['tracklength'] + self._valid_estimators = ["tracklength"] if not self.nu: - self._mgxs_type = 'consistent scatter matrix' + self._mgxs_type = "consistent scatter matrix" else: - self._mgxs_type = 'consistent nu-scatter matrix' + self._mgxs_type = "consistent nu-scatter matrix" @property def correction(self): @@ -4029,17 +4347,21 @@ def correction(self): @correction.setter def correction(self, correction): - cv.check_value('correction', correction, ('P0', None)) + cv.check_value("correction", correction, ("P0", None)) if self.scatter_format == SCATTER_LEGENDRE: - if correction == 'P0' and self.legendre_order > 0: - msg = 'The P0 correction will be ignored since the ' \ - 'scattering order {} is greater than '\ - 'zero'.format(self.legendre_order) + if correction == "P0" and self.legendre_order > 0: + msg = ( + "The P0 correction will be ignored since the " + "scattering order {} is greater than " + "zero".format(self.legendre_order) + ) warnings.warn(msg) elif self.scatter_format == SCATTER_HISTOGRAM: - msg = 'The P0 correction will be ignored since the ' \ - 'scatter format is set to histogram' + msg = ( + "The P0 correction will be ignored since the " + "scatter format is set to histogram" + ) warnings.warn(msg) self._correction = correction @@ -4050,7 +4372,7 @@ def scatter_format(self): @scatter_format.setter def scatter_format(self, scatter_format): - cv.check_value('scatter_format', scatter_format, MU_TREATMENTS) + cv.check_value("scatter_format", scatter_format, MU_TREATMENTS) self._scatter_format = scatter_format @property @@ -4059,22 +4381,26 @@ def legendre_order(self): @legendre_order.setter def legendre_order(self, legendre_order): - cv.check_type('legendre_order', legendre_order, Integral) - cv.check_greater_than('legendre_order', legendre_order, 0, - equality=True) - cv.check_less_than('legendre_order', legendre_order, _MAX_LEGENDRE, - equality=True) + cv.check_type("legendre_order", legendre_order, Integral) + cv.check_greater_than("legendre_order", legendre_order, 0, equality=True) + cv.check_less_than( + "legendre_order", legendre_order, _MAX_LEGENDRE, equality=True + ) if self.scatter_format == SCATTER_LEGENDRE: - if self.correction == 'P0' and legendre_order > 0: - msg = 'The P0 correction will be ignored since the ' \ - 'scattering order {} is greater than '\ - 'zero'.format(legendre_order) + if self.correction == "P0" and legendre_order > 0: + msg = ( + "The P0 correction will be ignored since the " + "scattering order {} is greater than " + "zero".format(legendre_order) + ) warnings.warn(msg, RuntimeWarning) self.correction = None elif self.scatter_format == SCATTER_HISTOGRAM: - msg = 'The legendre order will be ignored since the ' \ - 'scatter format is set to histogram' + msg = ( + "The legendre order will be ignored since the " + "scatter format is set to histogram" + ) warnings.warn(msg) self._legendre_order = legendre_order @@ -4085,8 +4411,8 @@ def histogram_bins(self): @histogram_bins.setter def histogram_bins(self, histogram_bins): - cv.check_type('histogram_bins', histogram_bins, Integral) - cv.check_greater_than('histogram_bins', histogram_bins, 0) + cv.check_type("histogram_bins", histogram_bins, Integral) + cv.check_greater_than("histogram_bins", histogram_bins, 0) self._histogram_bins = histogram_bins @@ -4096,108 +4422,108 @@ def nu(self): @nu.setter def nu(self, nu): - cv.check_type('nu', nu, bool) + cv.check_type("nu", nu, bool) self._nu = nu - if self.formulation == 'simple': + if self.formulation == "simple": if not nu: - self._rxn_type = 'scatter' - self._mgxs_type = 'scatter matrix' + self._rxn_type = "scatter" + self._mgxs_type = "scatter matrix" else: - self._rxn_type = 'nu-scatter' - self._mgxs_type = 'nu-scatter matrix' + self._rxn_type = "nu-scatter" + self._mgxs_type = "nu-scatter matrix" else: if not nu: - self._rxn_type = 'scatter' - self._mgxs_type = 'consistent scatter matrix' + self._rxn_type = "scatter" + self._mgxs_type = "consistent scatter matrix" else: - self._rxn_type = 'nu-scatter' - self._mgxs_type = 'consistent nu-scatter matrix' + self._rxn_type = "nu-scatter" + self._mgxs_type = "consistent nu-scatter matrix" @property def scores(self): - if self.formulation == 'simple': - scores = ['flux', self.rxn_type] + if self.formulation == "simple": + scores = ["flux", self.rxn_type] else: # Add scores for groupwise scattering cross section - scores = ['flux', 'scatter'] + scores = ["flux", "scatter"] # Add scores for group-to-group scattering probability matrix # these scores also contain the angular information, whether it be # Legendre expansion or histogram bins - scores.append('scatter') + scores.append("scatter") # Add scores for multiplicity matrix; scatter info for the # denominator will come from the previous score if self.nu: - scores.append('nu-scatter') + scores.append("nu-scatter") # Add scores for transport correction - if self.correction == 'P0' and self.legendre_order == 0: - scores.extend([self.rxn_type, 'flux']) + if self.correction == "P0" and self.legendre_order == 0: + scores.extend([self.rxn_type, "flux"]) return scores @property def tally_keys(self): - if self.formulation == 'simple': + if self.formulation == "simple": return super().tally_keys else: # Add keys for groupwise scattering cross section - tally_keys = ['flux (tracklength)', 'scatter'] + tally_keys = ["flux (tracklength)", "scatter"] # Add keys for group-to-group scattering probability matrix - tally_keys.append('scatter matrix') + tally_keys.append("scatter matrix") # Add keys for multiplicity matrix if self.nu: - tally_keys.extend(['nu-scatter']) + tally_keys.extend(["nu-scatter"]) # Add keys for transport correction - if self.correction == 'P0' and self.legendre_order == 0: - tally_keys.extend(['correction', 'flux (analog)']) + if self.correction == "P0" and self.legendre_order == 0: + tally_keys.extend(["correction", "flux (analog)"]) return tally_keys @property def estimator(self): - if self.formulation == 'simple': + if self.formulation == "simple": return self._estimator else: # Add estimators for groupwise scattering cross section - estimators = ['tracklength', 'tracklength'] + estimators = ["tracklength", "tracklength"] # Add estimators for group-to-group scattering probabilities - estimators.append('analog') + estimators.append("analog") # Add estimators for multiplicity matrix if self.nu: - estimators.extend(['analog']) + estimators.extend(["analog"]) # Add estimators for transport correction - if self.correction == 'P0' and self.legendre_order == 0: - estimators.extend(['analog', 'analog']) + if self.correction == "P0" and self.legendre_order == 0: + estimators.extend(["analog", "analog"]) return estimators @property def filters(self): - if self.formulation == 'simple': + if self.formulation == "simple": group_edges = self.energy_groups.group_edges energy = openmc.EnergyFilter(group_edges) energyout = openmc.EnergyoutFilter(group_edges) if self.scatter_format == SCATTER_LEGENDRE: - if self.correction == 'P0' and self.legendre_order == 0: + if self.correction == "P0" and self.legendre_order == 0: angle_filter = openmc.LegendreFilter(order=1) else: - angle_filter = \ - openmc.LegendreFilter(order=self.legendre_order) + angle_filter = openmc.LegendreFilter(order=self.legendre_order) elif self.scatter_format == SCATTER_HISTOGRAM: - bins = np.linspace(-1., 1., num=self.histogram_bins + 1, - endpoint=True) + bins = np.linspace( + -1.0, 1.0, num=self.histogram_bins + 1, endpoint=True + ) angle_filter = openmc.MuFilter(bins) filters = [[energy], [energy, energyout, angle_filter]] @@ -4213,8 +4539,9 @@ def filters(self): if self.scatter_format == SCATTER_LEGENDRE: angle_filter = openmc.LegendreFilter(order=self.legendre_order) elif self.scatter_format == SCATTER_HISTOGRAM: - bins = np.linspace(-1., 1., num=self.histogram_bins + 1, - endpoint=True) + bins = np.linspace( + -1.0, 1.0, num=self.histogram_bins + 1, endpoint=True + ) angle_filter = openmc.MuFilter(bins) filters.append([energy, energyout, angle_filter]) @@ -4223,9 +4550,8 @@ def filters(self): filters.extend([[energy, energyout]]) # Add filters for transport correction - if self.correction == 'P0' and self.legendre_order == 0: - filters.extend([[energyout, openmc.LegendreFilter(1)], - [energy]]) + if self.correction == "P0" and self.legendre_order == 0: + filters.extend([[energyout, openmc.LegendreFilter(1)], [energy]]) return self._add_angle_filters(filters) @@ -4234,16 +4560,16 @@ def rxn_rate_tally(self): if self._rxn_rate_tally is None: - if self.formulation == 'simple': + if self.formulation == "simple": if self.scatter_format == SCATTER_LEGENDRE: # If using P0 correction subtract P1 scatter from the diag. - if self.correction == 'P0' and self.legendre_order == 0: + if self.correction == "P0" and self.legendre_order == 0: scatter_p0 = self.tallies[self.rxn_type].get_slice( - filters=[openmc.LegendreFilter], - filter_bins=[('P0',)]) + filters=[openmc.LegendreFilter], filter_bins=[("P0",)] + ) scatter_p1 = self.tallies[self.rxn_type].get_slice( - filters=[openmc.LegendreFilter], - filter_bins=[('P1',)]) + filters=[openmc.LegendreFilter], filter_bins=[("P1",)] + ) # Set the Legendre order of these tallies to be 0 # so they can be subtracted @@ -4252,17 +4578,15 @@ def rxn_rate_tally(self): scatter_p1.filters[-1] = legendre scatter_p1 = scatter_p1.summation( - filter_type=openmc.EnergyFilter, - remove_filter=True) + filter_type=openmc.EnergyFilter, remove_filter=True + ) - energy_filter = \ - scatter_p0.find_filter(openmc.EnergyFilter) + energy_filter = scatter_p0.find_filter(openmc.EnergyFilter) # Transform scatter-p1 into an energyin/out matrix # to match scattering matrix shape for tally arithmetic energy_filter = copy.deepcopy(energy_filter) - scatter_p1 = \ - scatter_p1.diagonalize_filter(energy_filter, 1) + scatter_p1 = scatter_p1.diagonalize_filter(energy_filter, 1) self._rxn_rate_tally = scatter_p0 - scatter_p1 @@ -4276,8 +4600,10 @@ def rxn_rate_tally(self): self._rxn_rate_tally.sparse = self.sparse else: - msg = 'The reaction rate tally is poorly defined' \ - ' for the consistent formulation' + msg = ( + "The reaction rate tally is poorly defined" + " for the consistent formulation" + ) raise NotImplementedError(msg) return self._rxn_rate_tally @@ -4286,68 +4612,79 @@ def rxn_rate_tally(self): def xs_tally(self): if self._xs_tally is None: if self.tallies is None: - msg = 'Unable to get xs_tally since tallies have ' \ - 'not been loaded from a statepoint' + msg = ( + "Unable to get xs_tally since tallies have " + "not been loaded from a statepoint" + ) raise ValueError(msg) # Use super class method - if self.formulation == 'simple': + if self.formulation == "simple": self._xs_tally = MGXS.xs_tally.fget(self) else: # Compute scattering probability matrixS - tally_key = 'scatter matrix' + tally_key = "scatter matrix" # Compute normalization factor summed across outgoing energies if self.scatter_format == SCATTER_LEGENDRE: norm = self.tallies[tally_key].get_slice( - scores=['scatter'], + scores=["scatter"], filters=[openmc.LegendreFilter], - filter_bins=[('P0',)], squeeze=True) + filter_bins=[("P0",)], + squeeze=True, + ) # Compute normalization factor summed across outgoing mu bins elif self.scatter_format == SCATTER_HISTOGRAM: - norm = self.tallies[tally_key].get_slice( - scores=['scatter']) + norm = self.tallies[tally_key].get_slice(scores=["scatter"]) norm = norm.summation( - filter_type=openmc.MuFilter, remove_filter=True) - norm = norm.summation(filter_type=openmc.EnergyoutFilter, - remove_filter=True) + filter_type=openmc.MuFilter, remove_filter=True + ) + norm = norm.summation( + filter_type=openmc.EnergyoutFilter, remove_filter=True + ) # Compute groupwise scattering cross section - self._xs_tally = self.tallies['scatter'] * \ - self.tallies[tally_key] / norm / \ - self.tallies['flux (tracklength)'] + self._xs_tally = ( + self.tallies["scatter"] + * self.tallies[tally_key] + / norm + / self.tallies["flux (tracklength)"] + ) # Override the nuclides for tally arithmetic - self._xs_tally.nuclides = self.tallies['scatter'].nuclides + self._xs_tally.nuclides = self.tallies["scatter"].nuclides # Multiply by the multiplicity matrix if self.nu: - numer = self.tallies['nu-scatter'] + numer = self.tallies["nu-scatter"] # Get the denominator if self.scatter_format == SCATTER_LEGENDRE: denom = self.tallies[tally_key].get_slice( - scores=['scatter'], + scores=["scatter"], filters=[openmc.LegendreFilter], - filter_bins=[('P0',)], squeeze=True) + filter_bins=[("P0",)], + squeeze=True, + ) # Compute normalization factor summed across mu bins elif self.scatter_format == SCATTER_HISTOGRAM: - denom = self.tallies[tally_key].get_slice( - scores=['scatter']) + denom = self.tallies[tally_key].get_slice(scores=["scatter"]) # Sum across all mu bins denom = denom.summation( - filter_type=openmc.MuFilter, remove_filter=True) + filter_type=openmc.MuFilter, remove_filter=True + ) - self._xs_tally *= (numer / denom) + self._xs_tally *= numer / denom # If using P0 correction subtract scatter-1 from the diagonal - if self.correction == 'P0' and self.legendre_order == 0: - scatter_p1 = self.tallies['correction'].get_slice( - filters=[openmc.LegendreFilter], filter_bins=[('P1',)]) - flux = self.tallies['flux (analog)'] + if self.correction == "P0" and self.legendre_order == 0: + scatter_p1 = self.tallies["correction"].get_slice( + filters=[openmc.LegendreFilter], filter_bins=[("P1",)] + ) + flux = self.tallies["flux (analog)"] # Set the Legendre order of the P1 tally to be P0 # so it can be subtracted @@ -4368,10 +4705,12 @@ def xs_tally(self): # Set xs_tally to be itself with only P0 data self._xs_tally = self._xs_tally.get_slice( - filters=[openmc.LegendreFilter], filter_bins=[('P0',)]) + filters=[openmc.LegendreFilter], filter_bins=[("P0",)] + ) # Tell xs_tally that it is P0 - legendre_xs_tally = \ - self._xs_tally.find_filter(openmc.LegendreFilter) + legendre_xs_tally = self._xs_tally.find_filter( + openmc.LegendreFilter + ) legendre_xs_tally.order = 0 # And subtract the P1 correction from the P0 matrix @@ -4383,15 +4722,14 @@ def xs_tally(self): if self.scatter_format == SCATTER_HISTOGRAM: angle_filter = self._xs_tally.find_filter(openmc.MuFilter) else: - angle_filter = \ - self._xs_tally.find_filter(openmc.LegendreFilter) + angle_filter = self._xs_tally.find_filter(openmc.LegendreFilter) angle_filter_index = self._xs_tally.filters.index(angle_filter) # If the angle filter index is not last, then make it last if angle_filter_index != len(self._xs_tally.filters) - 1: - energyout_filter = \ - self._xs_tally.find_filter(openmc.EnergyoutFilter) - self._xs_tally._swap_filters(energyout_filter, - angle_filter) + energyout_filter = self._xs_tally.find_filter( + openmc.EnergyoutFilter + ) + self._xs_tally._swap_filters(energyout_filter, angle_filter) return self._xs_tally @@ -4426,8 +4764,9 @@ def load_from_statepoint(self, statepoint): super().load_from_statepoint(statepoint) - def get_slice(self, nuclides=[], in_groups=[], out_groups=[], - legendre_order='same'): + def get_slice( + self, nuclides=[], in_groups=[], out_groups=[], legendre_order="same" + ): """Build a sliced ScatterMatrix for the specified nuclides and energy groups. @@ -4469,18 +4808,18 @@ def get_slice(self, nuclides=[], in_groups=[], out_groups=[], slice_xs._xs_tally = None # Slice the Legendre order if needed - if legendre_order != 'same' and self.scatter_format == SCATTER_LEGENDRE: - cv.check_type('legendre_order', legendre_order, Integral) - cv.check_less_than('legendre_order', legendre_order, - self.legendre_order, equality=True) + if legendre_order != "same" and self.scatter_format == SCATTER_LEGENDRE: + cv.check_type("legendre_order", legendre_order, Integral) + cv.check_less_than( + "legendre_order", legendre_order, self.legendre_order, equality=True + ) slice_xs.legendre_order = legendre_order # Slice the scattering tally - filter_bins = [tuple([f'P{i}' - for i in range(self.legendre_order + 1)])] - slice_xs.tallies[self.rxn_type] = \ - slice_xs.tallies[self.rxn_type].get_slice( - filters=[openmc.LegendreFilter], filter_bins=filter_bins) + filter_bins = [tuple([f"P{i}" for i in range(self.legendre_order + 1)])] + slice_xs.tallies[self.rxn_type] = slice_xs.tallies[self.rxn_type].get_slice( + filters=[openmc.LegendreFilter], filter_bins=filter_bins + ) # Slice outgoing energy groups if needed if len(out_groups) != 0: @@ -4494,17 +4833,26 @@ def get_slice(self, nuclides=[], in_groups=[], out_groups=[], for tally_type, tally in slice_xs.tallies.items(): if tally.contains_filter(openmc.EnergyoutFilter): tally_slice = tally.get_slice( - filters=[openmc.EnergyoutFilter], - filter_bins=filter_bins) + filters=[openmc.EnergyoutFilter], filter_bins=filter_bins + ) slice_xs.tallies[tally_type] = tally_slice slice_xs.sparse = self.sparse return slice_xs - def get_xs(self, in_groups='all', out_groups='all', - subdomains='all', nuclides='all', moment='all', - xs_type='macro', order_groups='increasing', - row_column='inout', value='mean', squeeze=True): + def get_xs( + self, + in_groups="all", + out_groups="all", + subdomains="all", + nuclides="all", + moment="all", + xs_type="macro", + order_groups="increasing", + row_column="inout", + value="mean", + squeeze=True, + ): r"""Returns an array of multi-group cross sections. This method constructs a 5D NumPy array for the requested @@ -4566,14 +4914,16 @@ def get_xs(self, in_groups='all', out_groups='all', """ - cv.check_value('value', value, ['mean', 'std_dev', 'rel_err']) - cv.check_value('xs_type', xs_type, ['macro', 'micro']) + cv.check_value("value", value, ["mean", "std_dev", "rel_err"]) + cv.check_value("xs_type", xs_type, ["macro", "micro"]) # FIXME: Unable to get microscopic xs for mesh domain because the mesh # cells do not know the nuclide densities in each mesh cell. - if self.domain_type == 'mesh' and xs_type == 'micro': - msg = 'Unable to get micro xs for mesh domain since the mesh ' \ - 'cells do not know the nuclide densities in each mesh cell.' + if self.domain_type == "mesh" and xs_type == "micro": + msg = ( + "Unable to get micro xs for mesh domain since the mesh " + "cells do not know the nuclide densities in each mesh cell." + ) raise ValueError(msg) filters = [] @@ -4581,7 +4931,7 @@ def get_xs(self, in_groups='all', out_groups='all', # Construct a collection of the domain filter bins if not isinstance(subdomains, str): - cv.check_iterable_type('subdomains', subdomains, Integral, max_depth=3) + cv.check_iterable_type("subdomains", subdomains, Integral, max_depth=3) filters.append(_DOMAIN_TO_FILTER[self.domain_type]) subdomain_bins = [] for subdomain in subdomains: @@ -4590,30 +4940,28 @@ def get_xs(self, in_groups='all', out_groups='all', # Construct list of energy group bounds tuples for all requested groups if not isinstance(in_groups, str): - cv.check_iterable_type('groups', in_groups, Integral) + cv.check_iterable_type("groups", in_groups, Integral) filters.append(openmc.EnergyFilter) energy_bins = [] for group in in_groups: - energy_bins.append( - (self.energy_groups.get_group_bounds(group),)) + energy_bins.append((self.energy_groups.get_group_bounds(group),)) filter_bins.append(tuple(energy_bins)) # Construct list of energy group bounds tuples for all requested groups if not isinstance(out_groups, str): - cv.check_iterable_type('groups', out_groups, Integral) + cv.check_iterable_type("groups", out_groups, Integral) for group in out_groups: filters.append(openmc.EnergyoutFilter) filter_bins.append((self.energy_groups.get_group_bounds(group),)) # Construct CrossScore for requested scattering moment if self.scatter_format == SCATTER_LEGENDRE: - if moment != 'all': - cv.check_type('moment', moment, Integral) - cv.check_greater_than('moment', moment, 0, equality=True) - cv.check_less_than( - 'moment', moment, self.legendre_order, equality=True) + if moment != "all": + cv.check_type("moment", moment, Integral) + cv.check_greater_than("moment", moment, 0, equality=True) + cv.check_less_than("moment", moment, self.legendre_order, equality=True) filters.append(openmc.LegendreFilter) - filter_bins.append((f'P{moment}',)) + filter_bins.append((f"P{moment}",)) num_angle_bins = 1 else: num_angle_bins = self.legendre_order + 1 @@ -4622,80 +4970,96 @@ def get_xs(self, in_groups='all', out_groups='all', # Construct a collection of the nuclides to retrieve from the xs tally if self.by_nuclide: - if nuclides == 'all' or nuclides == 'sum' or nuclides == ['sum']: + if nuclides == "all" or nuclides == "sum" or nuclides == ["sum"]: query_nuclides = self.get_nuclides() else: query_nuclides = nuclides else: - query_nuclides = ['total'] + query_nuclides = ["total"] # Use tally summation if user requested the sum for all nuclides scores = self.xs_tally.scores - if nuclides == 'sum' or nuclides == ['sum']: + if nuclides == "sum" or nuclides == ["sum"]: xs_tally = self.xs_tally.summation(nuclides=query_nuclides) - xs = xs_tally.get_values(scores=scores, filters=filters, - filter_bins=filter_bins, value=value) + xs = xs_tally.get_values( + scores=scores, filters=filters, filter_bins=filter_bins, value=value + ) else: - xs = self.xs_tally.get_values(scores=scores, filters=filters, - filter_bins=filter_bins, - nuclides=query_nuclides, value=value) + xs = self.xs_tally.get_values( + scores=scores, + filters=filters, + filter_bins=filter_bins, + nuclides=query_nuclides, + value=value, + ) # Divide by atom number densities for microscopic cross sections - if xs_type == 'micro' and self._divide_by_density: + if xs_type == "micro" and self._divide_by_density: if self.by_nuclide: densities = self.get_nuclide_densities(nuclides) else: - densities = self.get_nuclide_densities('sum') - if value == 'mean' or value == 'std_dev': + densities = self.get_nuclide_densities("sum") + if value == "mean" or value == "std_dev": xs /= densities[np.newaxis, :, np.newaxis] # Convert and nans to zero xs = np.nan_to_num(xs) - if in_groups == 'all': + if in_groups == "all": num_in_groups = self.num_groups else: num_in_groups = len(in_groups) - if out_groups == 'all': + if out_groups == "all": num_out_groups = self.num_groups else: num_out_groups = len(out_groups) # Reshape tally data array with separate axes for domain and energy # Accomodate the polar and azimuthal bins if needed - num_subdomains = int(xs.shape[0] / (num_angle_bins * num_in_groups * - num_out_groups * self.num_polar * - self.num_azimuthal)) + num_subdomains = int( + xs.shape[0] + / ( + num_angle_bins + * num_in_groups + * num_out_groups + * self.num_polar + * self.num_azimuthal + ) + ) if self.num_polar > 1 or self.num_azimuthal > 1: - new_shape = (self.num_polar, self.num_azimuthal, - num_subdomains, num_in_groups, num_out_groups, - num_angle_bins) + new_shape = ( + self.num_polar, + self.num_azimuthal, + num_subdomains, + num_in_groups, + num_out_groups, + num_angle_bins, + ) new_shape += xs.shape[1:] xs = np.reshape(xs, new_shape) # Transpose the scattering matrix if requested by user - if row_column == 'outin': + if row_column == "outin": xs = np.swapaxes(xs, 3, 4) # Reverse data if user requested increasing energy groups since # tally data is stored in order of increasing energies - if order_groups == 'increasing': + if order_groups == "increasing": xs = xs[:, :, :, ::-1, ::-1, ...] else: - new_shape = (num_subdomains, num_in_groups, num_out_groups, - num_angle_bins) + new_shape = (num_subdomains, num_in_groups, num_out_groups, num_angle_bins) new_shape += xs.shape[1:] xs = np.reshape(xs, new_shape) # Transpose the scattering matrix if requested by user - if row_column == 'outin': + if row_column == "outin": xs = np.swapaxes(xs, 1, 2) # Reverse data if user requested increasing energy groups since # tally data is stored in order of increasing energies - if order_groups == 'increasing': + if order_groups == "increasing": xs = xs[:, ::-1, ::-1, ...] if squeeze: @@ -4706,8 +5070,9 @@ def get_xs(self, in_groups='all', out_groups='all', xs = self._squeeze_xs(xs) return xs - def get_pandas_dataframe(self, groups='all', nuclides='all', - xs_type='macro', paths=False): + def get_pandas_dataframe( + self, groups="all", nuclides="all", xs_type="macro", paths=False + ): """Build a Pandas DataFrame for the MGXS data. This method leverages :meth:`openmc.Tally.get_pandas_dataframe`, but @@ -4747,17 +5112,15 @@ def get_pandas_dataframe(self, groups='all', nuclides='all', """ # Build the dataframe using the parent class method - df = super().get_pandas_dataframe(groups, nuclides, xs_type, - paths=paths) + df = super().get_pandas_dataframe(groups, nuclides, xs_type, paths=paths) # If the matrix is P0, remove the legendre column if self.scatter_format == SCATTER_LEGENDRE and self.legendre_order == 0: - df = df.drop(axis=1, labels=['legendre']) + df = df.drop(axis=1, labels=["legendre"]) return df - def print_xs(self, subdomains='all', nuclides='all', - xs_type='macro', moment=0): + def print_xs(self, subdomains="all", nuclides="all", xs_type="macro", moment=0): """Prints a string representation for the multi-group cross section. Parameters @@ -4782,112 +5145,122 @@ def print_xs(self, subdomains='all', nuclides='all', # Construct a collection of the subdomains to report if not isinstance(subdomains, str): - cv.check_iterable_type('subdomains', subdomains, Integral) - elif self.domain_type == 'distribcell': + cv.check_iterable_type("subdomains", subdomains, Integral) + elif self.domain_type == "distribcell": subdomains = np.arange(self.num_subdomains, dtype=int) - elif self.domain_type == 'mesh': + elif self.domain_type == "mesh": subdomains = list(self.domain.indices) else: subdomains = [self.domain.id] # Construct a collection of the nuclides to report if self.by_nuclide: - if nuclides == 'all': + if nuclides == "all": nuclides = self.get_nuclides() - if nuclides == 'sum': - nuclides = ['sum'] + if nuclides == "sum": + nuclides = ["sum"] else: - cv.check_iterable_type('nuclides', nuclides, str) + cv.check_iterable_type("nuclides", nuclides, str) else: - nuclides = ['sum'] + nuclides = ["sum"] - cv.check_value('xs_type', xs_type, ['macro', 'micro']) + cv.check_value("xs_type", xs_type, ["macro", "micro"]) - if self.correction != 'P0' and self.scatter_format == SCATTER_LEGENDRE: - rxn_type = f'{self.mgxs_type} (P{moment})' + if self.correction != "P0" and self.scatter_format == SCATTER_LEGENDRE: + rxn_type = f"{self.mgxs_type} (P{moment})" else: rxn_type = self.mgxs_type # Build header for string with type and domain info - string = 'Multi-Group XS\n' - string += '{0: <16}=\t{1}\n'.format('\tReaction Type', rxn_type) - string += '{0: <16}=\t{1}\n'.format('\tDomain Type', self.domain_type) - string += '{0: <16}=\t{1}\n'.format('\tDomain ID', self.domain.id) + string = "Multi-Group XS\n" + string += "{0: <16}=\t{1}\n".format("\tReaction Type", rxn_type) + string += "{0: <16}=\t{1}\n".format("\tDomain Type", self.domain_type) + string += "{0: <16}=\t{1}\n".format("\tDomain ID", self.domain.id) # Generate the header for an individual XS - xs_header = f'\tCross Sections [{self.get_units(xs_type)}]:' + xs_header = f"\tCross Sections [{self.get_units(xs_type)}]:" # If cross section data has not been computed, only print string header if self.tallies is None: print(string) return - string += '{0: <16}\n'.format('\tEnergy Groups:') - template = '{0: <12}Group {1} [{2: <10} - {3: <10}eV]\n' + string += "{0: <16}\n".format("\tEnergy Groups:") + template = "{0: <12}Group {1} [{2: <10} - {3: <10}eV]\n" # Loop over energy groups ranges for group in range(1, self.num_groups + 1): bounds = self.energy_groups.get_group_bounds(group) - string += template.format('', group, bounds[0], bounds[1]) + string += template.format("", group, bounds[0], bounds[1]) # Set polar and azimuthal bins if necessary if self.num_polar > 1 or self.num_azimuthal > 1: - pol_bins = np.linspace(0., np.pi, num=self.num_polar + 1, - endpoint=True) - azi_bins = np.linspace(-np.pi, np.pi, num=self.num_azimuthal + 1, - endpoint=True) + pol_bins = np.linspace(0.0, np.pi, num=self.num_polar + 1, endpoint=True) + azi_bins = np.linspace( + -np.pi, np.pi, num=self.num_azimuthal + 1, endpoint=True + ) # Loop over all subdomains for subdomain in subdomains: - if self.domain_type == 'distribcell' or self.domain_type == 'mesh': - string += '{0: <16}=\t{1}\n'.format('\tSubdomain', subdomain) + if self.domain_type == "distribcell" or self.domain_type == "mesh": + string += "{0: <16}=\t{1}\n".format("\tSubdomain", subdomain) # Loop over all Nuclides for nuclide in nuclides: # Build header for nuclide type - if xs_type != 'sum': - string += '{0: <16}=\t{1}\n'.format('\tNuclide', nuclide) + if xs_type != "sum": + string += "{0: <16}=\t{1}\n".format("\tNuclide", nuclide) # Build header for cross section type - string += f'{xs_header: <16}\n' - - average_xs = self.get_xs(nuclides=[nuclide], - subdomains=[subdomain], - xs_type=xs_type, value='mean', - moment=moment) - rel_err_xs = self.get_xs(nuclides=[nuclide], - subdomains=[subdomain], - xs_type=xs_type, value='rel_err', - moment=moment) - rel_err_xs = rel_err_xs * 100. + string += f"{xs_header: <16}\n" + + average_xs = self.get_xs( + nuclides=[nuclide], + subdomains=[subdomain], + xs_type=xs_type, + value="mean", + moment=moment, + ) + rel_err_xs = self.get_xs( + nuclides=[nuclide], + subdomains=[subdomain], + xs_type=xs_type, + value="rel_err", + moment=moment, + ) + rel_err_xs = rel_err_xs * 100.0 # Create a function for printing group and histogram data - def print_groups_and_histogram(avg_xs, err_xs, num_groups, - num_histogram_bins): - template = '{0: <12}Group {1} -> Group {2}:\t\t' + def print_groups_and_histogram( + avg_xs, err_xs, num_groups, num_histogram_bins + ): + template = "{0: <12}Group {1} -> Group {2}:\t\t" to_print = "" # Loop over incoming/outgoing energy groups ranges for in_group in range(1, num_groups + 1): for out_group in range(1, num_groups + 1): - to_print += template.format('', in_group, - out_group) + to_print += template.format("", in_group, out_group) if num_histogram_bins > 0: for i in range(num_histogram_bins): - to_print += \ - '\n{0: <16}Histogram Bin {1}:{2: <6}'.format( - '', i + 1, '') - to_print += '{0:.2e} +/- {1:.2e}%'.format( + to_print += ( + "\n{0: <16}Histogram Bin {1}:{2: <6}".format( + "", i + 1, "" + ) + ) + to_print += "{0:.2e} +/- {1:.2e}%".format( avg_xs[in_group - 1, out_group - 1, i], - err_xs[in_group - 1, out_group - 1, i]) - to_print += '\n' + err_xs[in_group - 1, out_group - 1, i], + ) + to_print += "\n" else: - to_print += '{0:.2e} +/- {1:.2e}%'.format( + to_print += "{0:.2e} +/- {1:.2e}%".format( avg_xs[in_group - 1, out_group - 1], - err_xs[in_group - 1, out_group - 1]) - to_print += '\n' - to_print += '\n' + err_xs[in_group - 1, out_group - 1], + ) + to_print += "\n" + to_print += "\n" return to_print # Set the number of histogram bins @@ -4899,24 +5272,30 @@ def print_groups_and_histogram(avg_xs, err_xs, num_groups, if self.num_polar > 1 or self.num_azimuthal > 1: # Loop over polar, azi, and in/out energy group ranges for pol in range(len(pol_bins) - 1): - pol_low, pol_high = pol_bins[pol: pol + 2] + pol_low, pol_high = pol_bins[pol : pol + 2] for azi in range(len(azi_bins) - 1): - azi_low, azi_high = azi_bins[azi: azi + 2] - string += \ - f'\t\tPolar Angle: [{pol_low:5f} - {pol_high:5f}]' + \ - '\tAzimuthal Angle: [{0:5f} - {1:5f}]'.format( - azi_low, azi_high) + '\n' + azi_low, azi_high = azi_bins[azi : azi + 2] + string += ( + f"\t\tPolar Angle: [{pol_low:5f} - {pol_high:5f}]" + + "\tAzimuthal Angle: [{0:5f} - {1:5f}]".format( + azi_low, azi_high + ) + + "\n" + ) string += print_groups_and_histogram( average_xs[pol, azi, ...], - rel_err_xs[pol, azi, ...], self.num_groups, - num_mu_bins) - string += '\n' + rel_err_xs[pol, azi, ...], + self.num_groups, + num_mu_bins, + ) + string += "\n" else: string += print_groups_and_histogram( - average_xs, rel_err_xs, self.num_groups, num_mu_bins) - string += '\n' - string += '\n' - string += '\n' + average_xs, rel_err_xs, self.num_groups, num_mu_bins + ) + string += "\n" + string += "\n" + string += "\n" print(string) @@ -4970,17 +5349,32 @@ class MultiplicityMatrixXS(MatrixMGXS): # for microscopic data _divide_by_density = False - def __init__(self, domain=None, domain_type=None, energy_groups=None, - by_nuclide=False, name='', num_polar=1, num_azimuthal=1): - super().__init__(domain, domain_type, energy_groups, by_nuclide, name, - num_polar, num_azimuthal) - self._rxn_type = 'multiplicity matrix' - self._estimator = 'analog' - self._valid_estimators = ['analog'] + def __init__( + self, + domain=None, + domain_type=None, + energy_groups=None, + by_nuclide=False, + name="", + num_polar=1, + num_azimuthal=1, + ): + super().__init__( + domain, + domain_type, + energy_groups, + by_nuclide, + name, + num_polar, + num_azimuthal, + ) + self._rxn_type = "multiplicity matrix" + self._estimator = "analog" + self._valid_estimators = ["analog"] @property def scores(self): - scores = ['nu-scatter', 'scatter'] + scores = ["nu-scatter", "scatter"] return scores @property @@ -4996,7 +5390,7 @@ def filters(self): @property def rxn_rate_tally(self): if self._rxn_rate_tally is None: - self._rxn_rate_tally = self.tallies['nu-scatter'] + self._rxn_rate_tally = self.tallies["nu-scatter"] self._rxn_rate_tally.sparse = self.sparse return self._rxn_rate_tally @@ -5004,7 +5398,7 @@ def rxn_rate_tally(self): def xs_tally(self): if self._xs_tally is None: - scatter = self.tallies['scatter'] + scatter = self.tallies["scatter"] # Compute the multiplicity self._xs_tally = self.rxn_rate_tally / scatter @@ -5059,14 +5453,29 @@ class ScatterProbabilityMatrix(MatrixMGXS): # to 1.0, this density division is not necessary _divide_by_density = False - def __init__(self, domain=None, domain_type=None, energy_groups=None, - by_nuclide=False, name='', num_polar=1, num_azimuthal=1): - super().__init__(domain, domain_type, energy_groups, by_nuclide, - name, num_polar, num_azimuthal) - self._rxn_type = 'scatter' - self._mgxs_type = 'scatter probability matrix' - self._estimator = 'analog' - self._valid_estimators = ['analog'] + def __init__( + self, + domain=None, + domain_type=None, + energy_groups=None, + by_nuclide=False, + name="", + num_polar=1, + num_azimuthal=1, + ): + super().__init__( + domain, + domain_type, + energy_groups, + by_nuclide, + name, + num_polar, + num_azimuthal, + ) + self._rxn_type = "scatter" + self._mgxs_type = "scatter probability matrix" + self._estimator = "analog" + self._valid_estimators = ["analog"] @property def scores(self): @@ -5094,7 +5503,8 @@ def xs_tally(self): if self._xs_tally is None: norm = self.rxn_rate_tally.get_slice(scores=[self.rxn_type]) norm = norm.summation( - filter_type=openmc.EnergyoutFilter, remove_filter=True) + filter_type=openmc.EnergyoutFilter, remove_filter=True + ) # Compute the group-to-group probabilities self._xs_tally = self.tallies[self.rxn_type] / norm @@ -5230,19 +5640,34 @@ class NuFissionMatrixXS(MatrixMGXS): """ - def __init__(self, domain=None, domain_type=None, energy_groups=None, - by_nuclide=False, name='', num_polar=1, - num_azimuthal=1, prompt=False): - super().__init__(domain, domain_type, energy_groups, by_nuclide, name, - num_polar, num_azimuthal) + def __init__( + self, + domain=None, + domain_type=None, + energy_groups=None, + by_nuclide=False, + name="", + num_polar=1, + num_azimuthal=1, + prompt=False, + ): + super().__init__( + domain, + domain_type, + energy_groups, + by_nuclide, + name, + num_polar, + num_azimuthal, + ) if not prompt: - self._rxn_type = 'nu-fission' - self._mgxs_type = 'nu-fission matrix' + self._rxn_type = "nu-fission" + self._mgxs_type = "nu-fission matrix" else: - self._rxn_type = 'prompt-nu-fission' - self._mgxs_type = 'prompt-nu-fission matrix' - self._estimator = 'analog' - self._valid_estimators = ['analog'] + self._rxn_type = "prompt-nu-fission" + self._mgxs_type = "prompt-nu-fission matrix" + self._estimator = "analog" + self._valid_estimators = ["analog"] self.prompt = prompt @property @@ -5251,7 +5676,7 @@ def prompt(self): @prompt.setter def prompt(self, prompt): - cv.check_type('prompt', prompt, bool) + cv.check_type("prompt", prompt, bool) self._prompt = prompt def __deepcopy__(self, memo): @@ -5396,13 +5821,28 @@ class Chi(MGXS): # data should not be divided by the number density _divide_by_density = False - def __init__(self, domain=None, domain_type=None, energy_groups=None, - prompt=False, by_nuclide=False, name='', num_polar=1, - num_azimuthal=1): - super().__init__(domain, domain_type, energy_groups, by_nuclide, name, - num_polar, num_azimuthal) - self._estimator = 'analog' - self._valid_estimators = ['analog'] + def __init__( + self, + domain=None, + domain_type=None, + energy_groups=None, + prompt=False, + by_nuclide=False, + name="", + num_polar=1, + num_azimuthal=1, + ): + super().__init__( + domain, + domain_type, + energy_groups, + by_nuclide, + name, + num_polar, + num_azimuthal, + ) + self._estimator = "analog" + self._valid_estimators = ["analog"] self.prompt = prompt def __deepcopy__(self, memo): @@ -5416,14 +5856,14 @@ def prompt(self): @prompt.setter def prompt(self, prompt): - cv.check_type('prompt', prompt, bool) + cv.check_type("prompt", prompt, bool) self._prompt = prompt if not self.prompt: - self._rxn_type = 'chi' - self._mgxs_type = 'chi' + self._rxn_type = "chi" + self._mgxs_type = "chi" else: - self._rxn_type = 'chi-prompt' - self._mgxs_type = 'chi-prompt' + self._rxn_type = "chi-prompt" + self._mgxs_type = "chi-prompt" @property def _dont_squeeze(self): @@ -5438,9 +5878,9 @@ def _dont_squeeze(self): @property def scores(self): if not self.prompt: - return ['nu-fission', 'nu-fission'] + return ["nu-fission", "nu-fission"] else: - return ['prompt-nu-fission', 'prompt-nu-fission'] + return ["prompt-nu-fission", "prompt-nu-fission"] @property def filters(self): @@ -5454,12 +5894,12 @@ def filters(self): @property def tally_keys(self): - return ['nu-fission-in', 'nu-fission-out'] + return ["nu-fission-in", "nu-fission-out"] @property def rxn_rate_tally(self): if self._rxn_rate_tally is None: - self._rxn_rate_tally = self.tallies['nu-fission-out'] + self._rxn_rate_tally = self.tallies["nu-fission-out"] self._rxn_rate_tally.sparse = self.sparse return self._rxn_rate_tally @@ -5467,7 +5907,7 @@ def rxn_rate_tally(self): def xs_tally(self): if self._xs_tally is None: - nu_fission_in = self.tallies['nu-fission-in'] + nu_fission_in = self.tallies["nu-fission-in"] # Remove coarse energy filter to keep it out of tally arithmetic energy_filter = nu_fission_in.find_filter(openmc.EnergyFilter) @@ -5501,7 +5941,7 @@ def get_homogenized_mgxs(self, other_mgxs): """ - return self._get_homogenized_mgxs(other_mgxs, 'nu-fission-in') + return self._get_homogenized_mgxs(other_mgxs, "nu-fission-in") def get_slice(self, nuclides=[], groups=[]): """Build a sliced Chi for the specified nuclides and energy groups. @@ -5531,7 +5971,7 @@ def get_slice(self, nuclides=[], groups=[]): # Temporarily remove energy filter from nu-fission-in since its # group structure will work in super MGXS.get_slice(...) method - nu_fission_in = self.tallies['nu-fission-in'] + nu_fission_in = self.tallies["nu-fission-in"] energy_filter = nu_fission_in.find_filter(openmc.EnergyFilter) nu_fission_in.remove_filter(energy_filter) @@ -5549,14 +5989,15 @@ def get_slice(self, nuclides=[], groups=[]): filter_bins = [tuple(filter_bins)] # Slice nu-fission-out tally along energyout filter - nu_fission_out = slice_xs.tallies['nu-fission-out'] + nu_fission_out = slice_xs.tallies["nu-fission-out"] tally_slice = nu_fission_out.get_slice( - filters=[openmc.EnergyoutFilter], filter_bins=filter_bins) - slice_xs._tallies['nu-fission-out'] = tally_slice + filters=[openmc.EnergyoutFilter], filter_bins=filter_bins + ) + slice_xs._tallies["nu-fission-out"] = tally_slice # Add energy filter back to nu-fission-in tallies - self.tallies['nu-fission-in'].add_filter(energy_filter) - slice_xs._tallies['nu-fission-in'].add_filter(energy_filter) + self.tallies["nu-fission-in"].add_filter(energy_filter) + slice_xs._tallies["nu-fission-in"].add_filter(energy_filter) slice_xs.sparse = self.sparse return slice_xs @@ -5579,7 +6020,7 @@ def merge(self, other): """ if not self.can_merge(other): - raise ValueError('Unable to merge a Chi MGXS') + raise ValueError("Unable to merge a Chi MGXS") # Create deep copy of tally to return as merged tally merged_mgxs = copy.deepcopy(self) @@ -5598,7 +6039,7 @@ def merge(self, other): # The nuclides must be mutually exclusive for nuclide in self.nuclides: if nuclide in other.nuclides: - msg = 'Unable to merge a Chi MGXS with shared nuclides' + msg = "Unable to merge a Chi MGXS with shared nuclides" raise ValueError(msg) # Concatenate lists of nuclides for the merged MGXS @@ -5611,9 +6052,17 @@ def merge(self, other): return merged_mgxs - def get_xs(self, groups='all', subdomains='all', nuclides='all', - xs_type='macro', order_groups='increasing', - value='mean', squeeze=True, **kwargs): + def get_xs( + self, + groups="all", + subdomains="all", + nuclides="all", + xs_type="macro", + order_groups="increasing", + value="mean", + squeeze=True, + **kwargs, + ): """Returns an array of the fission spectrum. This method constructs a 3D NumPy array for the requested @@ -5659,14 +6108,16 @@ def get_xs(self, groups='all', subdomains='all', nuclides='all', """ - cv.check_value('value', value, ['mean', 'std_dev', 'rel_err']) - cv.check_value('xs_type', xs_type, ['macro', 'micro']) + cv.check_value("value", value, ["mean", "std_dev", "rel_err"]) + cv.check_value("xs_type", xs_type, ["macro", "micro"]) # FIXME: Unable to get microscopic xs for mesh domain because the mesh # cells do not know the nuclide densities in each mesh cell. - if self.domain_type == 'mesh' and xs_type == 'micro': - msg = 'Unable to get micro xs for mesh domain since the mesh ' \ - 'cells do not know the nuclide densities in each mesh cell.' + if self.domain_type == "mesh" and xs_type == "micro": + msg = ( + "Unable to get micro xs for mesh domain since the mesh " + "cells do not know the nuclide densities in each mesh cell." + ) raise ValueError(msg) filters = [] @@ -5674,8 +6125,7 @@ def get_xs(self, groups='all', subdomains='all', nuclides='all', # Construct a collection of the domain filter bins if not isinstance(subdomains, str): - cv.check_iterable_type('subdomains', subdomains, Integral, - max_depth=3) + cv.check_iterable_type("subdomains", subdomains, Integral, max_depth=3) filters.append(_DOMAIN_TO_FILTER[self.domain_type]) subdomain_bins = [] for subdomain in subdomains: @@ -5684,12 +6134,11 @@ def get_xs(self, groups='all', subdomains='all', nuclides='all', # Construct list of energy group bounds tuples for all requested groups if not isinstance(groups, str): - cv.check_iterable_type('groups', groups, Integral) + cv.check_iterable_type("groups", groups, Integral) filters.append(openmc.EnergyoutFilter) energy_bins = [] for group in groups: - energy_bins.append( - (self.energy_groups.get_group_bounds(group),)) + energy_bins.append((self.energy_groups.get_group_bounds(group),)) filter_bins.append(tuple(energy_bins)) # If chi was computed for each nuclide in the domain @@ -5697,11 +6146,11 @@ def get_xs(self, groups='all', subdomains='all', nuclides='all', # Get the sum as the fission source weighted average chi for all # nuclides in the domain - if nuclides == 'sum' or nuclides == ['sum']: + if nuclides == "sum" or nuclides == ["sum"]: # Retrieve the fission production tallies - nu_fission_in = self.tallies['nu-fission-in'] - nu_fission_out = self.tallies['nu-fission-out'] + nu_fission_in = self.tallies["nu-fission-in"] + nu_fission_out = self.tallies["nu-fission-out"] # Sum out all nuclides nuclides = self.get_nuclides() @@ -5719,51 +6168,64 @@ def get_xs(self, groups='all', subdomains='all', nuclides='all', # Add the coarse energy filter back to the nu-fission tally nu_fission_in.filters.append(energy_filter) - xs = xs_tally.get_values(filters=filters, - filter_bins=filter_bins, value=value) + xs = xs_tally.get_values( + filters=filters, filter_bins=filter_bins, value=value + ) # Get chi for all nuclides in the domain - elif nuclides == 'all': + elif nuclides == "all": nuclides = self.get_nuclides() - xs = self.xs_tally.get_values(filters=filters, - filter_bins=filter_bins, - nuclides=nuclides, value=value) + xs = self.xs_tally.get_values( + filters=filters, + filter_bins=filter_bins, + nuclides=nuclides, + value=value, + ) # Get chi for user-specified nuclides in the domain else: - cv.check_iterable_type('nuclides', nuclides, str) - xs = self.xs_tally.get_values(filters=filters, - filter_bins=filter_bins, - nuclides=nuclides, value=value) + cv.check_iterable_type("nuclides", nuclides, str) + xs = self.xs_tally.get_values( + filters=filters, + filter_bins=filter_bins, + nuclides=nuclides, + value=value, + ) # If chi was computed as an average of nuclides in the domain else: - xs = self.xs_tally.get_values(filters=filters, - filter_bins=filter_bins, value=value) + xs = self.xs_tally.get_values( + filters=filters, filter_bins=filter_bins, value=value + ) # Eliminate the trivial score dimension xs = np.squeeze(xs, axis=len(xs.shape) - 1) xs = np.nan_to_num(xs) - if groups == 'all': + if groups == "all": num_groups = self.num_groups else: num_groups = len(groups) # Reshape tally data array with separate axes for domain and energy # Accomodate the polar and azimuthal bins if needed - num_subdomains = int(xs.shape[0] / (num_groups * self.num_polar * - self.num_azimuthal)) + num_subdomains = int( + xs.shape[0] / (num_groups * self.num_polar * self.num_azimuthal) + ) if self.num_polar > 1 or self.num_azimuthal > 1: - new_shape = (self.num_polar, self.num_azimuthal, num_subdomains, - num_groups) + xs.shape[1:] + new_shape = ( + self.num_polar, + self.num_azimuthal, + num_subdomains, + num_groups, + ) + xs.shape[1:] else: new_shape = (num_subdomains, num_groups) + xs.shape[1:] xs = np.reshape(xs, new_shape) # Reverse data if user requested increasing energy groups since # tally data is stored in order of increasing energies - if order_groups == 'increasing': + if order_groups == "increasing": xs = xs[..., ::-1, :] if squeeze: @@ -5773,7 +6235,7 @@ def get_xs(self, groups='all', subdomains='all', nuclides='all', return xs - def get_units(self, xs_type='macro'): + def get_units(self, xs_type="macro"): """Returns the units of Chi. This method returns the units of Chi, which is "%" for both macro @@ -5792,10 +6254,10 @@ def get_units(self, xs_type='macro'): """ - cv.check_value('xs_type', xs_type, ['macro', 'micro']) + cv.check_value("xs_type", xs_type, ["macro", "micro"]) # Chi has the same units (%) for both macro and micro - return '%' + return "%" @add_params @@ -5836,13 +6298,28 @@ class InverseVelocity(MGXS): # values _divide_by_density = False - def __init__(self, domain=None, domain_type=None, energy_groups=None, - by_nuclide=False, name='', num_polar=1, num_azimuthal=1): - super().__init__(domain, domain_type, energy_groups, by_nuclide, name, - num_polar, num_azimuthal) - self._rxn_type = 'inverse-velocity' - - def get_units(self, xs_type='macro'): + def __init__( + self, + domain=None, + domain_type=None, + energy_groups=None, + by_nuclide=False, + name="", + num_polar=1, + num_azimuthal=1, + ): + super().__init__( + domain, + domain_type, + energy_groups, + by_nuclide, + name, + num_polar, + num_azimuthal, + ) + self._rxn_type = "inverse-velocity" + + def get_units(self, xs_type="macro"): """Returns the units of InverseVelocity. This method returns the units of an InverseVelocity based on a desired @@ -5861,11 +6338,13 @@ def get_units(self, xs_type='macro'): """ - if xs_type == 'macro': - return 'second/cm' + if xs_type == "macro": + return "second/cm" else: - raise ValueError('Unable to return the units of InverseVelocity' - ' for xs_type other than "macro"') + raise ValueError( + "Unable to return the units of InverseVelocity" + ' for xs_type other than "macro"' + ) class MeshSurfaceMGXS(MGXS): @@ -5951,12 +6430,19 @@ class MeshSurfaceMGXS(MGXS): .. versionadded:: 0.13.1 """ - def __init__(self, domain=None, domain_type=None, energy_groups=None, - by_nuclide=False, name=''): - super(MeshSurfaceMGXS, self).__init__(domain, domain_type, energy_groups, - by_nuclide, name) - self._estimator = ['analog'] - self._valid_estimators = ['analog'] + def __init__( + self, + domain=None, + domain_type=None, + energy_groups=None, + by_nuclide=False, + name="", + ): + super(MeshSurfaceMGXS, self).__init__( + domain, domain_type, energy_groups, by_nuclide, name + ) + self._estimator = ["analog"] + self._valid_estimators = ["analog"] @property def scores(self): @@ -5968,12 +6454,12 @@ def domain(self): @domain.setter def domain(self, domain): - cv.check_type('domain', domain, openmc.RegularMesh) + cv.check_type("domain", domain, openmc.RegularMesh) self._domain = domain # Assign a domain type if self.domain_type is None: - self._domain_type = 'mesh' + self._domain_type = "mesh" @property def domain_type(self): @@ -5981,7 +6467,7 @@ def domain_type(self): @domain_type.setter def domain_type(self, domain_type): - cv.check_value('domain type', domain_type, 'mesh') + cv.check_value("domain type", domain_type, "mesh") self._domain_type = domain_type @property @@ -5998,8 +6484,10 @@ def filters(self): def xs_tally(self): if self._xs_tally is None: if self.tallies is None: - msg = 'Unable to get xs_tally since tallies have ' \ - 'not been loaded from a statepoint' + msg = ( + "Unable to get xs_tally since tallies have " + "not been loaded from a statepoint" + ) raise ValueError(msg) self._xs_tally = self.rxn_rate_tally @@ -6028,14 +6516,16 @@ def load_from_statepoint(self, statepoint): linked with a summary object. """ - cv.check_type('statepoint', statepoint, openmc.statepoint.StatePoint) + cv.check_type("statepoint", statepoint, openmc.statepoint.StatePoint) if statepoint.summary is None: - msg = 'Unable to load data from a statepoint which has not been ' \ - 'linked with a summary file' + msg = ( + "Unable to load data from a statepoint which has not been " + "linked with a summary file" + ) raise ValueError(msg) - filters= [] + filters = [] filter_bins = [] # Clear any tallies previously loaded from a statepoint @@ -6049,18 +6539,31 @@ def load_from_statepoint(self, statepoint): # The tally slicing is needed if tally merging was used for tally_type, tally in self.tallies.items(): sp_tally = statepoint.get_tally( - tally.scores, tally.filters, tally.nuclides, - estimator=tally.estimator, exact_filters=True) + tally.scores, + tally.filters, + tally.nuclides, + estimator=tally.estimator, + exact_filters=True, + ) sp_tally = sp_tally.get_slice( - tally.scores, filters, filter_bins, tally.nuclides) + tally.scores, filters, filter_bins, tally.nuclides + ) sp_tally.sparse = self.sparse self.tallies[tally_type] = sp_tally self._loaded_sp = True - def get_xs(self, groups='all', subdomains='all', nuclides='all', - xs_type='macro', order_groups='increasing', - value='mean', squeeze=True, **kwargs): + def get_xs( + self, + groups="all", + subdomains="all", + nuclides="all", + xs_type="macro", + order_groups="increasing", + value="mean", + squeeze=True, + **kwargs, + ): r"""Returns an array of multi-group cross sections. This method constructs a 3D NumPy array for the requested @@ -6101,16 +6604,15 @@ def get_xs(self, groups='all', subdomains='all', nuclides='all', computed from tally data. """ - cv.check_value('value', value, ['mean', 'std_dev', 'rel_err']) - cv.check_value('xs_type', xs_type, ['macro']) + cv.check_value("value", value, ["mean", "std_dev", "rel_err"]) + cv.check_value("xs_type", xs_type, ["macro"]) filters = [] filter_bins = [] # Construct a collection of the domain filter bins if not isinstance(subdomains, str): - cv.check_iterable_type('subdomains', subdomains, Integral, - max_depth=3) + cv.check_iterable_type("subdomains", subdomains, Integral, max_depth=3) filters.append(_DOMAIN_TO_FILTER[self.domain_type]) subdomain_bins = [] @@ -6118,24 +6620,24 @@ def get_xs(self, groups='all', subdomains='all', nuclides='all', subdomain_bins.append(subdomain) filter_bins.append(tuple(subdomain_bins)) - xs = self.xs_tally.get_values(filters=filters, - filter_bins=filter_bins, value=value) + xs = self.xs_tally.get_values( + filters=filters, filter_bins=filter_bins, value=value + ) # Construct list of energy group bounds tuples for all requested groups if not isinstance(groups, str): - cv.check_iterable_type('groups', groups, Integral) + cv.check_iterable_type("groups", groups, Integral) filters.append(openmc.EnergyFilter) energy_bins = [] for group in groups: - energy_bins.append( - (self.energy_groups.get_group_bounds(group),)) + energy_bins.append((self.energy_groups.get_group_bounds(group),)) filter_bins.append(tuple(energy_bins)) # Eliminate the trivial score dimension xs = np.squeeze(xs, axis=len(xs.shape) - 1) xs = np.nan_to_num(xs) - if groups == 'all': + if groups == "all": num_groups = self.num_groups else: num_groups = len(groups) @@ -6143,11 +6645,18 @@ def get_xs(self, groups='all', subdomains='all', nuclides='all', # Reshape tally data array with separate axes for domain and energy # Accomodate the polar and azimuthal bins if needed num_surfaces = 4 * self.domain.n_dimension - num_subdomains = int(xs.shape[0] / (num_groups * self.num_polar * - self.num_azimuthal * num_surfaces)) + num_subdomains = int( + xs.shape[0] + / (num_groups * self.num_polar * self.num_azimuthal * num_surfaces) + ) if self.num_polar > 1 or self.num_azimuthal > 1: - new_shape = (self.num_polar, self.num_azimuthal, num_subdomains, - num_groups, num_surfaces) + new_shape = ( + self.num_polar, + self.num_azimuthal, + num_subdomains, + num_groups, + num_surfaces, + ) else: new_shape = (num_subdomains, num_groups, num_surfaces) new_shape += xs.shape[1:] @@ -6155,13 +6664,14 @@ def get_xs(self, groups='all', subdomains='all', nuclides='all', for cell in range(num_subdomains): for g in range(num_groups): for s in range(num_surfaces): - new_xs[cell,g,s] = \ - xs[cell*num_surfaces*num_groups+s*num_groups+g] + new_xs[cell, g, s] = xs[ + cell * num_surfaces * num_groups + s * num_groups + g + ] xs = new_xs # Reverse data if user requested increasing energy groups since # tally data is stored in order of increasing energies - if order_groups == 'increasing': + if order_groups == "increasing": xs = xs[..., ::-1, :, :] if squeeze: @@ -6171,8 +6681,9 @@ def get_xs(self, groups='all', subdomains='all', nuclides='all', return xs - def get_pandas_dataframe(self, groups='all', nuclides='all', - xs_type='macro', paths=True): + def get_pandas_dataframe( + self, groups="all", nuclides="all", xs_type="macro", paths=True + ): """Build a Pandas DataFrame for the MGXS data. This method leverages :meth:`openmc.Tally.get_pandas_dataframe`, but @@ -6206,13 +6717,13 @@ def get_pandas_dataframe(self, groups='all', nuclides='all', """ if not isinstance(groups, str): - cv.check_iterable_type('groups', groups, Integral) - cv.check_value('xs_type', xs_type, ['macro']) + cv.check_iterable_type("groups", groups, Integral) + cv.check_value("xs_type", xs_type, ["macro"]) df = self.xs_tally.get_pandas_dataframe(paths=paths) # Remove the score column since it is homogeneous and redundant - df = df.drop('score', axis=1, level=0) + df = df.drop("score", axis=1, level=0) # Convert azimuthal, polar, energy in and energy out bin values in to # bin indices @@ -6220,24 +6731,35 @@ def get_pandas_dataframe(self, groups='all', nuclides='all', # Select out those groups the user requested if not isinstance(groups, str): - if 'group in' in df: - df = df[df['group in'].isin(groups)] - if 'group out' in df: - df = df[df['group out'].isin(groups)] + if "group in" in df: + df = df[df["group in"].isin(groups)] + if "group out" in df: + df = df[df["group out"].isin(groups)] - mesh_str = f'mesh {self.domain.id}' - col_key = (mesh_str, 'surf') + mesh_str = f"mesh {self.domain.id}" + col_key = (mesh_str, "surf") surfaces = df.pop(col_key) df.insert(len(self.domain.dimension), col_key, surfaces) if len(self.domain.dimension) == 1: - df.sort_values(by=[(mesh_str, 'x'), (mesh_str, 'surf')] - + columns, inplace=True) + df.sort_values( + by=[(mesh_str, "x"), (mesh_str, "surf")] + columns, inplace=True + ) elif len(self.domain.dimension) == 2: - df.sort_values(by=[(mesh_str, 'x'), (mesh_str, 'y'), - (mesh_str, 'surf')] + columns, inplace=True) + df.sort_values( + by=[(mesh_str, "x"), (mesh_str, "y"), (mesh_str, "surf")] + columns, + inplace=True, + ) elif len(self.domain.dimension) == 3: - df.sort_values(by=[(mesh_str, 'x'), (mesh_str, 'y'), - (mesh_str, 'z'), (mesh_str, 'surf')] + columns, inplace=True) + df.sort_values( + by=[ + (mesh_str, "x"), + (mesh_str, "y"), + (mesh_str, "z"), + (mesh_str, "surf"), + ] + + columns, + inplace=True, + ) return df @@ -6340,8 +6862,15 @@ class Current(MeshSurfaceMGXS): .. versionadded:: 0.13.1 """ - def __init__(self, domain=None, domain_type=None, - energy_groups=None, by_nuclide=False, name=''): - super(Current, self).__init__(domain, domain_type, - energy_groups, by_nuclide, name) - self._rxn_type = 'current' + def __init__( + self, + domain=None, + domain_type=None, + energy_groups=None, + by_nuclide=False, + name="", + ): + super(Current, self).__init__( + domain, domain_type, energy_groups, by_nuclide, name + ) + self._rxn_type = "current" diff --git a/openmc/mgxs_library.py b/openmc/mgxs_library.py index b840563cffc..43c7430f184 100644 --- a/openmc/mgxs_library.py +++ b/openmc/mgxs_library.py @@ -10,31 +10,30 @@ import openmc import openmc.mgxs from openmc.mgxs import SCATTER_TABULAR, SCATTER_LEGENDRE, SCATTER_HISTOGRAM -from .checkvalue import check_type, check_value, check_greater_than, \ - check_iterable_type, check_less_than, check_filetype_version +from .checkvalue import ( + check_type, + check_value, + check_greater_than, + check_iterable_type, + check_less_than, + check_filetype_version, +) ROOM_TEMPERATURE_KELVIN = 294.0 # Supported incoming particle MGXS angular treatment representations -REPRESENTATION_ISOTROPIC = 'isotropic' -REPRESENTATION_ANGLE = 'angle' -_REPRESENTATIONS = { - REPRESENTATION_ISOTROPIC, - REPRESENTATION_ANGLE -} +REPRESENTATION_ISOTROPIC = "isotropic" +REPRESENTATION_ANGLE = "angle" +_REPRESENTATIONS = {REPRESENTATION_ISOTROPIC, REPRESENTATION_ANGLE} # Supported scattering angular distribution representations -_SCATTER_TYPES = { - SCATTER_TABULAR, - SCATTER_LEGENDRE, - SCATTER_HISTOGRAM -} +_SCATTER_TYPES = {SCATTER_TABULAR, SCATTER_LEGENDRE, SCATTER_HISTOGRAM} # Number of mu points for conversion between scattering formats _NMU = 257 # Filetype name of the MGXS Library -_FILETYPE_MGXS_LIBRARY = 'mgxs' +_FILETYPE_MGXS_LIBRARY = "mgxs" # Current version of the MGXS Library Format _VERSION_MGXS_LIBRARY = 1 @@ -174,8 +173,14 @@ class XSdata: """ - def __init__(self, name, energy_groups, temperatures=[ROOM_TEMPERATURE_KELVIN], - representation=REPRESENTATION_ISOTROPIC, num_delayed_groups=0): + def __init__( + self, + name, + energy_groups, + temperatures=[ROOM_TEMPERATURE_KELVIN], + representation=REPRESENTATION_ISOTROPIC, + num_delayed_groups=0, + ): # Initialize class attributes self.name = name @@ -226,22 +231,18 @@ def __deepcopy__(self, memo): clone._total = copy.deepcopy(self._total, memo) clone._absorption = copy.deepcopy(self._absorption, memo) clone._scatter_matrix = copy.deepcopy(self._scatter_matrix, memo) - clone._multiplicity_matrix = \ - copy.deepcopy(self._multiplicity_matrix, memo) + clone._multiplicity_matrix = copy.deepcopy(self._multiplicity_matrix, memo) clone._fission = copy.deepcopy(self._fission, memo) clone._nu_fission = copy.deepcopy(self._nu_fission, memo) - clone._prompt_nu_fission = \ - copy.deepcopy(self._prompt_nu_fission, memo) - clone._delayed_nu_fission = \ - copy.deepcopy(self._delayed_nu_fission, memo) + clone._prompt_nu_fission = copy.deepcopy(self._prompt_nu_fission, memo) + clone._delayed_nu_fission = copy.deepcopy(self._delayed_nu_fission, memo) clone._kappa_fission = copy.deepcopy(self._kappa_fission, memo) clone._chi = copy.deepcopy(self._chi, memo) clone._chi_prompt = copy.deepcopy(self._chi_prompt, memo) clone._chi_delayed = copy.deepcopy(self._chi_delayed, memo) clone._beta = copy.deepcopy(self._beta, memo) clone._decay_rate = copy.deepcopy(self._decay_rate, memo) - clone._inverse_velocity = \ - copy.deepcopy(self._inverse_velocity, memo) + clone._inverse_velocity = copy.deepcopy(self._inverse_velocity, memo) clone._xs_shapes = copy.deepcopy(self._xs_shapes, memo) memo[id(self)] = clone @@ -259,7 +260,7 @@ def name(self): @name.setter def name(self, name): - check_type('name for XSdata', name, str) + check_type("name for XSdata", name, str) self._name = name @property @@ -269,10 +270,12 @@ def energy_groups(self): @energy_groups.setter def energy_groups(self, energy_groups): - check_type('energy_groups', energy_groups, openmc.mgxs.EnergyGroups) + check_type("energy_groups", energy_groups, openmc.mgxs.EnergyGroups) if energy_groups.group_edges is None: - msg = 'Unable to assign an EnergyGroups object ' \ - 'with uninitialized group edges' + msg = ( + "Unable to assign an EnergyGroups object " + "with uninitialized group edges" + ) raise ValueError(msg) self._energy_groups = energy_groups @@ -284,11 +287,14 @@ def num_delayed_groups(self): @num_delayed_groups.setter def num_delayed_groups(self, num_delayed_groups): - check_type('num_delayed_groups', num_delayed_groups, Integral) - check_less_than('num_delayed_groups', num_delayed_groups, - openmc.mgxs.MAX_DELAYED_GROUPS, equality=True) - check_greater_than('num_delayed_groups', num_delayed_groups, 0, - equality=True) + check_type("num_delayed_groups", num_delayed_groups, Integral) + check_less_than( + "num_delayed_groups", + num_delayed_groups, + openmc.mgxs.MAX_DELAYED_GROUPS, + equality=True, + ) + check_greater_than("num_delayed_groups", num_delayed_groups, 0, equality=True) self._num_delayed_groups = num_delayed_groups @property @@ -298,7 +304,7 @@ def representation(self): @representation.setter def representation(self, representation): - check_value('representation', representation, _REPRESENTATIONS) + check_value("representation", representation, _REPRESENTATIONS) self._representation = representation @property @@ -308,8 +314,8 @@ def atomic_weight_ratio(self): @atomic_weight_ratio.setter def atomic_weight_ratio(self, atomic_weight_ratio): - check_type('atomic_weight_ratio', atomic_weight_ratio, Real) - check_greater_than('atomic_weight_ratio', atomic_weight_ratio, 0.0) + check_type("atomic_weight_ratio", atomic_weight_ratio, Real) + check_greater_than("atomic_weight_ratio", atomic_weight_ratio, 0.0) self._atomic_weight_ratio = atomic_weight_ratio @property @@ -323,7 +329,7 @@ def temperatures(self): @temperatures.setter def temperatures(self, temperatures): - check_iterable_type('temperatures', temperatures, Real) + check_iterable_type("temperatures", temperatures, Real) self._temperatures = np.array(temperatures) @property @@ -333,7 +339,7 @@ def scatter_format(self): @scatter_format.setter def scatter_format(self, scatter_format): - check_value('scatter_format', scatter_format, _SCATTER_TYPES) + check_value("scatter_format", scatter_format, _SCATTER_TYPES) self._scatter_format = scatter_format @property @@ -343,8 +349,8 @@ def order(self): @order.setter def order(self, order): - check_type('order', order, Integral) - check_greater_than('order', order, 0, equality=True) + check_type("order", order, Integral) + check_greater_than("order", order, 0, equality=True) self._order = order @property @@ -354,8 +360,8 @@ def num_polar(self): @num_polar.setter def num_polar(self, num_polar): - check_type('num_polar', num_polar, Integral) - check_greater_than('num_polar', num_polar, 0) + check_type("num_polar", num_polar, Integral) + check_greater_than("num_polar", num_polar, 0) self._num_polar = num_polar @property @@ -365,8 +371,8 @@ def num_azimuthal(self): @num_azimuthal.setter def num_azimuthal(self, num_azimuthal): - check_type('num_azimuthal', num_azimuthal, Integral) - check_greater_than('num_azimuthal', num_azimuthal, 0) + check_type("num_azimuthal", num_azimuthal, Integral) + check_greater_than("num_azimuthal", num_azimuthal, 0) self._num_azimuthal = num_azimuthal @property @@ -432,7 +438,7 @@ def inverse_velocity(self): @property def num_orders(self): if self._order is None: - raise ValueError('Order has not been set.') + raise ValueError("Order has not been set.") if self._scatter_format in (None, SCATTER_LEGENDRE): return self._order + 1 @@ -447,26 +453,35 @@ def xs_shapes(self): self._xs_shapes = {} self._xs_shapes["[G]"] = (self.energy_groups.num_groups,) self._xs_shapes["[G']"] = (self.energy_groups.num_groups,) - self._xs_shapes["[G][G']"] = (self.energy_groups.num_groups, - self.energy_groups.num_groups) + self._xs_shapes["[G][G']"] = ( + self.energy_groups.num_groups, + self.energy_groups.num_groups, + ) self._xs_shapes["[DG]"] = (self.num_delayed_groups,) - self._xs_shapes["[DG][G]"] = (self.num_delayed_groups, - self.energy_groups.num_groups) - self._xs_shapes["[DG][G']"] = (self.num_delayed_groups, - self.energy_groups.num_groups) - self._xs_shapes["[DG][G][G']"] = (self.num_delayed_groups, - self.energy_groups.num_groups, - self.energy_groups.num_groups) - - self._xs_shapes["[G][G'][Order]"] \ - = (self.energy_groups.num_groups, - self.energy_groups.num_groups, self.num_orders) + self._xs_shapes["[DG][G]"] = ( + self.num_delayed_groups, + self.energy_groups.num_groups, + ) + self._xs_shapes["[DG][G']"] = ( + self.num_delayed_groups, + self.energy_groups.num_groups, + ) + self._xs_shapes["[DG][G][G']"] = ( + self.num_delayed_groups, + self.energy_groups.num_groups, + self.energy_groups.num_groups, + ) + + self._xs_shapes["[G][G'][Order]"] = ( + self.energy_groups.num_groups, + self.energy_groups.num_groups, + self.num_orders, + ) # If representation is by angle prepend num polar and num azim if self.representation == REPRESENTATION_ANGLE: for key, shapes in self._xs_shapes.items(): - self._xs_shapes[key] \ - = (self.num_polar, self.num_azimuthal) + shapes + self._xs_shapes[key] = (self.num_polar, self.num_azimuthal) + shapes return self._xs_shapes @@ -482,7 +497,7 @@ def add_temperature(self, temperature): """ - check_type('temperature', temperature, Real) + check_type("temperature", temperature, Real) temp_store = self.temperatures.tolist().append(temperature) self.temperatures = temp_store @@ -504,8 +519,8 @@ def add_temperature(self, temperature): self._inverse_velocity.append(None) def _check_temperature(self, temperature): - check_type('temperature', temperature, Real) - check_value('temperature', temperature, self.temperatures) + check_type("temperature", temperature, Real) + check_value("temperature", temperature, self.temperatures) def _temperature_index(self, temperature): return np.where(self.temperatures == temperature)[0][0] @@ -537,7 +552,7 @@ def set_total(self, total, temperature=ROOM_TEMPERATURE_KELVIN): # Convert to a numpy array so we can easily get the shape for checking total = np.asarray(total) - check_value('total shape', total.shape, shapes) + check_value("total shape", total.shape, shapes) self._check_temperature(temperature) i = self._temperature_index(temperature) @@ -566,7 +581,7 @@ def set_absorption(self, absorption, temperature=ROOM_TEMPERATURE_KELVIN): # Convert to a numpy array so we can easily get the shape for checking absorption = np.asarray(absorption) - check_value('absorption shape', absorption.shape, shapes) + check_value("absorption shape", absorption.shape, shapes) self._check_temperature(temperature) i = self._temperature_index(temperature) @@ -595,7 +610,7 @@ def set_fission(self, fission, temperature=ROOM_TEMPERATURE_KELVIN): # Convert to a numpy array so we can easily get the shape for checking fission = np.asarray(fission) - check_value('fission shape', fission.shape, shapes) + check_value("fission shape", fission.shape, shapes) self._check_temperature(temperature) i = self._temperature_index(temperature) @@ -626,7 +641,7 @@ def set_kappa_fission(self, kappa_fission, temperature=ROOM_TEMPERATURE_KELVIN): # Convert to a numpy array so we can easily get the shape for checking kappa_fission = np.asarray(kappa_fission) - check_value('kappa fission shape', kappa_fission.shape, shapes) + check_value("kappa fission shape", kappa_fission.shape, shapes) self._check_temperature(temperature) i = self._temperature_index(temperature) @@ -657,7 +672,7 @@ def set_chi(self, chi, temperature=ROOM_TEMPERATURE_KELVIN): # Convert to a numpy array so we can easily get the shape for checking chi = np.asarray(chi) - check_value('chi shape', chi.shape, shapes) + check_value("chi shape", chi.shape, shapes) self._check_temperature(temperature) i = self._temperature_index(temperature) @@ -686,7 +701,7 @@ def set_chi_prompt(self, chi_prompt, temperature=ROOM_TEMPERATURE_KELVIN): # Convert to a numpy array so we can easily get the shape for checking chi_prompt = np.asarray(chi_prompt) - check_value('chi prompt shape', chi_prompt.shape, shapes) + check_value("chi prompt shape", chi_prompt.shape, shapes) self._check_temperature(temperature) i = self._temperature_index(temperature) @@ -715,10 +730,10 @@ def set_chi_delayed(self, chi_delayed, temperature=ROOM_TEMPERATURE_KELVIN): # Convert to a numpy array so we can easily get the shape for checking chi_delayed = np.asarray(chi_delayed) - check_value('chi delayed shape', chi_delayed.shape, shapes) + check_value("chi delayed shape", chi_delayed.shape, shapes) self._check_temperature(temperature) - check_type('temperature', temperature, Real) - check_value('temperature', temperature, self.temperatures) + check_type("temperature", temperature, Real) + check_value("temperature", temperature, self.temperatures) i = self._temperature_index(temperature) self._chi_delayed[i] = chi_delayed @@ -746,7 +761,7 @@ def set_beta(self, beta, temperature=ROOM_TEMPERATURE_KELVIN): # Convert to a numpy array so we can easily get the shape for checking beta = np.asarray(beta) - check_value('beta shape', beta.shape, shapes) + check_value("beta shape", beta.shape, shapes) self._check_temperature(temperature) i = self._temperature_index(temperature) @@ -775,7 +790,7 @@ def set_decay_rate(self, decay_rate, temperature=ROOM_TEMPERATURE_KELVIN): # Convert to a numpy array so we can easily get the shape for checking decay_rate = np.asarray(decay_rate) - check_value('decay rate shape', decay_rate.shape, shapes) + check_value("decay rate shape", decay_rate.shape, shapes) self._check_temperature(temperature) i = self._temperature_index(temperature) @@ -804,15 +819,16 @@ def set_scatter_matrix(self, scatter, temperature=ROOM_TEMPERATURE_KELVIN): # Convert to a numpy array so we can easily get the shape for checking scatter = np.asarray(scatter) - check_iterable_type('scatter', scatter, Real, - max_depth=len(scatter.shape)) - check_value('scatter shape', scatter.shape, shapes) + check_iterable_type("scatter", scatter, Real, max_depth=len(scatter.shape)) + check_value("scatter shape", scatter.shape, shapes) self._check_temperature(temperature) i = self._temperature_index(temperature) self._scatter_matrix[i] = scatter - def set_multiplicity_matrix(self, multiplicity, temperature=ROOM_TEMPERATURE_KELVIN): + def set_multiplicity_matrix( + self, multiplicity, temperature=ROOM_TEMPERATURE_KELVIN + ): """This method sets the cross section for this XSdata object at the provided temperature. @@ -835,9 +851,10 @@ def set_multiplicity_matrix(self, multiplicity, temperature=ROOM_TEMPERATURE_KEL # Convert to a numpy array so we can easily get the shape for checking multiplicity = np.asarray(multiplicity) - check_iterable_type('multiplicity', multiplicity, Real, - max_depth=len(multiplicity.shape)) - check_value('multiplicity shape', multiplicity.shape, shapes) + check_iterable_type( + "multiplicity", multiplicity, Real, max_depth=len(multiplicity.shape) + ) + check_value("multiplicity shape", multiplicity.shape, shapes) self._check_temperature(temperature) i = self._temperature_index(temperature) @@ -866,16 +883,19 @@ def set_nu_fission(self, nu_fission, temperature=ROOM_TEMPERATURE_KELVIN): # Convert to a numpy array so we can easily get the shape for checking nu_fission = np.asarray(nu_fission) - check_value('nu_fission shape', nu_fission.shape, shapes) - check_iterable_type('nu_fission', nu_fission, Real, - max_depth=len(nu_fission.shape)) + check_value("nu_fission shape", nu_fission.shape, shapes) + check_iterable_type( + "nu_fission", nu_fission, Real, max_depth=len(nu_fission.shape) + ) self._check_temperature(temperature) i = self._temperature_index(temperature) self._nu_fission[i] = nu_fission self._set_fissionable(nu_fission) - def set_prompt_nu_fission(self, prompt_nu_fission, temperature=ROOM_TEMPERATURE_KELVIN): + def set_prompt_nu_fission( + self, prompt_nu_fission, temperature=ROOM_TEMPERATURE_KELVIN + ): """This method sets the cross section for this XSdata object at the provided temperature. @@ -898,16 +918,22 @@ def set_prompt_nu_fission(self, prompt_nu_fission, temperature=ROOM_TEMPERATURE_ # Convert to a numpy array so we can easily get the shape for checking prompt_nu_fission = np.asarray(prompt_nu_fission) - check_value('prompt_nu_fission shape', prompt_nu_fission.shape, shapes) - check_iterable_type('prompt_nu_fission', prompt_nu_fission, Real, - max_depth=len(prompt_nu_fission.shape)) + check_value("prompt_nu_fission shape", prompt_nu_fission.shape, shapes) + check_iterable_type( + "prompt_nu_fission", + prompt_nu_fission, + Real, + max_depth=len(prompt_nu_fission.shape), + ) self._check_temperature(temperature) i = self._temperature_index(temperature) self._prompt_nu_fission[i] = prompt_nu_fission self._set_fissionable(prompt_nu_fission) - def set_delayed_nu_fission(self, delayed_nu_fission, temperature=ROOM_TEMPERATURE_KELVIN): + def set_delayed_nu_fission( + self, delayed_nu_fission, temperature=ROOM_TEMPERATURE_KELVIN + ): """This method sets the cross section for this XSdata object at the provided temperature. @@ -930,10 +956,13 @@ def set_delayed_nu_fission(self, delayed_nu_fission, temperature=ROOM_TEMPERATUR # Convert to a numpy array so we can easily get the shape for checking delayed_nu_fission = np.asarray(delayed_nu_fission) - check_value('delayed_nu_fission shape', delayed_nu_fission.shape, - shapes) - check_iterable_type('delayed_nu_fission', delayed_nu_fission, Real, - max_depth=len(delayed_nu_fission.shape)) + check_value("delayed_nu_fission shape", delayed_nu_fission.shape, shapes) + check_iterable_type( + "delayed_nu_fission", + delayed_nu_fission, + Real, + max_depth=len(delayed_nu_fission.shape), + ) self._check_temperature(temperature) i = self._temperature_index(temperature) @@ -959,14 +988,20 @@ def set_inverse_velocity(self, inv_vel, temperature=ROOM_TEMPERATURE_KELVIN): # Convert to a numpy array so we can easily get the shape for checking inv_vel = np.asarray(inv_vel) - check_value('inverse_velocity shape', inv_vel.shape, shapes) + check_value("inverse_velocity shape", inv_vel.shape, shapes) self._check_temperature(temperature) i = self._temperature_index(temperature) self._inverse_velocity[i] = inv_vel - def set_total_mgxs(self, total, temperature=ROOM_TEMPERATURE_KELVIN, nuclide='total', - xs_type='macro', subdomain=None): + def set_total_mgxs( + self, + total, + temperature=ROOM_TEMPERATURE_KELVIN, + nuclide="total", + xs_type="macro", + subdomain=None, + ): """This method allows for an openmc.mgxs.TotalXS or openmc.mgxs.TransportXS to be used to set the total cross section for this XSdata object. @@ -996,18 +1031,24 @@ def set_total_mgxs(self, total, temperature=ROOM_TEMPERATURE_KELVIN, nuclide='to """ - check_type('total', total, (openmc.mgxs.TotalXS, - openmc.mgxs.TransportXS)) - check_value('energy_groups', total.energy_groups, [self.energy_groups]) - check_value('domain_type', total.domain_type, openmc.mgxs.DOMAIN_TYPES) + check_type("total", total, (openmc.mgxs.TotalXS, openmc.mgxs.TransportXS)) + check_value("energy_groups", total.energy_groups, [self.energy_groups]) + check_value("domain_type", total.domain_type, openmc.mgxs.DOMAIN_TYPES) self._check_temperature(temperature) i = self._temperature_index(temperature) - self._total[i] = total.get_xs(nuclides=nuclide, xs_type=xs_type, - subdomains=subdomain) - - def set_absorption_mgxs(self, absorption, temperature=ROOM_TEMPERATURE_KELVIN, - nuclide='total', xs_type='macro', subdomain=None): + self._total[i] = total.get_xs( + nuclides=nuclide, xs_type=xs_type, subdomains=subdomain + ) + + def set_absorption_mgxs( + self, + absorption, + temperature=ROOM_TEMPERATURE_KELVIN, + nuclide="total", + xs_type="macro", + subdomain=None, + ): """This method allows for an openmc.mgxs.AbsorptionXS to be used to set the absorption cross section for this XSdata object. @@ -1036,20 +1077,24 @@ def set_absorption_mgxs(self, absorption, temperature=ROOM_TEMPERATURE_KELVIN, """ - check_type('absorption', absorption, openmc.mgxs.AbsorptionXS) - check_value('energy_groups', absorption.energy_groups, - [self.energy_groups]) - check_value('domain_type', absorption.domain_type, - openmc.mgxs.DOMAIN_TYPES) + check_type("absorption", absorption, openmc.mgxs.AbsorptionXS) + check_value("energy_groups", absorption.energy_groups, [self.energy_groups]) + check_value("domain_type", absorption.domain_type, openmc.mgxs.DOMAIN_TYPES) self._check_temperature(temperature) i = self._temperature_index(temperature) - self._absorption[i] = absorption.get_xs(nuclides=nuclide, - xs_type=xs_type, - subdomains=subdomain) - - def set_fission_mgxs(self, fission, temperature=ROOM_TEMPERATURE_KELVIN, nuclide='total', - xs_type='macro', subdomain=None): + self._absorption[i] = absorption.get_xs( + nuclides=nuclide, xs_type=xs_type, subdomains=subdomain + ) + + def set_fission_mgxs( + self, + fission, + temperature=ROOM_TEMPERATURE_KELVIN, + nuclide="total", + xs_type="macro", + subdomain=None, + ): """This method allows for an openmc.mgxs.FissionXS to be used to set the fission cross section for this XSdata object. @@ -1078,20 +1123,24 @@ def set_fission_mgxs(self, fission, temperature=ROOM_TEMPERATURE_KELVIN, nuclide """ - check_type('fission', fission, openmc.mgxs.FissionXS) - check_value('energy_groups', fission.energy_groups, - [self.energy_groups]) - check_value('domain_type', fission.domain_type, - openmc.mgxs.DOMAIN_TYPES) + check_type("fission", fission, openmc.mgxs.FissionXS) + check_value("energy_groups", fission.energy_groups, [self.energy_groups]) + check_value("domain_type", fission.domain_type, openmc.mgxs.DOMAIN_TYPES) self._check_temperature(temperature) i = self._temperature_index(temperature) - self._fission[i] = fission.get_xs(nuclides=nuclide, - xs_type=xs_type, - subdomains=subdomain) - - def set_nu_fission_mgxs(self, nu_fission, temperature=ROOM_TEMPERATURE_KELVIN, - nuclide='total', xs_type='macro', subdomain=None): + self._fission[i] = fission.get_xs( + nuclides=nuclide, xs_type=xs_type, subdomains=subdomain + ) + + def set_nu_fission_mgxs( + self, + nu_fission, + temperature=ROOM_TEMPERATURE_KELVIN, + nuclide="total", + xs_type="macro", + subdomain=None, + ): """This method allows for an openmc.mgxs.FissionXS to be used to set the nu-fission cross section for this XSdata object. @@ -1120,26 +1169,32 @@ def set_nu_fission_mgxs(self, nu_fission, temperature=ROOM_TEMPERATURE_KELVIN, """ - check_type('nu_fission', nu_fission, (openmc.mgxs.FissionXS, - openmc.mgxs.NuFissionMatrixXS)) + check_type( + "nu_fission", + nu_fission, + (openmc.mgxs.FissionXS, openmc.mgxs.NuFissionMatrixXS), + ) if isinstance(nu_fission, openmc.mgxs.FissionXS): - check_value('nu', nu_fission.nu, [True]) - check_value('energy_groups', nu_fission.energy_groups, - [self.energy_groups]) - check_value('domain_type', nu_fission.domain_type, - openmc.mgxs.DOMAIN_TYPES) + check_value("nu", nu_fission.nu, [True]) + check_value("energy_groups", nu_fission.energy_groups, [self.energy_groups]) + check_value("domain_type", nu_fission.domain_type, openmc.mgxs.DOMAIN_TYPES) self._check_temperature(temperature) i = self._temperature_index(temperature) - self._nu_fission[i] = nu_fission.get_xs(nuclides=nuclide, - xs_type=xs_type, - subdomains=subdomain) + self._nu_fission[i] = nu_fission.get_xs( + nuclides=nuclide, xs_type=xs_type, subdomains=subdomain + ) self._set_fissionable(self._nu_fission) - def set_prompt_nu_fission_mgxs(self, prompt_nu_fission, temperature=ROOM_TEMPERATURE_KELVIN, - nuclide='total', xs_type='macro', - subdomain=None): + def set_prompt_nu_fission_mgxs( + self, + prompt_nu_fission, + temperature=ROOM_TEMPERATURE_KELVIN, + nuclide="total", + xs_type="macro", + subdomain=None, + ): """Sets the prompt-nu-fission cross section. This method allows for an openmc.mgxs.FissionXS or @@ -1171,24 +1226,35 @@ def set_prompt_nu_fission_mgxs(self, prompt_nu_fission, temperature=ROOM_TEMPERA """ - check_type('prompt_nu_fission', prompt_nu_fission, - (openmc.mgxs.FissionXS, openmc.mgxs.NuFissionMatrixXS)) - check_value('prompt', prompt_nu_fission.prompt, [True]) - check_value('energy_groups', prompt_nu_fission.energy_groups, - [self.energy_groups]) - check_value('domain_type', prompt_nu_fission.domain_type, - openmc.mgxs.DOMAIN_TYPES) + check_type( + "prompt_nu_fission", + prompt_nu_fission, + (openmc.mgxs.FissionXS, openmc.mgxs.NuFissionMatrixXS), + ) + check_value("prompt", prompt_nu_fission.prompt, [True]) + check_value( + "energy_groups", prompt_nu_fission.energy_groups, [self.energy_groups] + ) + check_value( + "domain_type", prompt_nu_fission.domain_type, openmc.mgxs.DOMAIN_TYPES + ) self._check_temperature(temperature) i = self._temperature_index(temperature) self._prompt_nu_fission[i] = prompt_nu_fission.get_xs( - nuclides=nuclide, xs_type=xs_type, subdomains=subdomain) + nuclides=nuclide, xs_type=xs_type, subdomains=subdomain + ) self._set_fissionable(self._prompt_nu_fission) - def set_delayed_nu_fission_mgxs(self, delayed_nu_fission, temperature=ROOM_TEMPERATURE_KELVIN, - nuclide='total', xs_type='macro', - subdomain=None): + def set_delayed_nu_fission_mgxs( + self, + delayed_nu_fission, + temperature=ROOM_TEMPERATURE_KELVIN, + nuclide="total", + xs_type="macro", + subdomain=None, + ): """This method allows for an openmc.mgxs.DelayedNuFissionXS or openmc.mgxs.DelayedNuFissionMatrixXS to be used to set the delayed-nu-fission cross section for this XSdata object. @@ -1218,26 +1284,39 @@ def set_delayed_nu_fission_mgxs(self, delayed_nu_fission, temperature=ROOM_TEMPE """ - check_type('delayed_nu_fission', delayed_nu_fission, - (openmc.mgxs.DelayedNuFissionXS, - openmc.mgxs.DelayedNuFissionMatrixXS)) - check_value('energy_groups', delayed_nu_fission.energy_groups, - [self.energy_groups]) - check_value('num_delayed_groups', delayed_nu_fission.num_delayed_groups, - [self.num_delayed_groups]) - check_value('domain_type', delayed_nu_fission.domain_type, - openmc.mgxs.DOMAIN_TYPES) + check_type( + "delayed_nu_fission", + delayed_nu_fission, + (openmc.mgxs.DelayedNuFissionXS, openmc.mgxs.DelayedNuFissionMatrixXS), + ) + check_value( + "energy_groups", delayed_nu_fission.energy_groups, [self.energy_groups] + ) + check_value( + "num_delayed_groups", + delayed_nu_fission.num_delayed_groups, + [self.num_delayed_groups], + ) + check_value( + "domain_type", delayed_nu_fission.domain_type, openmc.mgxs.DOMAIN_TYPES + ) self._check_temperature(temperature) i = self._temperature_index(temperature) self._delayed_nu_fission[i] = delayed_nu_fission.get_xs( - nuclides=nuclide, xs_type=xs_type, subdomains=subdomain) + nuclides=nuclide, xs_type=xs_type, subdomains=subdomain + ) self._set_fissionable(self._delayed_nu_fission) - def set_kappa_fission_mgxs(self, k_fission, temperature=ROOM_TEMPERATURE_KELVIN, - nuclide='total', xs_type='macro', - subdomain=None): + def set_kappa_fission_mgxs( + self, + k_fission, + temperature=ROOM_TEMPERATURE_KELVIN, + nuclide="total", + xs_type="macro", + subdomain=None, + ): """This method allows for an openmc.mgxs.KappaFissionXS to be used to set the kappa-fission cross section for this XSdata object. @@ -1267,20 +1346,24 @@ def set_kappa_fission_mgxs(self, k_fission, temperature=ROOM_TEMPERATURE_KELVIN, """ - check_type('kappa_fission', k_fission, openmc.mgxs.KappaFissionXS) - check_value('energy_groups', k_fission.energy_groups, - [self.energy_groups]) - check_value('domain_type', k_fission.domain_type, - openmc.mgxs.DOMAIN_TYPES) + check_type("kappa_fission", k_fission, openmc.mgxs.KappaFissionXS) + check_value("energy_groups", k_fission.energy_groups, [self.energy_groups]) + check_value("domain_type", k_fission.domain_type, openmc.mgxs.DOMAIN_TYPES) self._check_temperature(temperature) i = self._temperature_index(temperature) - self._kappa_fission[i] = k_fission.get_xs(nuclides=nuclide, - xs_type=xs_type, - subdomains=subdomain) - - def set_chi_mgxs(self, chi, temperature=ROOM_TEMPERATURE_KELVIN, nuclide='total', - xs_type='macro', subdomain=None): + self._kappa_fission[i] = k_fission.get_xs( + nuclides=nuclide, xs_type=xs_type, subdomains=subdomain + ) + + def set_chi_mgxs( + self, + chi, + temperature=ROOM_TEMPERATURE_KELVIN, + nuclide="total", + xs_type="macro", + subdomain=None, + ): """This method allows for an openmc.mgxs.Chi to be used to set chi for this XSdata object. @@ -1308,17 +1391,24 @@ def set_chi_mgxs(self, chi, temperature=ROOM_TEMPERATURE_KELVIN, nuclide='total' """ - check_type('chi', chi, openmc.mgxs.Chi) - check_value('energy_groups', chi.energy_groups, [self.energy_groups]) - check_value('domain_type', chi.domain_type, openmc.mgxs.DOMAIN_TYPES) + check_type("chi", chi, openmc.mgxs.Chi) + check_value("energy_groups", chi.energy_groups, [self.energy_groups]) + check_value("domain_type", chi.domain_type, openmc.mgxs.DOMAIN_TYPES) self._check_temperature(temperature) i = self._temperature_index(temperature) - self._chi[i] = chi.get_xs(nuclides=nuclide, xs_type=xs_type, - subdomains=subdomain) - - def set_chi_prompt_mgxs(self, chi_prompt, temperature=ROOM_TEMPERATURE_KELVIN, - nuclide='total', xs_type='macro', subdomain=None): + self._chi[i] = chi.get_xs( + nuclides=nuclide, xs_type=xs_type, subdomains=subdomain + ) + + def set_chi_prompt_mgxs( + self, + chi_prompt, + temperature=ROOM_TEMPERATURE_KELVIN, + nuclide="total", + xs_type="macro", + subdomain=None, + ): """This method allows for an openmc.mgxs.Chi to be used to set chi-prompt for this XSdata object. @@ -1346,21 +1436,25 @@ def set_chi_prompt_mgxs(self, chi_prompt, temperature=ROOM_TEMPERATURE_KELVIN, """ - check_type('chi_prompt', chi_prompt, openmc.mgxs.Chi) - check_value('prompt', chi_prompt.prompt, [True]) - check_value('energy_groups', chi_prompt.energy_groups, - [self.energy_groups]) - check_value('domain_type', chi_prompt.domain_type, - openmc.mgxs.DOMAIN_TYPES) + check_type("chi_prompt", chi_prompt, openmc.mgxs.Chi) + check_value("prompt", chi_prompt.prompt, [True]) + check_value("energy_groups", chi_prompt.energy_groups, [self.energy_groups]) + check_value("domain_type", chi_prompt.domain_type, openmc.mgxs.DOMAIN_TYPES) self._check_temperature(temperature) i = self._temperature_index(temperature) - self._chi_prompt[i] = chi_prompt.get_xs(nuclides=nuclide, - xs_type=xs_type, - subdomains=subdomain) - - def set_chi_delayed_mgxs(self, chi_delayed, temperature=ROOM_TEMPERATURE_KELVIN, - nuclide='total', xs_type='macro', subdomain=None): + self._chi_prompt[i] = chi_prompt.get_xs( + nuclides=nuclide, xs_type=xs_type, subdomains=subdomain + ) + + def set_chi_delayed_mgxs( + self, + chi_delayed, + temperature=ROOM_TEMPERATURE_KELVIN, + nuclide="total", + xs_type="macro", + subdomain=None, + ): """This method allows for an openmc.mgxs.ChiDelayed to be used to set chi-delayed for this XSdata object. @@ -1388,22 +1482,29 @@ def set_chi_delayed_mgxs(self, chi_delayed, temperature=ROOM_TEMPERATURE_KELVIN, """ - check_type('chi_delayed', chi_delayed, openmc.mgxs.ChiDelayed) - check_value('energy_groups', chi_delayed.energy_groups, - [self.energy_groups]) - check_value('num_delayed_groups', chi_delayed.num_delayed_groups, - [self.num_delayed_groups]) - check_value('domain_type', chi_delayed.domain_type, - openmc.mgxs.DOMAIN_TYPES) + check_type("chi_delayed", chi_delayed, openmc.mgxs.ChiDelayed) + check_value("energy_groups", chi_delayed.energy_groups, [self.energy_groups]) + check_value( + "num_delayed_groups", + chi_delayed.num_delayed_groups, + [self.num_delayed_groups], + ) + check_value("domain_type", chi_delayed.domain_type, openmc.mgxs.DOMAIN_TYPES) self._check_temperature(temperature) i = self._temperature_index(temperature) - self._chi_delayed[i] = chi_delayed.get_xs(nuclides=nuclide, - xs_type=xs_type, - subdomains=subdomain) - - def set_beta_mgxs(self, beta, temperature=ROOM_TEMPERATURE_KELVIN, - nuclide='total', xs_type='macro', subdomain=None): + self._chi_delayed[i] = chi_delayed.get_xs( + nuclides=nuclide, xs_type=xs_type, subdomains=subdomain + ) + + def set_beta_mgxs( + self, + beta, + temperature=ROOM_TEMPERATURE_KELVIN, + nuclide="total", + xs_type="macro", + subdomain=None, + ): """This method allows for an openmc.mgxs.Beta to be used to set beta for this XSdata object. @@ -1431,19 +1532,26 @@ def set_beta_mgxs(self, beta, temperature=ROOM_TEMPERATURE_KELVIN, """ - check_type('beta', beta, openmc.mgxs.Beta) - check_value('num_delayed_groups', beta.num_delayed_groups, - [self.num_delayed_groups]) - check_value('domain_type', beta.domain_type, openmc.mgxs.DOMAIN_TYPES) + check_type("beta", beta, openmc.mgxs.Beta) + check_value( + "num_delayed_groups", beta.num_delayed_groups, [self.num_delayed_groups] + ) + check_value("domain_type", beta.domain_type, openmc.mgxs.DOMAIN_TYPES) self._check_temperature(temperature) i = self._temperature_index(temperature) - self._beta[i] = beta.get_xs(nuclides=nuclide, - xs_type=xs_type, - subdomains=subdomain) - - def set_decay_rate_mgxs(self, decay_rate, temperature=ROOM_TEMPERATURE_KELVIN, - nuclide='total', xs_type='macro', subdomain=None): + self._beta[i] = beta.get_xs( + nuclides=nuclide, xs_type=xs_type, subdomains=subdomain + ) + + def set_decay_rate_mgxs( + self, + decay_rate, + temperature=ROOM_TEMPERATURE_KELVIN, + nuclide="total", + xs_type="macro", + subdomain=None, + ): """This method allows for an openmc.mgxs.DecayRate to be used to set decay rate for this XSdata object. @@ -1471,21 +1579,28 @@ def set_decay_rate_mgxs(self, decay_rate, temperature=ROOM_TEMPERATURE_KELVIN, """ - check_type('decay_rate', decay_rate, openmc.mgxs.DecayRate) - check_value('num_delayed_groups', decay_rate.num_delayed_groups, - [self.num_delayed_groups]) - check_value('domain_type', decay_rate.domain_type, - openmc.mgxs.DOMAIN_TYPES) + check_type("decay_rate", decay_rate, openmc.mgxs.DecayRate) + check_value( + "num_delayed_groups", + decay_rate.num_delayed_groups, + [self.num_delayed_groups], + ) + check_value("domain_type", decay_rate.domain_type, openmc.mgxs.DOMAIN_TYPES) self._check_temperature(temperature) i = self._temperature_index(temperature) - self._decay_rate[i] = decay_rate.get_xs(nuclides=nuclide, - xs_type=xs_type, - subdomains=subdomain) - - def set_scatter_matrix_mgxs(self, scatter, temperature=ROOM_TEMPERATURE_KELVIN, - nuclide='total', xs_type='macro', - subdomain=None): + self._decay_rate[i] = decay_rate.get_xs( + nuclides=nuclide, xs_type=xs_type, subdomains=subdomain + ) + + def set_scatter_matrix_mgxs( + self, + scatter, + temperature=ROOM_TEMPERATURE_KELVIN, + nuclide="total", + xs_type="macro", + subdomain=None, + ): """This method allows for an openmc.mgxs.ScatterMatrixXS to be used to set the scatter matrix cross section for this XSdata object. If the XSdata.order attribute has not yet been set, then @@ -1516,11 +1631,9 @@ def set_scatter_matrix_mgxs(self, scatter, temperature=ROOM_TEMPERATURE_KELVIN, """ - check_type('scatter', scatter, openmc.mgxs.ScatterMatrixXS) - check_value('energy_groups', scatter.energy_groups, - [self.energy_groups]) - check_value('domain_type', scatter.domain_type, - openmc.mgxs.DOMAIN_TYPES) + check_type("scatter", scatter, openmc.mgxs.ScatterMatrixXS) + check_value("energy_groups", scatter.energy_groups, [self.energy_groups]) + check_value("domain_type", scatter.domain_type, openmc.mgxs.DOMAIN_TYPES) self._check_temperature(temperature) # Set the value of scatter_format based on the same value within @@ -1535,38 +1648,47 @@ def set_scatter_matrix_mgxs(self, scatter, temperature=ROOM_TEMPERATURE_KELVIN, if self.order is None: self.order = scatter.legendre_order else: - check_value('legendre_order', scatter.legendre_order, - [self.order]) + check_value("legendre_order", scatter.legendre_order, [self.order]) elif self.scatter_format == SCATTER_HISTOGRAM: if self.order is None: self.order = scatter.histogram_bins else: - check_value('histogram_bins', scatter.histogram_bins, - [self.order]) + check_value("histogram_bins", scatter.histogram_bins, [self.order]) i = self._temperature_index(temperature) if self.scatter_format == SCATTER_LEGENDRE: - self._scatter_matrix[i] = \ - np.zeros(self.xs_shapes["[G][G'][Order]"]) + self._scatter_matrix[i] = np.zeros(self.xs_shapes["[G][G'][Order]"]) # Get the scattering orders in the outermost dimension if self.representation == REPRESENTATION_ISOTROPIC: for moment in range(self.num_orders): - self._scatter_matrix[i][:, :, moment] = \ - scatter.get_xs(nuclides=nuclide, xs_type=xs_type, - moment=moment, subdomains=subdomain) + self._scatter_matrix[i][:, :, moment] = scatter.get_xs( + nuclides=nuclide, + xs_type=xs_type, + moment=moment, + subdomains=subdomain, + ) elif self.representation == REPRESENTATION_ANGLE: for moment in range(self.num_orders): - self._scatter_matrix[i][:, :, :, :, moment] = \ - scatter.get_xs(nuclides=nuclide, xs_type=xs_type, - moment=moment, subdomains=subdomain) + self._scatter_matrix[i][:, :, :, :, moment] = scatter.get_xs( + nuclides=nuclide, + xs_type=xs_type, + moment=moment, + subdomains=subdomain, + ) else: - self._scatter_matrix[i] = \ - scatter.get_xs(nuclides=nuclide, xs_type=xs_type, - subdomains=subdomain) - - def set_multiplicity_matrix_mgxs(self, nuscatter, scatter=None, - temperature=ROOM_TEMPERATURE_KELVIN, nuclide='total', - xs_type='macro', subdomain=None): + self._scatter_matrix[i] = scatter.get_xs( + nuclides=nuclide, xs_type=xs_type, subdomains=subdomain + ) + + def set_multiplicity_matrix_mgxs( + self, + nuscatter, + scatter=None, + temperature=ROOM_TEMPERATURE_KELVIN, + nuclide="total", + xs_type="macro", + subdomain=None, + ): """This method allows for either the direct use of only an openmc.mgxs.MultiplicityMatrixXS or an openmc.mgxs.ScatterMatrixXS and openmc.mgxs.ScatterMatrixXS to be used to set the scattering @@ -1604,47 +1726,52 @@ def set_multiplicity_matrix_mgxs(self, nuscatter, scatter=None, """ - check_type('nuscatter', nuscatter, (openmc.mgxs.ScatterMatrixXS, - openmc.mgxs.MultiplicityMatrixXS)) - check_value('energy_groups', nuscatter.energy_groups, - [self.energy_groups]) - check_value('domain_type', nuscatter.domain_type, - openmc.mgxs.DOMAIN_TYPES) + check_type( + "nuscatter", + nuscatter, + (openmc.mgxs.ScatterMatrixXS, openmc.mgxs.MultiplicityMatrixXS), + ) + check_value("energy_groups", nuscatter.energy_groups, [self.energy_groups]) + check_value("domain_type", nuscatter.domain_type, openmc.mgxs.DOMAIN_TYPES) self._check_temperature(temperature) if scatter is not None: - check_type('scatter', scatter, openmc.mgxs.ScatterMatrixXS) + check_type("scatter", scatter, openmc.mgxs.ScatterMatrixXS) if isinstance(nuscatter, openmc.mgxs.MultiplicityMatrixXS): - msg = 'Either an MultiplicityMatrixXS object must be passed ' \ - 'for "nuscatter" or the "scatter" argument must be ' \ - 'provided.' + msg = ( + "Either an MultiplicityMatrixXS object must be passed " + 'for "nuscatter" or the "scatter" argument must be ' + "provided." + ) raise ValueError(msg) - check_value('energy_groups', scatter.energy_groups, - [self.energy_groups]) - check_value('domain_type', scatter.domain_type, - openmc.mgxs.DOMAIN_TYPES) + check_value("energy_groups", scatter.energy_groups, [self.energy_groups]) + check_value("domain_type", scatter.domain_type, openmc.mgxs.DOMAIN_TYPES) i = self._temperature_index(temperature) - nuscatt = nuscatter.get_xs(nuclides=nuclide, - xs_type=xs_type, moment=0, - subdomains=subdomain) + nuscatt = nuscatter.get_xs( + nuclides=nuclide, xs_type=xs_type, moment=0, subdomains=subdomain + ) if isinstance(nuscatter, openmc.mgxs.MultiplicityMatrixXS): self._multiplicity_matrix[i] = nuscatt else: - scatt = scatter.get_xs(nuclides=nuclide, - xs_type=xs_type, moment=0, - subdomains=subdomain) + scatt = scatter.get_xs( + nuclides=nuclide, xs_type=xs_type, moment=0, subdomains=subdomain + ) if scatter.scatter_format == SCATTER_HISTOGRAM: scatt = np.sum(scatt, axis=2) if nuscatter.scatter_format == SCATTER_HISTOGRAM: nuscatt = np.sum(nuscatt, axis=2) self._multiplicity_matrix[i] = np.divide(nuscatt, scatt) - self._multiplicity_matrix[i] = \ - np.nan_to_num(self._multiplicity_matrix[i]) + self._multiplicity_matrix[i] = np.nan_to_num(self._multiplicity_matrix[i]) - def set_inverse_velocity_mgxs(self, inverse_velocity, temperature=ROOM_TEMPERATURE_KELVIN, - nuclide='total', xs_type='macro', - subdomain=None): + def set_inverse_velocity_mgxs( + self, + inverse_velocity, + temperature=ROOM_TEMPERATURE_KELVIN, + nuclide="total", + xs_type="macro", + subdomain=None, + ): """This method allows for an openmc.mgxs.InverseVelocity to be used to set the inverse velocity for this XSdata object. @@ -1673,20 +1800,23 @@ def set_inverse_velocity_mgxs(self, inverse_velocity, temperature=ROOM_TEMPERATU """ - check_type('inverse_velocity', inverse_velocity, - openmc.mgxs.InverseVelocity) - check_value('energy_groups', inverse_velocity.energy_groups, - [self.energy_groups]) - check_value('domain_type', inverse_velocity.domain_type, - openmc.mgxs.DOMAIN_TYPES) + check_type("inverse_velocity", inverse_velocity, openmc.mgxs.InverseVelocity) + check_value( + "energy_groups", inverse_velocity.energy_groups, [self.energy_groups] + ) + check_value( + "domain_type", inverse_velocity.domain_type, openmc.mgxs.DOMAIN_TYPES + ) self._check_temperature(temperature) i = self._temperature_index(temperature) self._inverse_velocity[i] = inverse_velocity.get_xs( - nuclides=nuclide, xs_type=xs_type, subdomains=subdomain) + nuclides=nuclide, xs_type=xs_type, subdomains=subdomain + ) - def convert_representation(self, target_representation, num_polar=None, - num_azimuthal=None): + def convert_representation( + self, target_representation, num_polar=None, num_azimuthal=None + ): """Produce a new XSdata object with the same data, but converted to the new representation (isotropic or angle-dependent). @@ -1720,13 +1850,12 @@ def convert_representation(self, target_representation, num_polar=None, """ - check_value('target_representation', target_representation, - _REPRESENTATIONS) + check_value("target_representation", target_representation, _REPRESENTATIONS) if target_representation == REPRESENTATION_ANGLE: - check_type('num_polar', num_polar, Integral) - check_type('num_azimuthal', num_azimuthal, Integral) - check_greater_than('num_polar', num_polar, 0) - check_greater_than('num_azimuthal', num_azimuthal, 0) + check_type("num_polar", num_polar, Integral) + check_type("num_azimuthal", num_azimuthal, Integral) + check_greater_than("num_polar", num_polar, 0) + check_greater_than("num_azimuthal", num_azimuthal, 0) xsdata = copy.deepcopy(self) @@ -1736,9 +1865,11 @@ def convert_representation(self, target_representation, num_polar=None, # Check to make sure the num_polar and num_azimuthal values match if target_representation == REPRESENTATION_ANGLE: if num_polar != self.num_polar or num_azimuthal != self.num_azimuthal: - raise ValueError("Cannot translate between `angle`" - " representations with different angle" - " bin structures") + raise ValueError( + "Cannot translate between `angle`" + " representations with different angle" + " bin structures" + ) # Nothing to do as the same structure was requested return xsdata @@ -1758,16 +1889,28 @@ def convert_representation(self, target_representation, num_polar=None, xsdata._xs_shapes = None for i, temp in enumerate(xsdata.temperatures): - for xs in ['total', 'absorption', 'fission', 'nu_fission', - 'scatter_matrix', 'multiplicity_matrix', - 'prompt_nu_fission', 'delayed_nu_fission', - 'kappa_fission', 'chi', 'chi_prompt', 'chi_delayed', - 'beta', 'decay_rate', 'inverse_velocity']: + for xs in [ + "total", + "absorption", + "fission", + "nu_fission", + "scatter_matrix", + "multiplicity_matrix", + "prompt_nu_fission", + "delayed_nu_fission", + "kappa_fission", + "chi", + "chi_prompt", + "chi_delayed", + "beta", + "decay_rate", + "inverse_velocity", + ]: # Get the original data - orig_data = getattr(self, '_' + xs)[i] + orig_data = getattr(self, "_" + xs)[i] if orig_data is not None: - if target_representation == 'isotropic': + if target_representation == "isotropic": # Since we are going from angle to isotropic, the # current data is just the average over the angle bins new_data = orig_data.mean(axis=(0, 1)) @@ -1775,11 +1918,10 @@ def convert_representation(self, target_representation, num_polar=None, elif target_representation == REPRESENTATION_ANGLE: # Since we are going from isotropic to angle, the # current data is just copied for every angle bin - new_shape = (num_polar, num_azimuthal) + \ - orig_data.shape + new_shape = (num_polar, num_azimuthal) + orig_data.shape new_data = np.resize(orig_data, new_shape) - setter = getattr(xsdata, 'set_' + xs) + setter = getattr(xsdata, "set_" + xs) setter(new_data, temp) return xsdata @@ -1805,12 +1947,12 @@ def convert_scatter_format(self, target_format, target_order=None): """ - check_value('target_format', target_format, _SCATTER_TYPES) - check_type('target_order', target_order, Integral) + check_value("target_format", target_format, _SCATTER_TYPES) + check_type("target_order", target_order, Integral) if target_format == SCATTER_LEGENDRE: - check_greater_than('target_order', target_order, 0, equality=True) + check_greater_than("target_order", target_order, 0, equality=True) else: - check_greater_than('target_order', target_order, 0) + check_greater_than("target_order", target_order, 0) xsdata = copy.deepcopy(self) xsdata.scatter_format = target_format @@ -1820,7 +1962,7 @@ def convert_scatter_format(self, target_format, target_order=None): xsdata._xs_shapes = None # scipy 1.11+ prefers 'simpson', whereas older versions use 'simps' - if hasattr(scipy.integrate, 'simpson'): + if hasattr(scipy.integrate, "simpson"): integrate = scipy.integrate.simpson else: integrate = scipy.integrate.simps @@ -1843,8 +1985,10 @@ def convert_scatter_format(self, target_format, target_order=None): for imu in range(len(mu)): for l in range(self.num_orders): new_data[..., imu] += ( - (l + 0.5) * eval_legendre(l, mu[imu]) * - orig_data[..., l]) + (l + 0.5) + * eval_legendre(l, mu[imu]) + * orig_data[..., l] + ) elif target_format == SCATTER_HISTOGRAM: # This code uses the vectorized integration capabilities @@ -1859,9 +2003,11 @@ def convert_scatter_format(self, target_format, target_order=None): table_fine = np.zeros(new_data.shape[:-1] + (_NMU,)) for imu in range(len(mu_fine)): for l in range(self.num_orders): - table_fine[..., imu] += ((l + 0.5) - * eval_legendre(l, mu_fine[imu]) * - orig_data[..., l]) + table_fine[..., imu] += ( + (l + 0.5) + * eval_legendre(l, mu_fine[imu]) + * orig_data[..., l] + ) new_data[..., h_bin] = integrate(table_fine, x=mu_fine) elif self.scatter_format == SCATTER_TABULAR: @@ -1874,8 +2020,9 @@ def convert_scatter_format(self, target_format, target_order=None): # this is done with fixed sample integration routines. mu_fine = np.linspace(-1, 1, _NMU) for l in range(xsdata.num_orders): - y = (interp1d(mu_self, orig_data)(mu_fine) * - eval_legendre(l, mu_fine)) + y = interp1d(mu_self, orig_data)(mu_fine) * eval_legendre( + l, mu_fine + ) new_data[..., l] = integrate(y, x=mu_fine) elif target_format == SCATTER_TABULAR: @@ -1903,11 +2050,9 @@ def convert_scatter_format(self, target_format, target_order=None): # error. We will make the assumption that the center of the bin # has the value of the bin. The mu=-1 and 1 points will be # extrapolated from the shape. - mu_midpoint = np.linspace(-1, 1, self.num_orders, - endpoint=False) + mu_midpoint = np.linspace(-1, 1, self.num_orders, endpoint=False) mu_midpoint += (mu_midpoint[1] - mu_midpoint[0]) * 0.5 - interp = interp1d(mu_midpoint, orig_data, - fill_value='extrapolate') + interp = interp1d(mu_midpoint, orig_data, fill_value="extrapolate") # Now get the distribution normalization factor to take from # an integral quantity to a point-wise quantity norm = float(self.num_orders) / 2.0 @@ -1939,11 +2084,12 @@ def convert_scatter_format(self, target_format, target_order=None): # angle representation path. for h_bin in range(xsdata.num_orders): mu_fine = np.linspace(mu[h_bin], mu[h_bin + 1], _NMU) - new_data[..., h_bin] = \ - norm * integrate(interp(mu_fine), x=mu_fine) + new_data[..., h_bin] = norm * integrate( + interp(mu_fine), x=mu_fine + ) # Remove small values resulting from numerical precision issues - new_data[..., np.abs(new_data) < 1.E-10] = 0. + new_data[..., np.abs(new_data) < 1.0e-10] = 0.0 xsdata.set_scatter_matrix(new_data, temp) @@ -1961,26 +2107,26 @@ def to_hdf5(self, file): grp = file.create_group(self.name) if self.atomic_weight_ratio is not None: - grp.attrs['atomic_weight_ratio'] = self.atomic_weight_ratio + grp.attrs["atomic_weight_ratio"] = self.atomic_weight_ratio if self.fissionable is not None: - grp.attrs['fissionable'] = self.fissionable + grp.attrs["fissionable"] = self.fissionable if self.representation is not None: - grp.attrs['representation'] = np.bytes_(self.representation) + grp.attrs["representation"] = np.bytes_(self.representation) if self.representation == REPRESENTATION_ANGLE: if self.num_azimuthal is not None: - grp.attrs['num_azimuthal'] = self.num_azimuthal + grp.attrs["num_azimuthal"] = self.num_azimuthal if self.num_polar is not None: - grp.attrs['num_polar'] = self.num_polar + grp.attrs["num_polar"] = self.num_polar - grp.attrs['scatter_shape'] = np.bytes_("[G][G'][Order]") + grp.attrs["scatter_shape"] = np.bytes_("[G][G'][Order]") if self.scatter_format is not None: - grp.attrs['scatter_format'] = np.bytes_(self.scatter_format) + grp.attrs["scatter_format"] = np.bytes_(self.scatter_format) if self.order is not None: - grp.attrs['order'] = self.order + grp.attrs["order"] = self.order - ktg = grp.create_group('kTs') + ktg = grp.create_group("kTs") for temperature in self.temperatures: temp_label = str(int(np.round(temperature))) + "K" kT = temperature * openmc.data.K_BOLTZMANN @@ -1992,14 +2138,16 @@ def to_hdf5(self, file): xs_grp = grp.create_group(str(int(np.round(temperature))) + "K") if self._total[i] is None: - raise ValueError('total data must be provided when writing ' - 'the HDF5 library') + raise ValueError( + "total data must be provided when writing " "the HDF5 library" + ) xs_grp.create_dataset("total", data=self._total[i]) if self._absorption[i] is None: - raise ValueError('absorption data must be provided when ' - 'writing the HDF5 library') + raise ValueError( + "absorption data must be provided when " "writing the HDF5 library" + ) xs_grp.create_dataset("absorption", data=self._absorption[i]) @@ -2008,49 +2156,50 @@ def to_hdf5(self, file): xs_grp.create_dataset("fission", data=self._fission[i]) if self._kappa_fission[i] is not None: - xs_grp.create_dataset("kappa-fission", - data=self._kappa_fission[i]) + xs_grp.create_dataset("kappa-fission", data=self._kappa_fission[i]) if self._chi[i] is not None: xs_grp.create_dataset("chi", data=self._chi[i]) if self._chi_prompt[i] is not None: - xs_grp.create_dataset("chi-prompt", - data=self._chi_prompt[i]) + xs_grp.create_dataset("chi-prompt", data=self._chi_prompt[i]) if self._chi_delayed[i] is not None: - xs_grp.create_dataset("chi-delayed", - data=self._chi_delayed[i]) - - if self._nu_fission[i] is None and \ - (self._delayed_nu_fission[i] is None or \ - self._prompt_nu_fission[i] is None): - raise ValueError('nu-fission or prompt-nu-fission and ' - 'delayed-nu-fission data must be ' - 'provided when writing the HDF5 library') + xs_grp.create_dataset("chi-delayed", data=self._chi_delayed[i]) + + if self._nu_fission[i] is None and ( + self._delayed_nu_fission[i] is None + or self._prompt_nu_fission[i] is None + ): + raise ValueError( + "nu-fission or prompt-nu-fission and " + "delayed-nu-fission data must be " + "provided when writing the HDF5 library" + ) if self._nu_fission[i] is not None: - xs_grp.create_dataset("nu-fission", - data=self._nu_fission[i]) + xs_grp.create_dataset("nu-fission", data=self._nu_fission[i]) if self._prompt_nu_fission[i] is not None: - xs_grp.create_dataset("prompt-nu-fission", - data=self._prompt_nu_fission[i]) + xs_grp.create_dataset( + "prompt-nu-fission", data=self._prompt_nu_fission[i] + ) if self._delayed_nu_fission[i] is not None: - xs_grp.create_dataset("delayed-nu-fission", - data=self._delayed_nu_fission[i]) + xs_grp.create_dataset( + "delayed-nu-fission", data=self._delayed_nu_fission[i] + ) if self._beta[i] is not None: xs_grp.create_dataset("beta", data=self._beta[i]) if self._decay_rate[i] is not None: - xs_grp.create_dataset("decay-rate", - data=self._decay_rate[i]) + xs_grp.create_dataset("decay-rate", data=self._decay_rate[i]) if self._scatter_matrix[i] is None: - raise ValueError('Scatter matrix must be provided when ' - 'writing the HDF5 library') + raise ValueError( + "Scatter matrix must be provided when " "writing the HDF5 library" + ) # Get the sparse scattering data to print to the library G = self.energy_groups.num_groups @@ -2067,20 +2216,18 @@ def to_hdf5(self, file): for g_in in range(G): if self.scatter_format == SCATTER_LEGENDRE: if self.representation == REPRESENTATION_ISOTROPIC: - matrix = \ - self._scatter_matrix[i][g_in, :, 0] + matrix = self._scatter_matrix[i][g_in, :, 0] elif self.representation == REPRESENTATION_ANGLE: - matrix = \ - self._scatter_matrix[i][p, a, g_in, :, 0] + matrix = self._scatter_matrix[i][p, a, g_in, :, 0] else: if self.representation == REPRESENTATION_ISOTROPIC: - matrix = \ - np.sum(self._scatter_matrix[i][g_in, :, :], - axis=1) + matrix = np.sum( + self._scatter_matrix[i][g_in, :, :], axis=1 + ) elif self.representation == REPRESENTATION_ANGLE: - matrix = \ - np.sum(self._scatter_matrix[i][p, a, g_in, :, :], - axis=1) + matrix = np.sum( + self._scatter_matrix[i][p, a, g_in, :, :], axis=1 + ) nz = np.nonzero(matrix) # It is possible that there only zeros in matrix # and therefore nz will be empty, in that case set @@ -2100,15 +2247,15 @@ def to_hdf5(self, file): elif self.representation == REPRESENTATION_ANGLE: matrix = self._scatter_matrix[i][p, a, :, :, :] for g_in in range(G): - for g_out in range(g_out_bounds[p, a, g_in, 0], - g_out_bounds[p, a, g_in, 1] + 1): + for g_out in range( + g_out_bounds[p, a, g_in, 0], g_out_bounds[p, a, g_in, 1] + 1 + ): for l in range(len(matrix[g_in, g_out, :])): flat_scatt.append(matrix[g_in, g_out, l]) # And write it. - scatt_grp = xs_grp.create_group('scatter_data') - scatt_grp.create_dataset("scatter_matrix", - data=np.array(flat_scatt)) + scatt_grp = xs_grp.create_group("scatter_data") + scatt_grp.create_dataset("scatter_matrix", data=np.array(flat_scatt)) # Repeat for multiplicity if self._multiplicity_matrix[i] is not None: @@ -2122,13 +2269,16 @@ def to_hdf5(self, file): elif self.representation == REPRESENTATION_ANGLE: matrix = self._multiplicity_matrix[i][p, a, :, :] for g_in in range(G): - for g_out in range(g_out_bounds[p, a, g_in, 0], - g_out_bounds[p, a, g_in, 1] + 1): + for g_out in range( + g_out_bounds[p, a, g_in, 0], + g_out_bounds[p, a, g_in, 1] + 1, + ): flat_mult.append(matrix[g_in, g_out]) # And write it. - scatt_grp.create_dataset("multiplicity_matrix", - data=np.array(flat_mult)) + scatt_grp.create_dataset( + "multiplicity_matrix", data=np.array(flat_mult) + ) # And finally, adjust g_out_bounds for 1-based group counting # and write it. @@ -2142,8 +2292,9 @@ def to_hdf5(self, file): # Add the kinetics data if self._inverse_velocity[i] is not None: - xs_grp.create_dataset("inverse-velocity", - data=self._inverse_velocity[i]) + xs_grp.create_dataset( + "inverse-velocity", data=self._inverse_velocity[i] + ) @classmethod def from_hdf5(cls, group, name, energy_groups, num_delayed_groups): @@ -2172,61 +2323,72 @@ def from_hdf5(cls, group, name, energy_groups, num_delayed_groups): subgroups = group.keys() temperatures = [] for subgroup in subgroups: - if subgroup != 'kTs': + if subgroup != "kTs": temperatures.append(subgroup) # To ensure the actual floating point temperature used when creating # the new library is consistent with that used when originally creating # the file, get the floating point temperatures straight from the kTs # group. - kTs_group = group['kTs'] + kTs_group = group["kTs"] float_temperatures = [] for temperature in temperatures: kT = kTs_group[temperature][()] float_temperatures.append(kT / openmc.data.K_BOLTZMANN) attrs = group.attrs.keys() - if 'representation' in attrs: - representation = group.attrs['representation'].decode() + if "representation" in attrs: + representation = group.attrs["representation"].decode() else: representation = REPRESENTATION_ISOTROPIC - data = cls(name, energy_groups, float_temperatures, representation, - num_delayed_groups) + data = cls( + name, energy_groups, float_temperatures, representation, num_delayed_groups + ) - if 'scatter_format' in attrs: - data.scatter_format = group.attrs['scatter_format'].decode() + if "scatter_format" in attrs: + data.scatter_format = group.attrs["scatter_format"].decode() # Get the remaining optional attributes - if 'atomic_weight_ratio' in attrs: - data.atomic_weight_ratio = group.attrs['atomic_weight_ratio'] - if 'order' in attrs: - data.order = group.attrs['order'] + if "atomic_weight_ratio" in attrs: + data.atomic_weight_ratio = group.attrs["atomic_weight_ratio"] + if "order" in attrs: + data.order = group.attrs["order"] if data.representation == REPRESENTATION_ANGLE: - data.num_azimuthal = group.attrs['num_azimuthal'] - data.num_polar = group.attrs['num_polar'] + data.num_azimuthal = group.attrs["num_azimuthal"] + data.num_polar = group.attrs["num_polar"] # Read the temperature-dependent datasets for temp, float_temp in zip(temperatures, float_temperatures): - xs_types = ['total', 'absorption', 'fission', 'kappa-fission', - 'chi', 'chi-prompt', 'chi-delayed', 'nu-fission', - 'prompt-nu-fission', 'delayed-nu-fission', 'beta', - 'decay-rate', 'inverse-velocity'] + xs_types = [ + "total", + "absorption", + "fission", + "kappa-fission", + "chi", + "chi-prompt", + "chi-delayed", + "nu-fission", + "prompt-nu-fission", + "delayed-nu-fission", + "beta", + "decay-rate", + "inverse-velocity", + ] temperature_group = group[temp] for xs_type in xs_types: - set_func = 'set_' + xs_type.replace(' ', '_').replace('-', '_') + set_func = "set_" + xs_type.replace(" ", "_").replace("-", "_") if xs_type in temperature_group: - getattr(data, set_func)(temperature_group[xs_type][()], - float_temp) + getattr(data, set_func)(temperature_group[xs_type][()], float_temp) - scatt_group = temperature_group['scatter_data'] + scatt_group = temperature_group["scatter_data"] # Get scatter matrix and 'un-flatten' it - g_max = scatt_group['g_max'] - g_min = scatt_group['g_min'] - flat_scatter = scatt_group['scatter_matrix'][()] + g_max = scatt_group["g_max"] + g_min = scatt_group["g_min"] + flat_scatter = scatt_group["scatter_matrix"][()] scatter_matrix = np.zeros(data.xs_shapes["[G][G'][Order]"]) G = data.energy_groups.num_groups if data.representation == REPRESENTATION_ISOTROPIC: @@ -2248,17 +2410,19 @@ def from_hdf5(cls, group, name, energy_groups, num_delayed_groups): for g_out in range(g_mins - 1, g_maxs): for ang in range(data.num_orders): if data.representation == REPRESENTATION_ISOTROPIC: - scatter_matrix[g_in, g_out, ang] = \ - flat_scatter[flat_index] + scatter_matrix[g_in, g_out, ang] = flat_scatter[ + flat_index + ] elif data.representation == REPRESENTATION_ANGLE: - scatter_matrix[p, a, g_in, g_out, ang] = \ + scatter_matrix[p, a, g_in, g_out, ang] = ( flat_scatter[flat_index] + ) flat_index += 1 data.set_scatter_matrix(scatter_matrix, float_temp) # Repeat for multiplicity - if 'multiplicity_matrix' in scatt_group: - flat_mult = scatt_group['multiplicity_matrix'][()] + if "multiplicity_matrix" in scatt_group: + flat_mult = scatt_group["multiplicity_matrix"][()] mult_matrix = np.zeros(data.xs_shapes["[G][G']"]) flat_index = 0 for p in range(Np): @@ -2272,11 +2436,11 @@ def from_hdf5(cls, group, name, energy_groups, num_delayed_groups): g_maxs = g_max[p, a, g_in] for g_out in range(g_mins - 1, g_maxs): if data.representation == REPRESENTATION_ISOTROPIC: - mult_matrix[g_in, g_out] = \ - flat_mult[flat_index] + mult_matrix[g_in, g_out] = flat_mult[flat_index] elif data.representation == REPRESENTATION_ANGLE: - mult_matrix[p, a, g_in, g_out] = \ - flat_mult[flat_index] + mult_matrix[p, a, g_in, g_out] = flat_mult[ + flat_index + ] flat_index += 1 data.set_multiplicity_matrix(mult_matrix, float_temp) @@ -2334,7 +2498,7 @@ def energy_groups(self): @energy_groups.setter def energy_groups(self, energy_groups): - check_type('energy groups', energy_groups, openmc.mgxs.EnergyGroups) + check_type("energy groups", energy_groups, openmc.mgxs.EnergyGroups) self._energy_groups = energy_groups @property @@ -2343,11 +2507,14 @@ def num_delayed_groups(self): @num_delayed_groups.setter def num_delayed_groups(self, num_delayed_groups): - check_type('num_delayed_groups', num_delayed_groups, Integral) - check_greater_than('num_delayed_groups', num_delayed_groups, 0, - equality=True) - check_less_than('num_delayed_groups', num_delayed_groups, - openmc.mgxs.MAX_DELAYED_GROUPS, equality=True) + check_type("num_delayed_groups", num_delayed_groups, Integral) + check_greater_than("num_delayed_groups", num_delayed_groups, 0, equality=True) + check_less_than( + "num_delayed_groups", + num_delayed_groups, + openmc.mgxs.MAX_DELAYED_GROUPS, + equality=True, + ) self._num_delayed_groups = num_delayed_groups @property @@ -2369,12 +2536,13 @@ def add_xsdata(self, xsdata): """ if not isinstance(xsdata, XSdata): - msg = f'Unable to add a non-XSdata "{xsdata}" to the ' \ - 'MGXSLibrary instance' + msg = ( + f'Unable to add a non-XSdata "{xsdata}" to the ' "MGXSLibrary instance" + ) raise ValueError(msg) if xsdata.energy_groups != self._energy_groups: - msg = 'Energy groups of XSdata do not match that of MGXSLibrary.' + msg = "Energy groups of XSdata do not match that of MGXSLibrary." raise ValueError(msg) self._xsdatas.append(xsdata) @@ -2389,7 +2557,7 @@ def add_xsdatas(self, xsdatas): """ - check_iterable_type('xsdatas', xsdatas, XSdata) + check_iterable_type("xsdatas", xsdatas, XSdata) for xsdata in xsdatas: self.add_xsdata(xsdata) @@ -2405,8 +2573,10 @@ def remove_xsdata(self, xsdata): """ if not isinstance(xsdata, XSdata): - msg = f'Unable to remove a non-XSdata "{xsdata}" from the ' \ - 'MGXSLibrary instance' + msg = ( + f'Unable to remove a non-XSdata "{xsdata}" from the ' + "MGXSLibrary instance" + ) raise ValueError(msg) self._xsdatas.remove(xsdata) @@ -2432,8 +2602,9 @@ def get_by_name(self, name): result = xsdata return result - def convert_representation(self, target_representation, num_polar=None, - num_azimuthal=None): + def convert_representation( + self, target_representation, num_polar=None, num_azimuthal=None + ): """Produce a new XSdata object with the same data, but converted to the new representation (isotropic or angle-dependent). @@ -2467,9 +2638,9 @@ def convert_representation(self, target_representation, num_polar=None, library = copy.deepcopy(self) for i, xsdata in enumerate(self.xsdatas): - library.xsdatas[i] = \ - xsdata.convert_representation(target_representation, - num_polar, num_azimuthal) + library.xsdatas[i] = xsdata.convert_representation( + target_representation, num_polar, num_azimuthal + ) return library def convert_scatter_format(self, target_format, target_order): @@ -2496,12 +2667,13 @@ def convert_scatter_format(self, target_format, target_order): library = copy.deepcopy(self) for i, xsdata in enumerate(self.xsdatas): - library.xsdatas[i] = \ - xsdata.convert_scatter_format(target_format, target_order) + library.xsdatas[i] = xsdata.convert_scatter_format( + target_format, target_order + ) return library - def export_to_hdf5(self, filename='mgxs.h5', libver='earliest'): + def export_to_hdf5(self, filename="mgxs.h5", libver="earliest"): """Create an hdf5 file that can be used for a simulation. Parameters @@ -2514,15 +2686,15 @@ def export_to_hdf5(self, filename='mgxs.h5', libver='earliest'): """ - check_type('filename', filename, str) + check_type("filename", filename, str) # Create and write to the HDF5 file file = h5py.File(filename, "w", libver=libver) - file.attrs['filetype'] = np.bytes_(_FILETYPE_MGXS_LIBRARY) - file.attrs['version'] = [_VERSION_MGXS_LIBRARY, 0] - file.attrs['energy_groups'] = self.energy_groups.num_groups - file.attrs['delayed_groups'] = self.num_delayed_groups - file.attrs['group structure'] = self.energy_groups.group_edges + file.attrs["filetype"] = np.bytes_(_FILETYPE_MGXS_LIBRARY) + file.attrs["version"] = [_VERSION_MGXS_LIBRARY, 0] + file.attrs["energy_groups"] = self.energy_groups.num_groups + file.attrs["delayed_groups"] = self.num_delayed_groups + file.attrs["group structure"] = self.energy_groups.group_edges for xsdata in self._xsdatas: xsdata.to_hdf5(file) @@ -2547,28 +2719,30 @@ def from_hdf5(cls, filename=None): """ # If filename is None, get the cross sections from openmc.config if filename is None: - filename = openmc.config.get('mg_cross_sections') + filename = openmc.config.get("mg_cross_sections") # Check to make sure there was an environmental variable. if filename is None: - raise ValueError("Either path or openmc.config['mg_cross_sections']" - "must be set") + raise ValueError( + "Either path or openmc.config['mg_cross_sections']" "must be set" + ) - check_type('filename', filename, str) - file = h5py.File(filename, 'r') + check_type("filename", filename, str) + file = h5py.File(filename, "r") # Check filetype and version - check_filetype_version(file, _FILETYPE_MGXS_LIBRARY, - _VERSION_MGXS_LIBRARY) + check_filetype_version(file, _FILETYPE_MGXS_LIBRARY, _VERSION_MGXS_LIBRARY) - group_structure = file.attrs['group structure'] - num_delayed_groups = file.attrs['delayed_groups'] + group_structure = file.attrs["group structure"] + num_delayed_groups = file.attrs["delayed_groups"] energy_groups = openmc.mgxs.EnergyGroups(group_structure) data = cls(energy_groups, num_delayed_groups) for group_name, group in file.items(): - data.add_xsdata(openmc.XSdata.from_hdf5(group, group_name, - energy_groups, - num_delayed_groups)) + data.add_xsdata( + openmc.XSdata.from_hdf5( + group, group_name, energy_groups, num_delayed_groups + ) + ) return data diff --git a/openmc/mixin.py b/openmc/mixin.py index 0bc4128b0bf..1a3cb24cad7 100644 --- a/openmc/mixin.py +++ b/openmc/mixin.py @@ -53,7 +53,7 @@ def id(self, uid): cls = self._id_class except AttributeError: for cls in self.__class__.__mro__: - if 'next_id' in cls.__dict__: + if "next_id" in cls.__dict__: break if uid is None: @@ -63,10 +63,10 @@ def id(self, uid): cls.used_ids.add(cls.next_id) else: name = cls.__name__ - cv.check_type(f'{name} ID', uid, Integral) - cv.check_greater_than(f'{name} ID', uid, 0, equality=True) + cv.check_type(f"{name} ID", uid, Integral) + cv.check_greater_than(f"{name} ID", uid, 0, equality=True) if uid in cls.used_ids: - msg = f'Another {name} instance already exists with id={uid}.' + msg = f"Another {name} instance already exists with id={uid}." warn(msg, IDWarning) else: cls.used_ids.add(uid) diff --git a/openmc/model/funcs.py b/openmc/model/funcs.py index 41aa920eae7..936a1789f92 100644 --- a/openmc/model/funcs.py +++ b/openmc/model/funcs.py @@ -5,8 +5,13 @@ from openmc import Cylinder, Universe, Cell from .surface_composite import RectangularPrism, HexagonalPrism -from ..checkvalue import (check_type, check_value, check_length, - check_less_than, check_iterable_type) +from ..checkvalue import ( + check_type, + check_value, + check_length, + check_less_than, + check_iterable_type, +) import openmc.data @@ -15,8 +20,15 @@ PSI_TO_MPA = 0.006895 -def borated_water(boron_ppm, temperature=293., pressure=0.1013, temp_unit='K', - press_unit='MPa', density=None, **kwargs): +def borated_water( + boron_ppm, + temperature=293.0, + pressure=0.1013, + temp_unit="K", + press_unit="MPa", + density=None, + **kwargs, +): """Return a Material with the composition of boron dissolved in water. The water density can be determined from a temperature and pressure, or it @@ -50,17 +62,17 @@ def borated_water(boron_ppm, temperature=293., pressure=0.1013, temp_unit='K', """ # Perform any necessary unit conversions. - check_value('temperature unit', temp_unit, ('K', 'C', 'F')) - if temp_unit == 'K': + check_value("temperature unit", temp_unit, ("K", "C", "F")) + if temp_unit == "K": T = temperature - elif temp_unit == 'C': + elif temp_unit == "C": T = temperature + ZERO_CELSIUS_TO_KELVIN - elif temp_unit == 'F': - T = (temperature + ZERO_FAHRENHEIT_TO_KELVIN) * (5/9) - check_value('pressure unit', press_unit, ('MPa', 'psi')) - if press_unit == 'MPa': + elif temp_unit == "F": + T = (temperature + ZERO_FAHRENHEIT_TO_KELVIN) * (5 / 9) + check_value("pressure unit", press_unit, ("MPa", "psi")) + if press_unit == "MPa": P = pressure - elif press_unit == 'psi': + elif press_unit == "psi": P = pressure * PSI_TO_MPA # Set the density of water, either from an explicitly given density or from @@ -74,18 +86,18 @@ def borated_water(boron_ppm, temperature=293., pressure=0.1013, temp_unit='K', solution_density = water_density / (1 - boron_ppm * 1e-6) # Compute the molar mass of pure water. - hydrogen = openmc.Element('H') - oxygen = openmc.Element('O') + hydrogen = openmc.Element("H") + oxygen = openmc.Element("O") M_H2O = 0.0 - for iso_name, frac, junk in hydrogen.expand(2.0, 'ao'): + for iso_name, frac, junk in hydrogen.expand(2.0, "ao"): M_H2O += frac * openmc.data.atomic_mass(iso_name) - for iso_name, frac, junk in oxygen.expand(1.0, 'ao'): + for iso_name, frac, junk in oxygen.expand(1.0, "ao"): M_H2O += frac * openmc.data.atomic_mass(iso_name) # Compute the molar mass of boron. - boron = openmc.Element('B') + boron = openmc.Element("B") M_B = 0.0 - for iso_name, frac, junk in boron.expand(1.0, 'ao'): + for iso_name, frac, junk in boron.expand(1.0, "ao"): M_B += frac * openmc.data.atomic_mass(iso_name) # Compute the number fractions of each element. @@ -99,40 +111,66 @@ def borated_water(boron_ppm, temperature=293., pressure=0.1013, temp_unit='K', out = openmc.Material(temperature=T, **kwargs) else: out = openmc.Material(**kwargs) - out.add_element('H', frac_H, 'ao') - out.add_element('O', frac_O, 'ao') - out.add_element('B', frac_B, 'ao') - out.set_density('g/cc', solution_density) - out.add_s_alpha_beta('c_H_in_H2O') + out.add_element("H", frac_H, "ao") + out.add_element("O", frac_O, "ao") + out.add_element("B", frac_B, "ao") + out.set_density("g/cc", solution_density) + out.add_s_alpha_beta("c_H_in_H2O") return out - - -def rectangular_prism(width, height, axis='z', origin=(0., 0.), - boundary_type='transmission', corner_radius=0.): - warn("The rectangular_prism(...) function has been replaced by the " - "RectangularPrism(...) class. Future versions of OpenMC will not " - "accept rectangular_prism.", FutureWarning) +def rectangular_prism( + width, + height, + axis="z", + origin=(0.0, 0.0), + boundary_type="transmission", + corner_radius=0.0, +): + warn( + "The rectangular_prism(...) function has been replaced by the " + "RectangularPrism(...) class. Future versions of OpenMC will not " + "accept rectangular_prism.", + FutureWarning, + ) return -RectangularPrism( - width=width, height=height, axis=axis, origin=origin, - boundary_type=boundary_type, corner_radius=corner_radius) - - -def hexagonal_prism(edge_length=1., orientation='y', origin=(0., 0.), - boundary_type='transmission', corner_radius=0.): - warn("The hexagonal_prism(...) function has been replaced by the " - "HexagonalPrism(...) class. Future versions of OpenMC will not " - "accept hexagonal_prism.", FutureWarning) + width=width, + height=height, + axis=axis, + origin=origin, + boundary_type=boundary_type, + corner_radius=corner_radius, + ) + + +def hexagonal_prism( + edge_length=1.0, + orientation="y", + origin=(0.0, 0.0), + boundary_type="transmission", + corner_radius=0.0, +): + warn( + "The hexagonal_prism(...) function has been replaced by the " + "HexagonalPrism(...) class. Future versions of OpenMC will not " + "accept hexagonal_prism.", + FutureWarning, + ) return -HexagonalPrism( - edge_length=edge_length, orientation=orientation, origin=origin, - boundary_type=boundary_type, corner_radius=corner_radius) + edge_length=edge_length, + orientation=orientation, + origin=origin, + boundary_type=boundary_type, + corner_radius=corner_radius, + ) def get_hexagonal_prism(*args, **kwargs): - warn("get_hexagonal_prism(...) has been renamed hexagonal_prism(...). " - "Future versions of OpenMC will not accept get_hexagonal_prism.", - FutureWarning) + warn( + "get_hexagonal_prism(...) has been renamed hexagonal_prism(...). " + "Future versions of OpenMC will not accept get_hexagonal_prism.", + FutureWarning, + ) return hexagonal_prism(*args, **kwargs) @@ -166,8 +204,7 @@ def subdivide(surfaces): return regions -def pin(surfaces, items, subdivisions=None, divide_vols=True, - **kwargs): +def pin(surfaces, items, subdivisions=None, divide_vols=True, **kwargs): """Convenience function for building a fuel pin Parameters @@ -204,8 +241,9 @@ def pin(surfaces, items, subdivisions=None, divide_vols=True, """ if "cells" in kwargs: raise ValueError( - "Cells will be set by this function, not from input arguments.") - check_type("items", items, Iterable) + "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) @@ -220,8 +258,7 @@ def pin(surfaces, items, subdivisions=None, divide_vols=True, elif surf_type is openmc.XCylinder: center_getter = attrgetter("z0", "y0") else: - raise TypeError( - f"Not configured to interpret {surf_type.__name__} surfaces") + raise TypeError(f"Not configured to interpret {surf_type.__name__} surfaces") centers = set() prev_rad = 0 @@ -231,28 +268,27 @@ def pin(surfaces, items, subdivisions=None, divide_vols=True, 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)) + "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)) + "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) + 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) + check_less_than("outer ring", max(subdivisions), len(surfaces), equality=True) # ensure ability to concatenate if not isinstance(items, list): @@ -272,22 +308,19 @@ def pin(surfaces, items, subdivisions=None, divide_vols=True, upper_rad = surfaces[ring_index].r - area_term = (upper_rad ** 2 - lower_rad ** 2) / nr + area_term = (upper_rad**2 - lower_rad**2) / nr for new_index in range(nr - 1): - lower_rad = sqrt(area_term + lower_rad ** 2) + 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:]) + 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): + 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)] + items[ring_index:ring_index] = [filler.clone() for _i in range(nr - 1)] # Build the universe regions = subdivide(surfaces) diff --git a/openmc/model/model.py b/openmc/model/model.py index 49cb0a83ef5..33a61ed499a 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -63,8 +63,9 @@ class Model: """ - def __init__(self, geometry=None, materials=None, settings=None, - tallies=None, plots=None): + def __init__( + self, geometry=None, materials=None, settings=None, tallies=None, plots=None + ): self.geometry = openmc.Geometry() self.materials = openmc.Materials() self.settings = openmc.Settings() @@ -88,7 +89,7 @@ def geometry(self) -> openmc.Geometry | None: @geometry.setter def geometry(self, geometry): - check_type('geometry', geometry, openmc.Geometry) + check_type("geometry", geometry, openmc.Geometry) self._geometry = geometry @property @@ -97,7 +98,7 @@ def materials(self) -> openmc.Materials | None: @materials.setter def materials(self, materials): - check_type('materials', materials, Iterable, openmc.Material) + check_type("materials", materials, Iterable, openmc.Material) if isinstance(materials, openmc.Materials): self._materials = materials else: @@ -111,7 +112,7 @@ def settings(self) -> openmc.Settings | None: @settings.setter def settings(self, settings): - check_type('settings', settings, openmc.Settings) + check_type("settings", settings, openmc.Settings) self._settings = settings @property @@ -120,7 +121,7 @@ def tallies(self) -> openmc.Tallies | None: @tallies.setter def tallies(self, tallies): - check_type('tallies', tallies, Iterable, openmc.Tally) + check_type("tallies", tallies, Iterable, openmc.Tally) if isinstance(tallies, openmc.Tallies): self._tallies = tallies else: @@ -134,7 +135,7 @@ def plots(self) -> openmc.Plots | None: @plots.setter def plots(self, plots): - check_type('plots', plots, Iterable, openmc.Plot) + check_type("plots", plots, Iterable, openmc.Plot) if isinstance(plots, openmc.Plots): self._plots = plots else: @@ -146,6 +147,7 @@ def plots(self, plots): def is_initialized(self) -> bool: try: import openmc.lib + return openmc.lib.is_initialized except ImportError: return False @@ -195,9 +197,14 @@ def _materials_by_name(self) -> dict[int, openmc.Material]: return result @classmethod - def from_xml(cls, geometry='geometry.xml', materials='materials.xml', - settings='settings.xml', tallies='tallies.xml', - plots='plots.xml') -> Model: + def from_xml( + cls, + geometry="geometry.xml", + materials="materials.xml", + settings="settings.xml", + tallies="tallies.xml", + plots="plots.xml", + ) -> Model: """Create model from existing XML files Parameters @@ -231,7 +238,7 @@ def from_xml(cls, geometry='geometry.xml', materials='materials.xml', return cls(geometry, materials, settings, tallies, plots) @classmethod - def from_model_xml(cls, path='model.xml'): + def from_model_xml(cls, path="model.xml"): """Create model from single XML file .. versionadded:: 0.13.3 @@ -248,20 +255,32 @@ def from_model_xml(cls, path='model.xml'): model = cls() meshes = {} - model.settings = openmc.Settings.from_xml_element(root.find('settings'), meshes) - model.materials = openmc.Materials.from_xml_element(root.find('materials')) - model.geometry = openmc.Geometry.from_xml_element(root.find('geometry'), model.materials) - - if root.find('tallies') is not None: - model.tallies = openmc.Tallies.from_xml_element(root.find('tallies'), meshes) + model.settings = openmc.Settings.from_xml_element(root.find("settings"), meshes) + model.materials = openmc.Materials.from_xml_element(root.find("materials")) + model.geometry = openmc.Geometry.from_xml_element( + root.find("geometry"), model.materials + ) + + if root.find("tallies") is not None: + model.tallies = openmc.Tallies.from_xml_element( + root.find("tallies"), meshes + ) - if root.find('plots') is not None: - model.plots = openmc.Plots.from_xml_element(root.find('plots')) + if root.find("plots") is not None: + model.plots = openmc.Plots.from_xml_element(root.find("plots")) return model - def init_lib(self, threads=None, geometry_debug=False, restart_file=None, - tracks=False, output=True, event_based=None, intracomm=None): + def init_lib( + self, + threads=None, + geometry_debug=False, + restart_file=None, + tracks=False, + output=True, + event_based=None, + intracomm=None, + ): """Initializes the model in memory via the C API .. versionadded:: 0.13.0 @@ -300,9 +319,13 @@ def init_lib(self, threads=None, geometry_debug=False, restart_file=None, # should be exposed so that it can be accessed via model.run(...) args = _process_CLI_arguments( - volume=False, geometry_debug=geometry_debug, - restart_file=restart_file, threads=threads, tracks=tracks, - event_based=event_based) + volume=False, + geometry_debug=geometry_debug, + restart_file=restart_file, + threads=threads, + tracks=tracks, + event_based=event_based, + ) # Args adds the openmc_exec command in the first entry; remove it args = args[1:] @@ -335,9 +358,16 @@ def finalize_lib(self): openmc.lib.finalize() - def deplete(self, timesteps, method='cecm', final_step=True, - operator_kwargs=None, directory='.', output=True, - **integrator_kwargs): + def deplete( + self, + timesteps, + method="cecm", + final_step=True, + operator_kwargs=None, + directory=".", + output=True, + **integrator_kwargs, + ): """Deplete model using specified timesteps/power .. versionchanged:: 0.13.0 @@ -393,11 +423,11 @@ def deplete(self, timesteps, method='cecm', final_step=True, depletion_operator.cleanup_when_done = False # Set up the integrator - check_value('method', method, - dep.integrators.integrator_by_name.keys()) + check_value("method", method, dep.integrators.integrator_by_name.keys()) integrator_class = dep.integrators.integrator_by_name[method] - integrator = integrator_class(depletion_operator, timesteps, - **integrator_kwargs) + integrator = integrator_class( + depletion_operator, timesteps, **integrator_kwargs + ) # Now perform the depletion with openmc.lib.quiet_dll(output): @@ -413,14 +443,14 @@ def deplete(self, timesteps, method='cecm', final_step=True, mat.nuclides.clear() for nuc, density in zip(nuclides, densities): mat.add_nuclide(nuc, density) - mat.set_density('atom/b-cm', sum(densities)) + mat.set_density("atom/b-cm", sum(densities)) # If we didnt start intialized, we should cleanup after ourselves if not started_initialized: depletion_operator.cleanup_when_done = True depletion_operator.finalize() - def export_to_xml(self, directory='.', remove_surfs=False): + def export_to_xml(self, directory=".", remove_surfs=False): """Export model to separate XML files. Parameters @@ -448,8 +478,7 @@ def export_to_xml(self, directory='.', remove_surfs=False): if self.materials: self.materials.export_to_xml(d) else: - materials = openmc.Materials(self.geometry.get_all_materials() - .values()) + materials = openmc.Materials(self.geometry.get_all_materials().values()) materials.export_to_xml(d) if self.tallies: @@ -457,7 +486,7 @@ def export_to_xml(self, directory='.', remove_surfs=False): if self.plots: self.plots.export_to_xml(d) - def export_to_model_xml(self, path='model.xml', remove_surfs=False): + def export_to_model_xml(self, path="model.xml", remove_surfs=False): """Export model to a single XML file. .. versionadded:: 0.13.3 @@ -476,20 +505,24 @@ def export_to_model_xml(self, path='model.xml', remove_surfs=False): # if the provided path doesn't end with the XML extension, assume the # input path is meant to be a directory. If the directory does not # exist, create it and place a 'model.xml' file there. - if not str(xml_path).endswith('.xml'): + if not str(xml_path).endswith(".xml"): if not xml_path.exists(): xml_path.mkdir(parents=True, exist_ok=True) elif not xml_path.is_dir(): - raise FileExistsError(f"File exists and is not a directory: '{xml_path}'") - xml_path /= 'model.xml' + raise FileExistsError( + f"File exists and is not a directory: '{xml_path}'" + ) + xml_path /= "model.xml" # if this is an XML file location and the file's parent directory does # not exist, create it before continuing elif not xml_path.parent.exists(): xml_path.parent.mkdir(parents=True, exist_ok=True) if remove_surfs: - warnings.warn("remove_surfs kwarg will be deprecated soon, please " - "set the Geometry.merge_surfaces attribute instead.") + warnings.warn( + "remove_surfs kwarg will be deprecated soon, please " + "set the Geometry.merge_surfaces attribute instead." + ) self.geometry.merge_surfaces = True # provide a memo to track which meshes have been written @@ -506,10 +539,9 @@ def export_to_model_xml(self, path='model.xml', remove_surfs=False): if self.materials: materials = self.materials else: - materials = openmc.Materials(self.geometry.get_all_materials() - .values()) + materials = openmc.Materials(self.geometry.get_all_materials().values()) - with open(xml_path, 'w', encoding='utf-8', errors='xmlcharrefreplace') as fh: + with open(xml_path, "w", encoding="utf-8", errors="xmlcharrefreplace") as fh: # write the XML header fh.write("\n") fh.write("\n") @@ -522,7 +554,9 @@ def export_to_model_xml(self, path='model.xml', remove_surfs=False): if self.tallies: tallies_element = self.tallies.to_xml_element(mesh_memo) - xml.clean_indentation(tallies_element, level=1, trailing_indent=self.plots) + xml.clean_indentation( + tallies_element, level=1, trailing_indent=self.plots + ) fh.write(ET.tostring(tallies_element, encoding="unicode")) if self.plots: plots_element = self.plots.to_xml_element() @@ -551,21 +585,22 @@ def import_properties(self, filename): cells = self.geometry.get_all_cells() materials = self.geometry.get_all_materials() - with h5py.File(filename, 'r') as fh: - cells_group = fh['geometry/cells'] + with h5py.File(filename, "r") as fh: + cells_group = fh["geometry/cells"] # Make sure number of cells matches - n_cells = fh['geometry'].attrs['n_cells'] + n_cells = fh["geometry"].attrs["n_cells"] if n_cells != len(cells): - raise ValueError("Number of cells in properties file doesn't " - "match current model.") + raise ValueError( + "Number of cells in properties file doesn't " "match current model." + ) # Update temperatures for cells filled with materials for name, group in cells_group.items(): cell_id = int(name.split()[1]) cell = cells[cell_id] - if cell.fill_type in ('material', 'distribmat'): - temperature = group['temperature'][()] + if cell.fill_type in ("material", "distribmat"): + temperature = group["temperature"][()] cell.temperature = temperature if self.is_initialized: lib_cell = openmc.lib.cells[cell_id] @@ -576,25 +611,38 @@ def import_properties(self, filename): lib_cell.set_temperature(temperature[0]) # Make sure number of materials matches - mats_group = fh['materials'] - n_cells = mats_group.attrs['n_materials'] + mats_group = fh["materials"] + n_cells = mats_group.attrs["n_materials"] if n_cells != len(materials): - raise ValueError("Number of materials in properties file " - "doesn't match current model.") + raise ValueError( + "Number of materials in properties file " + "doesn't match current model." + ) # Update material densities for name, group in mats_group.items(): mat_id = int(name.split()[1]) - atom_density = group.attrs['atom_density'] - materials[mat_id].set_density('atom/b-cm', atom_density) + atom_density = group.attrs["atom_density"] + materials[mat_id].set_density("atom/b-cm", atom_density) if self.is_initialized: C_mat = openmc.lib.materials[mat_id] - C_mat.set_density(atom_density, 'atom/b-cm') + C_mat.set_density(atom_density, "atom/b-cm") - def run(self, particles=None, threads=None, geometry_debug=False, - restart_file=None, tracks=False, output=True, cwd='.', - openmc_exec='openmc', mpi_args=None, event_based=None, - export_model_xml=True, **export_kwargs): + def run( + self, + particles=None, + threads=None, + geometry_debug=False, + restart_file=None, + tracks=False, + output=True, + cwd=".", + openmc_exec="openmc", + mpi_args=None, + event_based=None, + export_model_xml=True, + **export_kwargs, + ): """Run OpenMC If the C API has been initialized, then the C API is used, otherwise, @@ -670,9 +718,9 @@ def run(self, particles=None, threads=None, geometry_debug=False, # Handle the run options as applicable # First dont allow ones that must be set via init for arg_name, arg, default in zip( - ['threads', 'geometry_debug', 'restart_file', 'tracks'], + ["threads", "geometry_debug", "restart_file", "tracks"], [threads, geometry_debug, restart_file, tracks], - [None, False, None, False] + [None, False, None, False], ): if arg != default: msg = f"{arg_name} must be set via Model.is_initialized(...)" @@ -701,26 +749,43 @@ def run(self, particles=None, threads=None, geometry_debug=False, else: self.export_to_xml(**export_kwargs) path_input = export_kwargs.get("path", None) - openmc.run(particles, threads, geometry_debug, restart_file, - tracks, output, Path('.'), openmc_exec, mpi_args, - event_based, path_input) + openmc.run( + particles, + threads, + geometry_debug, + restart_file, + tracks, + output, + Path("."), + openmc_exec, + mpi_args, + event_based, + path_input, + ) # Get output directory and return the last statepoint written - if self.settings.output and 'path' in self.settings.output: - output_dir = Path(self.settings.output['path']) + if self.settings.output and "path" in self.settings.output: + output_dir = Path(self.settings.output["path"]) else: output_dir = Path.cwd() - for sp in output_dir.glob('statepoint.*.h5'): + for sp in output_dir.glob("statepoint.*.h5"): mtime = sp.stat().st_mtime if mtime >= tstart: # >= allows for poor clock resolution tstart = mtime last_statepoint = sp return last_statepoint - def calculate_volumes(self, threads=None, output=True, cwd='.', - openmc_exec='openmc', mpi_args=None, - apply_volumes=True, export_model_xml=True, - **export_kwargs): + def calculate_volumes( + self, + threads=None, + output=True, + cwd=".", + openmc_exec="openmc", + mpi_args=None, + apply_volumes=True, + export_model_xml=True, + **export_kwargs, + ): """Runs an OpenMC stochastic volume calculation and, if requested, applies volumes to the model @@ -760,8 +825,10 @@ def calculate_volumes(self, threads=None, output=True, cwd='.', if len(self.settings.volume_calculations) == 0: # Then there is no volume calculation specified - raise ValueError("The Settings.volume_calculations attribute must" - " be specified before executing this method!") + raise ValueError( + "The Settings.volume_calculations attribute must" + " be specified before executing this method!" + ) with change_directory(cwd): if self.is_initialized: @@ -769,8 +836,10 @@ def calculate_volumes(self, threads=None, output=True, cwd='.', msg = "Threads must be set via Model.is_initialized(...)" raise ValueError(msg) if mpi_args is not None: - msg = "The MPI environment must be set otherwise such as" \ + msg = ( + "The MPI environment must be set otherwise such as" "with the call to mpi4py" + ) raise ValueError(msg) # Compute the volumes @@ -783,8 +852,11 @@ def calculate_volumes(self, threads=None, output=True, cwd='.', self.export_to_xml(**export_kwargs) path_input = export_kwargs.get("path", None) openmc.calculate_volumes( - threads=threads, output=output, openmc_exec=openmc_exec, - mpi_args=mpi_args, path_input=path_input + threads=threads, + output=output, + openmc_exec=openmc_exec, + mpi_args=mpi_args, + path_input=path_input, ) # Now we apply the volumes @@ -801,16 +873,17 @@ def calculate_volumes(self, threads=None, output=True, cwd='.', self.geometry.add_volume_information(vol_calc) # And now repeat for the C API - if self.is_initialized and vol_calc.domain_type == 'material': + if self.is_initialized and vol_calc.domain_type == "material": # Then we can do this in the C API for domain_id in vol_calc.ids: - openmc.lib.materials[domain_id].volume = \ - vol_calc.volumes[domain_id].n + openmc.lib.materials[domain_id].volume = vol_calc.volumes[ + domain_id + ].n def plot( self, n_samples: int | None = None, - plane_tolerance: float = 1., + plane_tolerance: float = 1.0, source_kwargs: dict | None = None, **kwargs, ): @@ -838,11 +911,11 @@ def plot( Axes containing resulting image """ - check_type('n_samples', n_samples, int | None) - check_type('plane_tolerance', plane_tolerance, float) + check_type("n_samples", n_samples, int | None) + check_type("plane_tolerance", plane_tolerance, float) if source_kwargs is None: source_kwargs = {} - source_kwargs.setdefault('marker', 'x') + source_kwargs.setdefault("marker", "x") ax = self.geometry.plot(**kwargs) if n_samples: @@ -851,9 +924,9 @@ def plot( # Determine plotting parameters and bounding box of geometry bbox = self.geometry.bounding_box - origin = kwargs.get('origin', None) - basis = kwargs.get('basis', 'xy') - indices = {'xy': (0, 1, 2), 'xz': (0, 2, 1), 'yz': (1, 2, 0)}[basis] + origin = kwargs.get("origin", None) + basis = kwargs.get("basis", "xy") + indices = {"xy": (0, 1, 2), "xz": (0, 2, 1), "yz": (1, 2, 0)}[basis] # Infer origin if not provided if np.isinf(bbox.extent[basis]).any(): @@ -874,17 +947,14 @@ def plot( ys = [] tol = plane_tolerance for particle in particles: - if (slice_value - tol < particle.r[slice_index] < slice_value + tol): + if slice_value - tol < particle.r[slice_index] < slice_value + tol: xs.append(particle.r[indices[0]]) ys.append(particle.r[indices[1]]) ax.scatter(xs, ys, **source_kwargs) return ax def sample_external_source( - self, - n_samples: int = 1000, - prn_seed: int | None = None, - **init_kwargs + self, n_samples: int = 1000, prn_seed: int | None = None, **init_kwargs ) -> openmc.ParticleList: """Sample external source and return source particles. @@ -909,8 +979,8 @@ def sample_external_source( # Silence output by default. Also set arguments to start in volume # calculation mode to avoid loading cross sections - init_kwargs.setdefault('output', False) - init_kwargs.setdefault('args', ['-c']) + init_kwargs.setdefault("output", False) + init_kwargs.setdefault("args", ["-c"]) with change_directory(tmpdir=True): # Export model within temporary directory @@ -922,8 +992,14 @@ def sample_external_source( n_samples=n_samples, prn_seed=prn_seed ) - def plot_geometry(self, output=True, cwd='.', openmc_exec='openmc', - export_model_xml=True, **export_kwargs): + def plot_geometry( + self, + output=True, + cwd=".", + openmc_exec="openmc", + export_model_xml=True, + **export_kwargs, + ): """Creates plot images as specified by the Model.plots attribute .. versionadded:: 0.13.0 @@ -949,8 +1025,10 @@ def plot_geometry(self, output=True, cwd='.', openmc_exec='openmc', if len(self.plots) == 0: # Then there is no volume calculation specified - raise ValueError("The Model.plots attribute must be specified " - "before executing this method!") + raise ValueError( + "The Model.plots attribute must be specified " + "before executing this method!" + ) with change_directory(cwd): if self.is_initialized: @@ -962,40 +1040,43 @@ def plot_geometry(self, output=True, cwd='.', openmc_exec='openmc', else: self.export_to_xml(**export_kwargs) path_input = export_kwargs.get("path", None) - openmc.plot_geometry(output=output, openmc_exec=openmc_exec, - path_input=path_input) + openmc.plot_geometry( + output=output, openmc_exec=openmc_exec, path_input=path_input + ) - def _change_py_lib_attribs(self, names_or_ids, value, obj_type, - attrib_name, density_units='atom/b-cm'): + def _change_py_lib_attribs( + self, names_or_ids, value, obj_type, attrib_name, density_units="atom/b-cm" + ): # Method to do the same work whether it is a cell or material and # a temperature or volume - check_type('names_or_ids', names_or_ids, Iterable, (Integral, str)) - check_type('obj_type', obj_type, str) + check_type("names_or_ids", names_or_ids, Iterable, (Integral, str)) + check_type("obj_type", obj_type, str) obj_type = obj_type.lower() - check_value('obj_type', obj_type, ('material', 'cell')) - check_value('attrib_name', attrib_name, - ('temperature', 'volume', 'density', 'rotation', - 'translation')) + check_value("obj_type", obj_type, ("material", "cell")) + check_value( + "attrib_name", + attrib_name, + ("temperature", "volume", "density", "rotation", "translation"), + ) # The C API only allows setting density units of atom/b-cm and g/cm3 - check_value('density_units', density_units, ('atom/b-cm', 'g/cm3')) + check_value("density_units", density_units, ("atom/b-cm", "g/cm3")) # The C API has no way to set cell volume or material temperature # so lets raise exceptions as needed - if obj_type == 'cell' and attrib_name == 'volume': - raise NotImplementedError( - 'Setting a Cell volume is not supported!') - if obj_type == 'material' and attrib_name == 'temperature': + if obj_type == "cell" and attrib_name == "volume": + raise NotImplementedError("Setting a Cell volume is not supported!") + if obj_type == "material" and attrib_name == "temperature": raise NotImplementedError( - 'Setting a material temperature is not supported!') + "Setting a material temperature is not supported!" + ) # And some items just dont make sense - if obj_type == 'cell' and attrib_name == 'density': - raise ValueError('Cannot set a Cell density!') - if obj_type == 'material' and attrib_name in ('rotation', - 'translation'): - raise ValueError('Cannot set a material rotation/translation!') + if obj_type == "cell" and attrib_name == "density": + raise ValueError("Cannot set a Cell density!") + if obj_type == "material" and attrib_name in ("rotation", "translation"): + raise ValueError("Cannot set a material rotation/translation!") # Set the - if obj_type == 'cell': + if obj_type == "cell": by_name = self._cells_by_name by_id = self._cells_by_id if self.is_initialized: @@ -1031,16 +1112,16 @@ def _change_py_lib_attribs(self, names_or_ids, value, obj_type, # Now perform the change to both python and C API for id_ in ids: obj = by_id[id_] - if attrib_name == 'density': + if attrib_name == "density": obj.set_density(density_units, value) else: setattr(obj, attrib_name, value) # Next lets keep what is in C API memory up to date as well if self.is_initialized: lib_obj = obj_by_id[id_] - if attrib_name == 'density': + if attrib_name == "density": lib_obj.set_density(value, density_units) - elif attrib_name == 'temperature': + elif attrib_name == "temperature": lib_obj.set_temperature(value) else: setattr(lib_obj, attrib_name, value) @@ -1065,7 +1146,7 @@ def rotate_cells(self, names_or_ids, vector): """ - self._change_py_lib_attribs(names_or_ids, vector, 'cell', 'rotation') + self._change_py_lib_attribs(names_or_ids, vector, "cell", "rotation") def translate_cells(self, names_or_ids, vector): """Translate the identified cell(s) by the specified translation vector. @@ -1087,10 +1168,9 @@ def translate_cells(self, names_or_ids, vector): """ - self._change_py_lib_attribs(names_or_ids, vector, 'cell', - 'translation') + self._change_py_lib_attribs(names_or_ids, vector, "cell", "translation") - def update_densities(self, names_or_ids, density, density_units='atom/b-cm'): + def update_densities(self, names_or_ids, density, density_units="atom/b-cm"): """Update the density of a given set of materials to a new value .. note:: If applying this change to a name that is not unique, then @@ -1110,8 +1190,9 @@ def update_densities(self, names_or_ids, density, density_units='atom/b-cm'): """ - self._change_py_lib_attribs(names_or_ids, density, 'material', - 'density', density_units) + self._change_py_lib_attribs( + names_or_ids, density, "material", "density", density_units + ) def update_cell_temperatures(self, names_or_ids, temperature): """Update the temperature of a set of cells to the given value @@ -1131,8 +1212,7 @@ def update_cell_temperatures(self, names_or_ids, temperature): """ - self._change_py_lib_attribs(names_or_ids, temperature, 'cell', - 'temperature') + self._change_py_lib_attribs(names_or_ids, temperature, "cell", "temperature") def update_material_volumes(self, names_or_ids, volume): """Update the volume of a set of materials to the given value @@ -1152,7 +1232,7 @@ def update_material_volumes(self, names_or_ids, volume): """ - self._change_py_lib_attribs(names_or_ids, volume, 'material', 'volume') + self._change_py_lib_attribs(names_or_ids, volume, "material", "volume") def differentiate_depletable_mats(self, diff_volume_method: str): """Assign distribmats for each depletable material @@ -1172,14 +1252,16 @@ def differentiate_depletable_mats(self, diff_volume_method: str): # Extract all depletable materials which have multiple instances distribmats = set( - [mat for mat in self.materials - if mat.depletable and mat.num_instances > 1]) + [mat for mat in self.materials if mat.depletable and mat.num_instances > 1] + ) - if diff_volume_method == 'divide equally': + if diff_volume_method == "divide equally": for mat in distribmats: if mat.volume is None: - raise RuntimeError("Volume not specified for depletable " - f"material with ID={mat.id}.") + raise RuntimeError( + "Volume not specified for depletable " + f"material with ID={mat.id}." + ) mat.volume /= mat.num_instances if distribmats: @@ -1187,9 +1269,9 @@ def differentiate_depletable_mats(self, diff_volume_method: str): for cell in self.geometry.get_all_material_cells().values(): if cell.fill in distribmats: mat = cell.fill - if diff_volume_method == 'divide equally': + if diff_volume_method == "divide equally": cell.fill = [mat.clone() for _ in range(cell.num_instances)] - elif diff_volume_method == 'match cell': + elif diff_volume_method == "match cell": for _ in range(cell.num_instances): cell.fill = mat.clone() if not cell.volume: diff --git a/openmc/model/surface_composite.py b/openmc/model/surface_composite.py index 1f25b9ca636..b2d4b7ccfdf 100644 --- a/openmc/model/surface_composite.py +++ b/openmc/model/surface_composite.py @@ -12,8 +12,14 @@ from scipy.spatial import ConvexHull, Delaunay import openmc -from openmc.checkvalue import (check_greater_than, check_value, check_less_than, - check_iterable_type, check_length, check_type) +from openmc.checkvalue import ( + check_greater_than, + check_value, + check_less_than, + check_iterable_type, + check_length, + check_type, +) class CompositeSurface(ABC): @@ -26,7 +32,7 @@ def translate(self, vector, inplace=False): setattr(surf, name, s.translate(vector, inplace)) return surf - def rotate(self, rotation, pivot=(0., 0., 0.), order='xyz', inplace=False): + def rotate(self, rotation, pivot=(0.0, 0.0, 0.0), order="xyz", inplace=False): surf = self if inplace else copy(self) for name in self._surface_names: s = getattr(surf, name) @@ -43,7 +49,7 @@ def boundary_type(self, boundary_type): # on one-sided cones classes = (XConeOneSided, YConeOneSided, ZConeOneSided, Vessel) for name in self._surface_names: - if isinstance(self, classes) and name.startswith('plane'): + if isinstance(self, classes) and name.startswith("plane"): continue getattr(self, name).boundary_type = boundary_type @@ -114,45 +120,46 @@ class CylinderSector(CompositeSurface): """ - _surface_names = ('outer_cyl', 'inner_cyl', 'plane1', 'plane2') + _surface_names = ("outer_cyl", "inner_cyl", "plane1", "plane2") - def __init__(self, - r1, - r2, - theta1, - theta2, - center=(0., 0.), - axis='z', - **kwargs): + def __init__(self, r1, r2, theta1, theta2, center=(0.0, 0.0), axis="z", **kwargs): if r2 <= r1: - raise ValueError('r2 must be greater than r1.') + raise ValueError("r2 must be greater than r1.") if theta2 <= theta1: - raise ValueError('theta2 must be greater than theta1.') + raise ValueError("theta2 must be greater than theta1.") phi1 = pi / 180 * theta1 phi2 = pi / 180 * theta2 # Coords for axis-perpendicular planes - p1 = np.array([center[0], center[1], 1.]) + p1 = np.array([center[0], center[1], 1.0]) - p2_plane1 = np.array([r1 * cos(phi1) + center[0], r1 * sin(phi1) + center[1], 0.]) - p3_plane1 = np.array([r2 * cos(phi1) + center[0], r2 * sin(phi1) + center[1], 0.]) + p2_plane1 = np.array( + [r1 * cos(phi1) + center[0], r1 * sin(phi1) + center[1], 0.0] + ) + p3_plane1 = np.array( + [r2 * cos(phi1) + center[0], r2 * sin(phi1) + center[1], 0.0] + ) - p2_plane2 = np.array([r1 * cos(phi2) + center[0], r1 * sin(phi2)+ center[1], 0.]) - p3_plane2 = np.array([r2 * cos(phi2) + center[0], r2 * sin(phi2)+ center[1], 0.]) + p2_plane2 = np.array( + [r1 * cos(phi2) + center[0], r1 * sin(phi2) + center[1], 0.0] + ) + p3_plane2 = np.array( + [r2 * cos(phi2) + center[0], r2 * sin(phi2) + center[1], 0.0] + ) points = [p1, p2_plane1, p3_plane1, p2_plane2, p3_plane2] - if axis == 'z': + if axis == "z": coord_map = [0, 1, 2] self.inner_cyl = openmc.ZCylinder(*center, r1, **kwargs) self.outer_cyl = openmc.ZCylinder(*center, r2, **kwargs) - elif axis == 'y': + elif axis == "y": coord_map = [0, 2, 1] self.inner_cyl = openmc.YCylinder(*center, r1, **kwargs) self.outer_cyl = openmc.YCylinder(*center, r2, **kwargs) - elif axis == 'x': + elif axis == "x": coord_map = [2, 0, 1] self.inner_cyl = openmc.XCylinder(*center, r1, **kwargs) self.outer_cyl = openmc.XCylinder(*center, r2, **kwargs) @@ -161,20 +168,13 @@ def __init__(self, for p in points: p[:] = p[coord_map] - self.plane1 = openmc.Plane.from_points(p1, p2_plane1, p3_plane1, - **kwargs) - self.plane2 = openmc.Plane.from_points(p1, p2_plane2, p3_plane2, - **kwargs) + self.plane1 = openmc.Plane.from_points(p1, p2_plane1, p3_plane1, **kwargs) + self.plane2 = openmc.Plane.from_points(p1, p2_plane2, p3_plane2, **kwargs) @classmethod - def from_theta_alpha(cls, - r1, - r2, - theta, - alpha, - center = (0.,0.), - axis='z', - **kwargs): + def from_theta_alpha( + cls, r1, r2, theta, alpha, center=(0.0, 0.0), axis="z", **kwargs + ): r"""Alternate constructor for :class:`CylinderSector`. Returns a :class:`CylinderSector` object based on a central angle :math:`\theta` and an angular offset :math:`\alpha`. Note that @@ -210,8 +210,8 @@ def from_theta_alpha(cls, CylinderSector with the given central angle at the given offset. """ - if theta >= 360. or theta <= 0: - raise ValueError('theta must be less than 360 and greater than 0.') + if theta >= 360.0 or theta <= 0: + raise ValueError("theta must be less than 360 and greater than 0.") theta1 = alpha theta2 = alpha + theta @@ -284,12 +284,18 @@ class IsogonalOctagon(CompositeSurface): """ - _surface_names = ('top', 'bottom', - 'upper_right', 'lower_left', - 'right', 'left', - 'lower_right', 'upper_left') - - def __init__(self, center, r1, r2, axis='z', **kwargs): + _surface_names = ( + "top", + "bottom", + "upper_right", + "lower_left", + "right", + "left", + "lower_right", + "upper_left", + ) + + def __init__(self, center, r1, r2, axis="z", **kwargs): c1, c2 = center # Coordinates for axis-perpendicular planes @@ -301,22 +307,24 @@ def __init__(self, center, r1, r2, axis='z', **kwargs): # Side lengths if r2 > r1 * sqrt(2): - raise ValueError('r2 is greater than sqrt(2) * r1. Octagon' + - ' may be erroneous.') + raise ValueError( + "r2 is greater than sqrt(2) * r1. Octagon" + " may be erroneous." + ) if r1 > r2 * sqrt(2): - raise ValueError('r1 is greater than sqrt(2) * r2. Octagon' + - ' may be erroneous.') + raise ValueError( + "r1 is greater than sqrt(2) * r2. Octagon" + " may be erroneous." + ) - L_basis_ax = (r2 * sqrt(2) - r1) + L_basis_ax = r2 * sqrt(2) - r1 # Coordinates for quadrant planes - p1_ur = np.array([L_basis_ax, r1, 0.]) - p2_ur = np.array([r1, L_basis_ax, 0.]) - p3_ur = np.array([r1, L_basis_ax, 1.]) + p1_ur = np.array([L_basis_ax, r1, 0.0]) + p2_ur = np.array([r1, L_basis_ax, 0.0]) + p3_ur = np.array([r1, L_basis_ax, 1.0]) - p1_lr = np.array([r1, -L_basis_ax, 0.]) - p2_lr = np.array([L_basis_ax, -r1, 0.]) - p3_lr = np.array([L_basis_ax, -r1, 1.]) + p1_lr = np.array([r1, -L_basis_ax, 0.0]) + p2_lr = np.array([L_basis_ax, -r1, 0.0]) + p3_lr = np.array([L_basis_ax, -r1, 1.0]) p1_ll = -p1_ur p2_ll = -p2_ur @@ -326,23 +334,35 @@ def __init__(self, center, r1, r2, axis='z', **kwargs): p2_ul = -p2_lr p3_ul = -p3_lr - points = [p1_ur, p2_ur, p3_ur, p1_lr, p2_lr, p3_lr, - p1_ll, p2_ll, p3_ll, p1_ul, p2_ul, p3_ul] + points = [ + p1_ur, + p2_ur, + p3_ur, + p1_lr, + p2_lr, + p3_lr, + p1_ll, + p2_ll, + p3_ll, + p1_ul, + p2_ul, + p3_ul, + ] # Orientation specific variables - if axis == 'z': + if axis == "z": coord_map = [0, 1, 2] self.top = openmc.YPlane(ctop, **kwargs) self.bottom = openmc.YPlane(cbottom, **kwargs) self.right = openmc.XPlane(cright, **kwargs) self.left = openmc.XPlane(cleft, **kwargs) - elif axis == 'y': + elif axis == "y": coord_map = [0, 2, 1] self.top = openmc.ZPlane(ctop, **kwargs) self.bottom = openmc.ZPlane(cbottom, **kwargs) self.right = openmc.XPlane(cright, **kwargs) self.left = openmc.XPlane(cleft, **kwargs) - elif axis == 'x': + elif axis == "x": coord_map = [2, 0, 1] self.top = openmc.ZPlane(ctop, **kwargs) self.bottom = openmc.ZPlane(cbottom, **kwargs) @@ -356,36 +376,60 @@ def __init__(self, center, r1, r2, axis='z', **kwargs): p[1] += c2 p[:] = p[coord_map] - self.upper_right = openmc.Plane.from_points(p1_ur, p2_ur, p3_ur, - **kwargs) - self.lower_right = openmc.Plane.from_points(p1_lr, p2_lr, p3_lr, - **kwargs) - self.lower_left = openmc.Plane.from_points(p1_ll, p2_ll, p3_ll, - **kwargs) - self.upper_left = openmc.Plane.from_points(p1_ul, p2_ul, p3_ul, - **kwargs) + self.upper_right = openmc.Plane.from_points(p1_ur, p2_ur, p3_ur, **kwargs) + self.lower_right = openmc.Plane.from_points(p1_lr, p2_lr, p3_lr, **kwargs) + self.lower_left = openmc.Plane.from_points(p1_ll, p2_ll, p3_ll, **kwargs) + self.upper_left = openmc.Plane.from_points(p1_ul, p2_ul, p3_ul, **kwargs) def __neg__(self): - if self.axis == 'y': - region = -self.top & +self.bottom & -self.right & +self.left & \ - -self.upper_right & -self.lower_right & +self.lower_left & \ - +self.upper_left + if self.axis == "y": + region = ( + -self.top + & +self.bottom + & -self.right + & +self.left + & -self.upper_right + & -self.lower_right + & +self.lower_left + & +self.upper_left + ) else: - region = -self.top & +self.bottom & -self.right & +self.left & \ - +self.upper_right & +self.lower_right & -self.lower_left & \ - -self.upper_left + region = ( + -self.top + & +self.bottom + & -self.right + & +self.left + & +self.upper_right + & +self.lower_right + & -self.lower_left + & -self.upper_left + ) return region def __pos__(self): - if self.axis == 'y': - region = +self.top | -self.bottom | +self.right | -self.left | \ - +self.upper_right | +self.lower_right | -self.lower_left | \ - -self.upper_left + if self.axis == "y": + region = ( + +self.top + | -self.bottom + | +self.right + | -self.left + | +self.upper_right + | +self.lower_right + | -self.lower_left + | -self.upper_left + ) else: - region = +self.top | -self.bottom | +self.right | -self.left | \ - -self.upper_right | -self.lower_right | +self.lower_left | \ - +self.upper_left + region = ( + +self.top + | -self.bottom + | +self.right + | -self.left + | -self.upper_right + | -self.lower_right + | +self.lower_left + | +self.upper_left + ) return region @@ -445,108 +489,132 @@ class RightCircularCylinder(CompositeSurface): :attr:`lower_fillet_radius` is set. """ - _surface_names = ('cyl', 'bottom', 'top') - def __init__(self, center_base, height, radius, axis='z', - upper_fillet_radius=0., lower_fillet_radius=0., **kwargs): + _surface_names = ("cyl", "bottom", "top") + + def __init__( + self, + center_base, + height, + radius, + axis="z", + upper_fillet_radius=0.0, + lower_fillet_radius=0.0, + **kwargs, + ): cx, cy, cz = center_base - check_greater_than('cylinder height', height, 0.0) - check_greater_than('cylinder radius', radius, 0.0) - check_value('cylinder axis', axis, ('x', 'y', 'z')) - check_type('upper_fillet_radius', upper_fillet_radius, float) - check_less_than('upper_fillet_radius', upper_fillet_radius, - radius, equality=True) - check_type('lower_fillet_radius', lower_fillet_radius, float) - check_less_than('lower_fillet_radius', lower_fillet_radius, - radius, equality=True) - - if axis == 'x': + check_greater_than("cylinder height", height, 0.0) + check_greater_than("cylinder radius", radius, 0.0) + check_value("cylinder axis", axis, ("x", "y", "z")) + check_type("upper_fillet_radius", upper_fillet_radius, float) + check_less_than( + "upper_fillet_radius", upper_fillet_radius, radius, equality=True + ) + check_type("lower_fillet_radius", lower_fillet_radius, float) + check_less_than( + "lower_fillet_radius", lower_fillet_radius, radius, equality=True + ) + + if axis == "x": self.cyl = openmc.XCylinder(y0=cy, z0=cz, r=radius, **kwargs) self.bottom = openmc.XPlane(x0=cx, **kwargs) self.top = openmc.XPlane(x0=cx + height, **kwargs) - x1, x2 = 'y', 'z' + x1, x2 = "y", "z" axcoord, axcoord1, axcoord2 = 0, 1, 2 - elif axis == 'y': + elif axis == "y": self.cyl = openmc.YCylinder(x0=cx, z0=cz, r=radius, **kwargs) self.bottom = openmc.YPlane(y0=cy, **kwargs) self.top = openmc.YPlane(y0=cy + height, **kwargs) - x1, x2 = 'x', 'z' + x1, x2 = "x", "z" axcoord, axcoord1, axcoord2 = 1, 0, 2 - elif axis == 'z': + elif axis == "z": self.cyl = openmc.ZCylinder(x0=cx, y0=cy, r=radius, **kwargs) self.bottom = openmc.ZPlane(z0=cz, **kwargs) self.top = openmc.ZPlane(z0=cz + height, **kwargs) - x1, x2 = 'x', 'y' + x1, x2 = "x", "y" axcoord, axcoord1, axcoord2 = 2, 0, 1 - def _create_fillet_objects(axis_args, height, center_base, radius, fillet_radius, pos='upper'): + def _create_fillet_objects( + axis_args, height, center_base, radius, fillet_radius, pos="upper" + ): axis, x1, x2, axcoord, axcoord1, axcoord2 = axis_args fillet_ext = height / 2 - fillet_radius sign = 1 - if pos == 'lower': + if pos == "lower": sign = -1 coord = center_base[axcoord] + (height / 2) + sign * fillet_ext # cylinder - cyl_name = f'{pos}_min' + cyl_name = f"{pos}_min" cylinder_args = { - x1 + '0': center_base[axcoord1], - x2 + '0': center_base[axcoord2], - 'r': radius - fillet_radius + x1 + "0": center_base[axcoord1], + x2 + "0": center_base[axcoord2], + "r": radius - fillet_radius, } - cls = getattr(openmc, f'{axis.upper()}Cylinder') - cyl = cls(name=f'{cyl_name} {axis}', **cylinder_args) + cls = getattr(openmc, f"{axis.upper()}Cylinder") + cyl = cls(name=f"{cyl_name} {axis}", **cylinder_args) - #torus - tor_name = f'{axis} {pos}' + # torus + tor_name = f"{axis} {pos}" tor_args = { - 'a': radius - fillet_radius, - 'b': fillet_radius, - 'c': fillet_radius, - x1 + '0': center_base[axcoord1], - x2 + '0': center_base[axcoord2], - axis + '0': coord + "a": radius - fillet_radius, + "b": fillet_radius, + "c": fillet_radius, + x1 + "0": center_base[axcoord1], + x2 + "0": center_base[axcoord2], + axis + "0": coord, } - cls = getattr(openmc, f'{axis.upper()}Torus') + cls = getattr(openmc, f"{axis.upper()}Torus") torus = cls(name=tor_name, **tor_args) # plane - p_name = f'{pos} ext' - p_args = {axis + '0': coord} - cls = getattr(openmc, f'{axis.upper()}Plane') + p_name = f"{pos} ext" + p_args = {axis + "0": coord} + cls = getattr(openmc, f"{axis.upper()}Plane") plane = cls(name=p_name, **p_args) return cyl, torus, plane - if upper_fillet_radius > 0. or lower_fillet_radius > 0.: - if 'boundary_type' in kwargs: - if kwargs['boundary_type'] == 'periodic': - raise ValueError('Periodic boundary conditions not permitted when ' - 'rounded corners are used.') + if upper_fillet_radius > 0.0 or lower_fillet_radius > 0.0: + if "boundary_type" in kwargs: + if kwargs["boundary_type"] == "periodic": + raise ValueError( + "Periodic boundary conditions not permitted when " + "rounded corners are used." + ) axis_args = (axis, x1, x2, axcoord, axcoord1, axcoord2) - if upper_fillet_radius > 0.: + if upper_fillet_radius > 0.0: cylinder, torus, plane = _create_fillet_objects( - axis_args, height, center_base, radius, upper_fillet_radius) + axis_args, height, center_base, radius, upper_fillet_radius + ) self.upper_fillet_cylinder = cylinder self.upper_fillet_torus = torus self.upper_fillet_plane = plane - self._surface_names += ('upper_fillet_cylinder', - 'upper_fillet_torus', - 'upper_fillet_plane') + self._surface_names += ( + "upper_fillet_cylinder", + "upper_fillet_torus", + "upper_fillet_plane", + ) - if lower_fillet_radius > 0.: + if lower_fillet_radius > 0.0: cylinder, torus, plane = _create_fillet_objects( - axis_args, height, center_base, radius, lower_fillet_radius, - pos='lower' + axis_args, + height, + center_base, + radius, + lower_fillet_radius, + pos="lower", ) self.lower_fillet_cylinder = cylinder self.lower_fillet_torus = torus self.lower_fillet_plane = plane - self._surface_names += ('lower_fillet_cylinder', - 'lower_fillet_torus', - 'lower_fillet_plane') + self._surface_names += ( + "lower_fillet_cylinder", + "lower_fillet_torus", + "lower_fillet_plane", + ) def _get_fillet(self): upper_fillet = self._get_upper_fillet() @@ -564,17 +632,25 @@ def _get_fillet(self): return fillet def _get_upper_fillet(self): - has_upper_fillet = hasattr(self, 'upper_fillet_plane') + has_upper_fillet = hasattr(self, "upper_fillet_plane") if has_upper_fillet: - upper_fillet = +self.upper_fillet_cylinder & +self.upper_fillet_torus & +self.upper_fillet_plane + upper_fillet = ( + +self.upper_fillet_cylinder + & +self.upper_fillet_torus + & +self.upper_fillet_plane + ) else: upper_fillet = None return upper_fillet def _get_lower_fillet(self): - has_lower_fillet = hasattr(self, 'lower_fillet_plane') + has_lower_fillet = hasattr(self, "lower_fillet_plane") if has_lower_fillet: - lower_fillet = +self.lower_fillet_cylinder & +self.lower_fillet_torus & -self.lower_fillet_plane + lower_fillet = ( + +self.lower_fillet_cylinder + & +self.lower_fillet_torus + & -self.lower_fillet_plane + ) else: lower_fillet = None return lower_fillet @@ -625,15 +701,16 @@ class RectangularParallelepiped(CompositeSurface): Sides of the parallelepiped """ - _surface_names = ('xmin', 'xmax', 'ymin', 'ymax', 'zmin', 'zmax') + + _surface_names = ("xmin", "xmax", "ymin", "ymax", "zmin", "zmax") def __init__(self, xmin, xmax, ymin, ymax, zmin, zmax, **kwargs): if xmin >= xmax: - raise ValueError('xmin must be less than xmax') + raise ValueError("xmin must be less than xmax") if ymin >= ymax: - raise ValueError('ymin must be less than ymax') + raise ValueError("ymin must be less than ymax") if zmin >= zmax: - raise ValueError('zmin must be less than zmax') + raise ValueError("zmin must be less than zmax") self.xmin = openmc.XPlane(x0=xmin, **kwargs) self.xmax = openmc.XPlane(x0=xmax, **kwargs) self.ymin = openmc.YPlane(y0=ymin, **kwargs) @@ -642,10 +719,14 @@ def __init__(self, xmin, xmax, ymin, ymax, zmin, zmax, **kwargs): self.zmax = openmc.ZPlane(z0=zmax, **kwargs) def __neg__(self): - return -self.xmax & +self.xmin & -self.ymax & +self.ymin & -self.zmax & +self.zmin + return ( + -self.xmax & +self.xmin & -self.ymax & +self.ymin & -self.zmax & +self.zmin + ) def __pos__(self): - return +self.xmax | -self.xmin | +self.ymax | -self.ymin | +self.zmax | -self.zmin + return ( + +self.xmax | -self.xmin | +self.ymax | -self.ymin | +self.zmax | -self.zmin + ) class OrthogonalBox(CompositeSurface): @@ -679,7 +760,8 @@ class OrthogonalBox(CompositeSurface): Planes representing minimum and maximum along third axis """ - _surface_names = ('ax1_min', 'ax1_max', 'ax2_min', 'ax2_max', 'ax3_min', 'ax3_max') + + _surface_names = ("ax1_min", "ax1_max", "ax2_min", "ax2_max", "ax3_min", "ax3_max") def __init__(self, v, a1, a2, a3=None, **kwargs): v = np.array(v) @@ -710,21 +792,20 @@ def __init__(self, v, a1, a2, a3=None, **kwargs): # Make sure a point inside the box produces the correct senses. If not, # flip the plane coefficients so it does. - mid_point = v + (a1 + a2 + a3)/2 + mid_point = v + (a1 + a2 + a3) / 2 nums = (1, 2, 3) if has_a3 else (1, 2) for num in nums: - min_surf = getattr(self, f'ax{num}_min') - max_surf = getattr(self, f'ax{num}_max') + min_surf = getattr(self, f"ax{num}_min") + max_surf = getattr(self, f"ax{num}_max") if mid_point in -min_surf: min_surf.flip_normal() if mid_point in +max_surf: max_surf.flip_normal() def __neg__(self): - region = (+self.ax1_min & -self.ax1_max & - +self.ax2_min & -self.ax2_max) - if hasattr(self, 'ax3_min'): - region &= (+self.ax3_min & -self.ax3_max) + region = +self.ax1_min & -self.ax1_max & +self.ax2_min & -self.ax2_max + if hasattr(self, "ax3_min"): + region &= +self.ax3_min & -self.ax3_max return region @@ -770,10 +851,11 @@ class XConeOneSided(CompositeSurface): the ambiguity plane) """ - _surface_names = ('cone', 'plane') - def __init__(self, x0=0., y0=0., z0=0., r2=1., up=True, **kwargs): - check_greater_than('cone R^2', r2, 0.0) + _surface_names = ("cone", "plane") + + def __init__(self, x0=0.0, y0=0.0, z0=0.0, r2=1.0, up=True, **kwargs): + check_greater_than("cone R^2", r2, 0.0) self.cone = openmc.XCone(x0, y0, z0, r2, **kwargs) self.plane = openmc.XPlane(x0) self.up = up @@ -824,10 +906,11 @@ class YConeOneSided(CompositeSurface): the ambiguity plane) """ - _surface_names = ('cone', 'plane') - def __init__(self, x0=0., y0=0., z0=0., r2=1., up=True, **kwargs): - check_greater_than('cone R^2', r2, 0.0) + _surface_names = ("cone", "plane") + + def __init__(self, x0=0.0, y0=0.0, z0=0.0, r2=1.0, up=True, **kwargs): + check_greater_than("cone R^2", r2, 0.0) self.cone = openmc.YCone(x0, y0, z0, r2, **kwargs) self.plane = openmc.YPlane(y0) self.up = up @@ -877,10 +960,11 @@ class ZConeOneSided(CompositeSurface): the ambiguity plane) """ - _surface_names = ('cone', 'plane') - def __init__(self, x0=0., y0=0., z0=0., r2=1., up=True, **kwargs): - check_greater_than('cone R^2', r2, 0.0) + _surface_names = ("cone", "plane") + + def __init__(self, x0=0.0, y0=0.0, z0=0.0, r2=1.0, up=True, **kwargs): + check_greater_than("cone R^2", r2, 0.0) self.cone = openmc.ZCone(x0, y0, z0, r2, **kwargs) self.plane = openmc.ZPlane(z0) self.up = up @@ -917,8 +1001,8 @@ class Polygon(CompositeSurface): The union of all the regions comprising the polygon. """ - def __init__(self, points, basis='rz'): - check_value('basis', basis, ('xy', 'yz', 'xz', 'rz')) + def __init__(self, points, basis="rz"): + check_value("basis", basis, ("xy", "yz", "xz", "rz")) self._basis = basis # Create a constrained triangulation of the validated points. @@ -935,8 +1019,8 @@ def __init__(self, points, basis='rz'): for surfset in self._surfsets: for surf, op, on_boundary in surfset: if on_boundary: - setattr(self, f'surface_{i}', surf) - surfnames.append(f'surface_{i}') + setattr(self, f"surface_{i}", surf) + surfnames.append(f"surface_{i}") i += 1 self._surfnames = tuple(surfnames) @@ -958,13 +1042,15 @@ def _surface_names(self): @CompositeSurface.boundary_type.setter def boundary_type(self, boundary_type): - if boundary_type != 'transmission': - warnings.warn("Setting boundary_type to a value other than " - "'transmission' on Polygon composite surfaces can " - "result in unintended behavior. Please use the " - "regions property of the Polygon to generate " - "individual openmc.Cell objects to avoid unwanted " - "behavior.") + if boundary_type != "transmission": + warnings.warn( + "Setting boundary_type to a value other than " + "'transmission' on Polygon composite surfaces can " + "result in unintended behavior. Please use the " + "regions property of the Polygon to generate " + "individual openmc.Cell objects to avoid unwanted " + "behavior." + ) for name in self._surface_names: getattr(self, name).boundary_type = boundary_type @@ -981,7 +1067,7 @@ def _normals(self): """Generate the outward normal unit vectors for the polygon.""" # Rotation matrix for 90 degree clockwise rotation (-90 degrees about z # axis for an 'xy' basis). - rotation = np.array([[0., 1.], [-1., 0.]]) + rotation = np.array([[0.0, 1.0], [-1.0, 0.0]]) # Get the unit vectors that point from one point in the polygon to the # next given that they are ordered counterclockwise and that the final # point is connected to the first point @@ -997,7 +1083,7 @@ def _equations(self): normals = self._normals equations = np.empty((normals.shape[0], 3)) equations[:, :2] = normals - equations[:, 2] = -np.sum(normals*self.points, axis=-1) + equations[:, 2] = -np.sum(normals * self.points, axis=-1) return equations @property @@ -1022,17 +1108,17 @@ def _validate_points(self, points): ordered_points : the input points ordered counter-clockwise """ points = np.asarray(points, dtype=float) - check_iterable_type('points', points, float, min_depth=2, max_depth=2) - check_length('points', points[0, :], 2, 2) + check_iterable_type("points", points, float, min_depth=2, max_depth=2) + check_length("points", points[0, :], 2, 2) # If the last point is the same as the first, remove it and make sure # there are still at least 3 points for a valid polygon. if np.allclose(points[0, :], points[-1, :]): points = points[:-1, :] - check_length('points', points, 3) + check_length("points", points, 3) if len(points) != len(np.unique(points, axis=0)): - raise ValueError('Duplicate points were detected in the Polygon input') + raise ValueError("Duplicate points were detected in the Polygon input") # Order the points counter-clockwise (necessary for offset method) # Calculates twice the signed area of the polygon using the "Shoelace @@ -1040,7 +1126,7 @@ def _validate_points(self, points): # If signed area is positive the curve is oriented counter-clockwise. # If the signed area is negative the curve is oriented clockwise. xpts, ypts = points.T - if np.sum(ypts*(np.roll(xpts, 1) - np.roll(xpts, -1))) < 0: + if np.sum(ypts * (np.roll(xpts, 1) - np.roll(xpts, -1))) < 0: points = points[::-1, :] # Check if polygon is self-intersecting by comparing edges pairwise @@ -1052,13 +1138,13 @@ def _validate_points(self, points): p2 = np.append(points[j, :], 0) p3 = np.append(points[(j + 1) % n, :], 0) # Compute orientation of p0 wrt p2->p3 line segment - cp0 = np.cross(p3-p0, p2-p0)[-1] + cp0 = np.cross(p3 - p0, p2 - p0)[-1] # Compute orientation of p1 wrt p2->p3 line segment - cp1 = np.cross(p3-p1, p2-p1)[-1] + cp1 = np.cross(p3 - p1, p2 - p1)[-1] # Compute orientation of p2 wrt p0->p1 line segment - cp2 = np.cross(p1-p2, p0-p2)[-1] + cp2 = np.cross(p1 - p2, p0 - p2)[-1] # Compute orientation of p3 wrt p0->p1 line segment - cp3 = np.cross(p1-p3, p0-p3)[-1] + cp3 = np.cross(p1 - p3, p0 - p3)[-1] # Group cross products in an array and find out how many are 0 cross_products = np.array([[cp0, cp1], [cp2, cp3]]) @@ -1087,7 +1173,7 @@ def _validate_points(self, points): # and the orientations of p2 and p3 have opposite signs # then there is an intersection. if all(np.prod(cross_products, axis=-1) < 0): - raise ValueError('Polygon cannot be self-intersecting') + raise ValueError("Polygon cannot be self-intersecting") continue elif num_zeros == 1: @@ -1095,14 +1181,14 @@ def _validate_points(self, points): # points idx = np.argwhere(np.sum(cps_near_zero, axis=-1) == 0) if np.prod(cross_products[idx, :]) < 0: - raise ValueError('Polygon cannot be self-intersecting') + raise ValueError("Polygon cannot be self-intersecting") continue elif num_zeros == 2: continue elif num_zeros == 3: - warnings.warn('Unclear if Polygon is self-intersecting') + warnings.warn("Unclear if Polygon is self-intersecting") continue else: @@ -1116,7 +1202,7 @@ def _validate_points(self, points): xlap = xmin1 < xmax2 and xmin2 < xmax1 ylap = ymin1 < ymax2 and ymin2 < ymax1 if xlap or ylap: - raise ValueError('Polygon cannot be self-intersecting') + raise ValueError("Polygon cannot be self-intersecting") continue return points @@ -1137,10 +1223,11 @@ def _constrain_triangulation(self, points, depth=0): """ # Only attempt the triangulation up to 5 times. if depth > 4: - raise RuntimeError('Could not create a valid triangulation after 5' - ' attempts') + raise RuntimeError( + "Could not create a valid triangulation after 5" " attempts" + ) - tri = Delaunay(points, qhull_options='QJ') + tri = Delaunay(points, qhull_options="QJ") # Loop through the boundary edges of the polygon. If an edge is not # included in the triangulation, break it into two line segments. n = len(points) @@ -1199,7 +1286,7 @@ def _group_simplices(self, neighbor_map, group=None): test_group = group + [n] test_point_idx = np.unique(self._tri.simplices[test_group, :]) test_points = self.points[test_point_idx] - test_hull = ConvexHull(test_points, qhull_options='Qc') + test_hull = ConvexHull(test_points, qhull_options="Qc") pts_on_hull = len(test_hull.vertices) + len(test_hull.coplanar) # If test_points are convex (including coplanar) keep adding to # this group @@ -1232,33 +1319,33 @@ def _get_convex_hull_surfs(self, qhull): on_boundary = any([np.allclose(facet_eq, eq) for eq in boundary_eqns]) # Check if the facet is horizontal if isclose(dx, 0, abs_tol=1e-8): - if basis in ('xz', 'yz', 'rz'): - surf = openmc.ZPlane(z0=-c/dy) + if basis in ("xz", "yz", "rz"): + surf = openmc.ZPlane(z0=-c / dy) else: - surf = openmc.YPlane(y0=-c/dy) + surf = openmc.YPlane(y0=-c / dy) # if (0, 1).(dx, dy) < 0 we want positive halfspace instead op = operator.pos if dy < 0 else operator.neg # Check if the facet is vertical elif isclose(dy, 0, abs_tol=1e-8): - if basis in ('xy', 'xz'): - surf = openmc.XPlane(x0=-c/dx) - elif basis == 'yz': - surf = openmc.YPlane(y0=-c/dx) + if basis in ("xy", "xz"): + surf = openmc.XPlane(x0=-c / dx) + elif basis == "yz": + surf = openmc.YPlane(y0=-c / dx) else: - surf = openmc.ZCylinder(r=-c/dx) + surf = openmc.ZCylinder(r=-c / dx) # if (1, 0).(dx, dy) < 0 we want positive halfspace instead op = operator.pos if dx < 0 else operator.neg # Otherwise the facet is at an angle else: op = operator.neg - if basis == 'xy': + if basis == "xy": surf = openmc.Plane(a=dx, b=dy, d=-c) - elif basis == 'yz': + elif basis == "yz": surf = openmc.Plane(b=dx, c=dy, d=-c) - elif basis == 'xz': + elif basis == "xz": surf = openmc.Plane(a=dx, c=dy, d=-c) else: - y0 = -c/dy + y0 = -c / dy r2 = dy**2 / dx**2 # Check if the *slope* of the facet is positive. If dy/dx < 0 # then we want up to be True for the one-sided cones. @@ -1296,7 +1383,7 @@ def _decompose_polygon_into_convex_sets(self): for i, nlist in enumerate(self._tri.neighbors): if not in_polygon[i]: continue - neighbor_map[i] = [n for n in nlist if in_polygon[n] and n >=0] + neighbor_map[i] = [n for n in nlist if in_polygon[n] and n >= 0] # Get the groups of simplices forming convex polygons whose union # comprises the full input polygon. While there are still simplices @@ -1351,9 +1438,9 @@ def offset(self, distance: float | Sequence[float] | np.ndarray) -> Polygon: ) normals = np.insert(self._normals, 0, self._normals[-1, :], axis=0) - cos2theta = np.sum(normals[1:, :]*normals[:-1, :], axis=-1, keepdims=True) + cos2theta = np.sum(normals[1:, :] * normals[:-1, :], axis=-1, keepdims=True) costheta = np.cos(np.arccos(cos2theta) / 2) - nvec = (normals[1:, :] + normals[:-1, :]) + nvec = normals[1:, :] + normals[:-1, :] unit_nvec = nvec / np.linalg.norm(nvec, axis=-1, keepdims=True) disp_vec = distance[:, np.newaxis] / costheta * unit_nvec @@ -1384,17 +1471,17 @@ class CruciformPrism(CompositeSurface): """ - def __init__(self, distances, center=(0., 0.), axis='z', **kwargs): + def __init__(self, distances, center=(0.0, 0.0), axis="z", **kwargs): x0, y0 = center self.distances = distances - if axis == 'x': + if axis == "x": cls_horizontal = openmc.YPlane cls_vertical = openmc.ZPlane - elif axis == 'y': + elif axis == "y": cls_horizontal = openmc.XPlane cls_vertical = openmc.ZPlane - elif axis == 'z': + elif axis == "z": cls_horizontal = openmc.XPlane cls_vertical = openmc.YPlane else: @@ -1403,11 +1490,11 @@ def __init__(self, distances, center=(0., 0.), axis='z', **kwargs): # Create each planar surface surfnames = [] for i, d in enumerate(distances): - setattr(self, f'hmin{i}', cls_horizontal(x0 - d, **kwargs)) - setattr(self, f'hmax{i}', cls_horizontal(x0 + d, **kwargs)) - setattr(self, f'vmin{i}', cls_vertical(y0 - d, **kwargs)) - setattr(self, f'vmax{i}', cls_vertical(y0 + d, **kwargs)) - surfnames.extend([f'hmin{i}', f'hmax{i}', f'vmin{i}', f'vmax{i}']) + setattr(self, f"hmin{i}", cls_horizontal(x0 - d, **kwargs)) + setattr(self, f"hmax{i}", cls_horizontal(x0 + d, **kwargs)) + setattr(self, f"vmin{i}", cls_vertical(y0 - d, **kwargs)) + setattr(self, f"vmax{i}", cls_vertical(y0 + d, **kwargs)) + surfnames.extend([f"hmin{i}", f"hmax{i}", f"vmin{i}", f"vmax{i}"]) # Set _surfnames to satisfy CompositeSurface protocol self._surfnames = tuple(surfnames) @@ -1437,19 +1524,18 @@ def __neg__(self): regions = [] for i in range(n): regions.append( - +getattr(self, f'hmin{i}') & - -getattr(self, f'hmax{i}') & - +getattr(self, f'vmin{n-1-i}') & - -getattr(self, f'vmax{n-1-i}') + +getattr(self, f"hmin{i}") + & -getattr(self, f"hmax{i}") + & +getattr(self, f"vmin{n-1-i}") + & -getattr(self, f"vmax{n-1-i}") ) return openmc.Union(regions) # Define function to create a plane on given axis -def _plane(axis, name, value, boundary_type='transmission', albedo=1.0): - cls = getattr(openmc, f'{axis.upper()}Plane') - return cls(value, name=f'{name} {axis}', - boundary_type=boundary_type, albedo=albedo) +def _plane(axis, name, value, boundary_type="transmission", albedo=1.0): + cls = getattr(openmc, f"{axis.upper()}Plane") + return cls(value, name=f"{name} {axis}", boundary_type=boundary_type, albedo=albedo) class RectangularPrism(CompositeSurface): @@ -1482,94 +1568,111 @@ class RectangularPrism(CompositeSurface): Prism corner radius in units of [cm]. """ - _surface_names = ('min_x1', 'max_x1', 'min_x2', 'max_x2') + + _surface_names = ("min_x1", "max_x1", "min_x2", "max_x2") def __init__( - self, - width: float, - height: float, - axis: str = 'z', - origin: Sequence[float] = (0., 0.), - boundary_type: str = 'transmission', - albedo: float = 1., - corner_radius: float = 0. - ): - check_type('width', width, Real) - check_type('height', height, Real) - check_type('albedo', albedo, Real) - check_type('corner_radius', corner_radius, Real) - check_value('axis', axis, ('x', 'y', 'z')) - check_type('origin', origin, Iterable, Real) - - if axis == 'x': - x1, x2 = 'y', 'z' - elif axis == 'y': - x1, x2 = 'x', 'z' + self, + width: float, + height: float, + axis: str = "z", + origin: Sequence[float] = (0.0, 0.0), + boundary_type: str = "transmission", + albedo: float = 1.0, + corner_radius: float = 0.0, + ): + check_type("width", width, Real) + check_type("height", height, Real) + check_type("albedo", albedo, Real) + check_type("corner_radius", corner_radius, Real) + check_value("axis", axis, ("x", "y", "z")) + check_type("origin", origin, Iterable, Real) + + if axis == "x": + x1, x2 = "y", "z" + elif axis == "y": + x1, x2 = "x", "z" else: - x1, x2 = 'x', 'y' + x1, x2 = "x", "y" # Get cylinder class corresponding to given axis - cyl = getattr(openmc, f'{axis.upper()}Cylinder') + cyl = getattr(openmc, f"{axis.upper()}Cylinder") # Create container for boundary arguments - bc_args = {'boundary_type': boundary_type, 'albedo': albedo} + bc_args = {"boundary_type": boundary_type, "albedo": albedo} # Create rectangular region - self.min_x1 = _plane(x1, 'minimum', -width/2 + origin[0], **bc_args) - self.max_x1 = _plane(x1, 'maximum', width/2 + origin[0], **bc_args) - self.min_x2 = _plane(x2, 'minimum', -height/2 + origin[1], **bc_args) - self.max_x2 = _plane(x2, 'maximum', height/2 + origin[1], **bc_args) - if boundary_type == 'periodic': + self.min_x1 = _plane(x1, "minimum", -width / 2 + origin[0], **bc_args) + self.max_x1 = _plane(x1, "maximum", width / 2 + origin[0], **bc_args) + self.min_x2 = _plane(x2, "minimum", -height / 2 + origin[1], **bc_args) + self.max_x2 = _plane(x2, "maximum", height / 2 + origin[1], **bc_args) + if boundary_type == "periodic": self.min_x1.periodic_surface = self.max_x1 self.min_x2.periodic_surface = self.max_x2 # Handle rounded corners if given - if corner_radius > 0.: - if boundary_type == 'periodic': - raise ValueError('Periodic boundary conditions not permitted when ' - 'rounded corners are used.') - - args = {'r': corner_radius, 'boundary_type': boundary_type, 'albedo': albedo} - - args[x1 + '0'] = origin[0] - width/2 + corner_radius - args[x2 + '0'] = origin[1] - height/2 + corner_radius - self.x1_min_x2_min = cyl(name=f'{x1} min {x2} min', **args) - - args[x1 + '0'] = origin[0] - width/2 + corner_radius - args[x2 + '0'] = origin[1] + height/2 - corner_radius - self.x1_min_x2_max = cyl(name=f'{x1} min {x2} max', **args) - - args[x1 + '0'] = origin[0] + width/2 - corner_radius - args[x2 + '0'] = origin[1] - height/2 + corner_radius - self.x1_max_x2_min = cyl(name=f'{x1} max {x2} min', **args) - - args[x1 + '0'] = origin[0] + width/2 - corner_radius - args[x2 + '0'] = origin[1] + height/2 - corner_radius - self.x1_max_x2_max = cyl(name=f'{x1} max {x2} max', **args) - - self.x1_min = _plane(x1, 'min', -width/2 + origin[0] + corner_radius, - **bc_args) - self.x1_max = _plane(x1, 'max', width/2 + origin[0] - corner_radius, - **bc_args) - self.x2_min = _plane(x2, 'min', -height/2 + origin[1] + corner_radius, - **bc_args) - self.x2_max = _plane(x2, 'max', height/2 + origin[1] - corner_radius, - **bc_args) + if corner_radius > 0.0: + if boundary_type == "periodic": + raise ValueError( + "Periodic boundary conditions not permitted when " + "rounded corners are used." + ) + + args = { + "r": corner_radius, + "boundary_type": boundary_type, + "albedo": albedo, + } + + args[x1 + "0"] = origin[0] - width / 2 + corner_radius + args[x2 + "0"] = origin[1] - height / 2 + corner_radius + self.x1_min_x2_min = cyl(name=f"{x1} min {x2} min", **args) + + args[x1 + "0"] = origin[0] - width / 2 + corner_radius + args[x2 + "0"] = origin[1] + height / 2 - corner_radius + self.x1_min_x2_max = cyl(name=f"{x1} min {x2} max", **args) + + args[x1 + "0"] = origin[0] + width / 2 - corner_radius + args[x2 + "0"] = origin[1] - height / 2 + corner_radius + self.x1_max_x2_min = cyl(name=f"{x1} max {x2} min", **args) + + args[x1 + "0"] = origin[0] + width / 2 - corner_radius + args[x2 + "0"] = origin[1] + height / 2 - corner_radius + self.x1_max_x2_max = cyl(name=f"{x1} max {x2} max", **args) + + self.x1_min = _plane( + x1, "min", -width / 2 + origin[0] + corner_radius, **bc_args + ) + self.x1_max = _plane( + x1, "max", width / 2 + origin[0] - corner_radius, **bc_args + ) + self.x2_min = _plane( + x2, "min", -height / 2 + origin[1] + corner_radius, **bc_args + ) + self.x2_max = _plane( + x2, "max", height / 2 + origin[1] - corner_radius, **bc_args + ) self._surface_names += ( - 'x1_min_x2_min', 'x1_min_x2_max', 'x1_max_x2_min', - 'x1_max_x2_max', 'x1_min', 'x1_max', 'x2_min', 'x2_max' + "x1_min_x2_min", + "x1_min_x2_max", + "x1_max_x2_min", + "x1_max_x2_max", + "x1_min", + "x1_max", + "x2_min", + "x2_max", ) def __neg__(self): prism = +self.min_x1 & -self.max_x1 & +self.min_x2 & -self.max_x2 # Cut out corners if a corner radius was given - if hasattr(self, 'x1_min'): + if hasattr(self, "x1_min"): corners = ( - (+self.x1_min_x2_min & -self.x1_min & -self.x2_min) | - (+self.x1_min_x2_max & -self.x1_min & +self.x2_max) | - (+self.x1_max_x2_min & +self.x1_max & -self.x2_min) | - (+self.x1_max_x2_max & +self.x1_max & +self.x2_max) + (+self.x1_min_x2_min & -self.x1_min & -self.x2_min) + | (+self.x1_min_x2_max & -self.x1_min & +self.x2_max) + | (+self.x1_max_x2_min & +self.x1_max & -self.x2_min) + | (+self.x1_max_x2_max & +self.x1_max & +self.x2_max) ) prism &= ~corners @@ -1603,137 +1706,187 @@ class HexagonalPrism(CompositeSurface): Prism corner radius in units of [cm]. """ - _surface_names = ('plane_max', 'plane_min', 'upper_right', 'upper_left', - 'lower_right', 'lower_left') + + _surface_names = ( + "plane_max", + "plane_min", + "upper_right", + "upper_left", + "lower_right", + "lower_left", + ) def __init__( - self, - edge_length: float = 1., - orientation: str = 'y', - origin: Sequence[float] = (0., 0.), - boundary_type: str = 'transmission', - albedo: float = 1., - corner_radius: float = 0. + self, + edge_length: float = 1.0, + orientation: str = "y", + origin: Sequence[float] = (0.0, 0.0), + boundary_type: str = "transmission", + albedo: float = 1.0, + corner_radius: float = 0.0, ): - check_type('edge_length', edge_length, Real) - check_type('albedo', albedo, Real) - check_type('corner_radius', corner_radius, Real) - check_value('orientation', orientation, ('x', 'y')) - check_type('origin', origin, Iterable, Real) + check_type("edge_length", edge_length, Real) + check_type("albedo", albedo, Real) + check_type("corner_radius", corner_radius, Real) + check_value("orientation", orientation, ("x", "y")) + check_type("origin", origin, Iterable, Real) l = edge_length x, y = origin # Create container for boundary arguments - bc_args = {'boundary_type': boundary_type, 'albedo': albedo} + bc_args = {"boundary_type": boundary_type, "albedo": albedo} - if orientation == 'y': + if orientation == "y": # Left and right planes - self.plane_max = openmc.XPlane(x + sqrt(3.)/2*l, **bc_args) - self.plane_min = openmc.XPlane(x - sqrt(3.)/2*l, **bc_args) - c = sqrt(3.)/3. + self.plane_max = openmc.XPlane(x + sqrt(3.0) / 2 * l, **bc_args) + self.plane_min = openmc.XPlane(x - sqrt(3.0) / 2 * l, **bc_args) + c = sqrt(3.0) / 3.0 # y = -x/sqrt(3) + a - self.upper_right = openmc.Plane(a=c, b=1., d=l+x*c+y, **bc_args) + self.upper_right = openmc.Plane(a=c, b=1.0, d=l + x * c + y, **bc_args) # y = x/sqrt(3) + a - self.upper_left = openmc.Plane(a=-c, b=1., d=l-x*c+y, **bc_args) + self.upper_left = openmc.Plane(a=-c, b=1.0, d=l - x * c + y, **bc_args) # y = x/sqrt(3) - a - self.lower_right = openmc.Plane(a=-c, b=1., d=-l-x*c+y, **bc_args) + self.lower_right = openmc.Plane(a=-c, b=1.0, d=-l - x * c + y, **bc_args) # y = -x/sqrt(3) - a - self.lower_left = openmc.Plane(a=c, b=1., d=-l+x*c+y, **bc_args) + self.lower_left = openmc.Plane(a=c, b=1.0, d=-l + x * c + y, **bc_args) - elif orientation == 'x': - self.plane_max = openmc.YPlane(y + sqrt(3.)/2*l, **bc_args) - self.plane_min = openmc.YPlane(y - sqrt(3.)/2*l, **bc_args) - c = sqrt(3.) + elif orientation == "x": + self.plane_max = openmc.YPlane(y + sqrt(3.0) / 2 * l, **bc_args) + self.plane_min = openmc.YPlane(y - sqrt(3.0) / 2 * l, **bc_args) + c = sqrt(3.0) # Upper-right surface: y = -sqrt(3)*(x - a) - self.upper_right = openmc.Plane(a=c, b=1., d=c*l+x*c+y, **bc_args) + self.upper_right = openmc.Plane(a=c, b=1.0, d=c * l + x * c + y, **bc_args) # Lower-right surface: y = sqrt(3)*(x + a) - self.lower_right = openmc.Plane(a=-c, b=1., d=-c*l-x*c+y, **bc_args) + self.lower_right = openmc.Plane( + a=-c, b=1.0, d=-c * l - x * c + y, **bc_args + ) # Lower-left surface: y = -sqrt(3)*(x + a) - self.lower_left = openmc.Plane(a=c, b=1., d=-c*l+x*c+y, **bc_args) + self.lower_left = openmc.Plane(a=c, b=1.0, d=-c * l + x * c + y, **bc_args) # Upper-left surface: y = sqrt(3)*(x + a) - self.upper_left = openmc.Plane(a=-c, b=1., d=c*l-x*c+y, **bc_args) + self.upper_left = openmc.Plane(a=-c, b=1.0, d=c * l - x * c + y, **bc_args) # Handle periodic boundary conditions - if boundary_type == 'periodic': + if boundary_type == "periodic": self.plane_min.periodic_surface = self.plane_max self.upper_right.periodic_surface = self.lower_left self.lower_right.periodic_surface = self.upper_left # Handle rounded corners if given - if corner_radius > 0.: - if boundary_type == 'periodic': - raise ValueError('Periodic boundary conditions not permitted ' - 'when rounded corners are used.') + if corner_radius > 0.0: + if boundary_type == "periodic": + raise ValueError( + "Periodic boundary conditions not permitted " + "when rounded corners are used." + ) - c = sqrt(3.)/2 - t = l - corner_radius/c + c = sqrt(3.0) / 2 + t = l - corner_radius / c # Cylinder with corner radius and boundary type pre-applied cyl1 = partial(openmc.ZCylinder, r=corner_radius, **bc_args) - cyl2 = partial(openmc.ZCylinder, r=corner_radius/(2*c), **bc_args) - - if orientation == 'x': - self.x_min_y_min_in = cyl1(name='x min y min in', x0=x-t/2, y0=y-c*t) - self.x_min_y_max_in = cyl1(name='x min y max in', x0=x+t/2, y0=y-c*t) - self.x_max_y_min_in = cyl1(name='x max y min in', x0=x-t/2, y0=y+c*t) - self.x_max_y_max_in = cyl1(name='x max y max in', x0=x+t/2, y0=y+c*t) - self.min_in = cyl1(name='x min in', x0=x-t, y0=y) - self.max_in = cyl1(name='x max in', x0=x+t, y0=y) - - self.x_min_y_min_out = cyl2(name='x min y min out', x0=x-l/2, y0=y-c*l) - self.x_min_y_max_out = cyl2(name='x min y max out', x0=x+l/2, y0=y-c*l) - self.x_max_y_min_out = cyl2(name='x max y min out', x0=x-l/2, y0=y+c*l) - self.x_max_y_max_out = cyl2(name='x max y max out', x0=x+l/2, y0=y+c*l) - self.min_out = cyl2(name='x min out', x0=x-l, y0=y) - self.max_out = cyl2(name='x max out', x0=x+l, y0=y) - - elif orientation == 'y': - self.x_min_y_min_in = cyl1(name='x min y min in', x0=x-c*t, y0=y-t/2) - self.x_min_y_max_in = cyl1(name='x min y max in', x0=x-c*t, y0=y+t/2) - self.x_max_y_min_in = cyl1(name='x max y min in', x0=x+c*t, y0=y-t/2) - self.x_max_y_max_in = cyl1(name='x max y max in', x0=x+c*t, y0=y+t/2) - self.min_in = cyl1(name='y min in', x0=x, y0=y-t) - self.max_in = cyl1(name='y max in', x0=x, y0=y+t) - - self.x_min_y_min_out = cyl2(name='x min y min out', x0=x-c*l, y0=y-l/2) - self.x_min_y_max_out = cyl2(name='x min y max out', x0=x-c*l, y0=y+l/2) - self.x_max_y_min_out = cyl2(name='x max y min out', x0=x+c*l, y0=y-l/2) - self.x_max_y_max_out = cyl2(name='x max y max out', x0=x+c*l, y0=y+l/2) - self.min_out = cyl2(name='y min out', x0=x, y0=y-l) - self.max_out = cyl2(name='y max out', x0=x, y0=y+l) + cyl2 = partial(openmc.ZCylinder, r=corner_radius / (2 * c), **bc_args) + + if orientation == "x": + self.x_min_y_min_in = cyl1( + name="x min y min in", x0=x - t / 2, y0=y - c * t + ) + self.x_min_y_max_in = cyl1( + name="x min y max in", x0=x + t / 2, y0=y - c * t + ) + self.x_max_y_min_in = cyl1( + name="x max y min in", x0=x - t / 2, y0=y + c * t + ) + self.x_max_y_max_in = cyl1( + name="x max y max in", x0=x + t / 2, y0=y + c * t + ) + self.min_in = cyl1(name="x min in", x0=x - t, y0=y) + self.max_in = cyl1(name="x max in", x0=x + t, y0=y) + + self.x_min_y_min_out = cyl2( + name="x min y min out", x0=x - l / 2, y0=y - c * l + ) + self.x_min_y_max_out = cyl2( + name="x min y max out", x0=x + l / 2, y0=y - c * l + ) + self.x_max_y_min_out = cyl2( + name="x max y min out", x0=x - l / 2, y0=y + c * l + ) + self.x_max_y_max_out = cyl2( + name="x max y max out", x0=x + l / 2, y0=y + c * l + ) + self.min_out = cyl2(name="x min out", x0=x - l, y0=y) + self.max_out = cyl2(name="x max out", x0=x + l, y0=y) + + elif orientation == "y": + self.x_min_y_min_in = cyl1( + name="x min y min in", x0=x - c * t, y0=y - t / 2 + ) + self.x_min_y_max_in = cyl1( + name="x min y max in", x0=x - c * t, y0=y + t / 2 + ) + self.x_max_y_min_in = cyl1( + name="x max y min in", x0=x + c * t, y0=y - t / 2 + ) + self.x_max_y_max_in = cyl1( + name="x max y max in", x0=x + c * t, y0=y + t / 2 + ) + self.min_in = cyl1(name="y min in", x0=x, y0=y - t) + self.max_in = cyl1(name="y max in", x0=x, y0=y + t) + + self.x_min_y_min_out = cyl2( + name="x min y min out", x0=x - c * l, y0=y - l / 2 + ) + self.x_min_y_max_out = cyl2( + name="x min y max out", x0=x - c * l, y0=y + l / 2 + ) + self.x_max_y_min_out = cyl2( + name="x max y min out", x0=x + c * l, y0=y - l / 2 + ) + self.x_max_y_max_out = cyl2( + name="x max y max out", x0=x + c * l, y0=y + l / 2 + ) + self.min_out = cyl2(name="y min out", x0=x, y0=y - l) + self.max_out = cyl2(name="y max out", x0=x, y0=y + l) # Add to tuple of surface names - for s in ('in', 'out'): + for s in ("in", "out"): self._surface_names += ( - f'x_min_y_min_{s}', f'x_min_y_max_{s}', - f'x_max_y_min_{s}', f'x_max_y_max_{s}', - f'min_{s}', f'max_{s}') + f"x_min_y_min_{s}", + f"x_min_y_max_{s}", + f"x_max_y_min_{s}", + f"x_max_y_max_{s}", + f"min_{s}", + f"max_{s}", + ) def __neg__(self) -> openmc.Region: prism = ( - -self.plane_max & +self.plane_min & - -self.upper_right & -self.upper_left & - +self.lower_right & +self.lower_left + -self.plane_max + & +self.plane_min + & -self.upper_right + & -self.upper_left + & +self.lower_right + & +self.lower_left ) # Cut out corners if a corner radius was given - if hasattr(self, 'min_in'): + if hasattr(self, "min_in"): corners = ( - +self.x_min_y_min_in & -self.x_min_y_min_out | - +self.x_min_y_max_in & -self.x_min_y_max_out | - +self.x_max_y_min_in & -self.x_max_y_min_out | - +self.x_max_y_max_in & -self.x_max_y_max_out | - +self.min_in & -self.min_out | - +self.max_in & -self.max_out + +self.x_min_y_min_in & -self.x_min_y_min_out + | +self.x_min_y_max_in & -self.x_min_y_max_out + | +self.x_max_y_min_in & -self.x_max_y_min_out + | +self.x_max_y_max_in & -self.x_max_y_max_out + | +self.min_in & -self.min_out + | +self.max_in & -self.max_out ) prism &= ~corners @@ -1764,10 +1917,10 @@ def _rotation_matrix(v1, v2): # Handle special case where vectors are parallel or anti-parallel if isclose(abs(cos_angle), 1.0, rel_tol=1e-8): - return np.sign(cos_angle)*I + return np.sign(cos_angle) * I else: # Calculate rotation angle - sin_angle = np.sqrt(1 - cos_angle*cos_angle) + sin_angle = np.sqrt(1 - cos_angle * cos_angle) # Calculate axis of rotation axis = np.cross(u1, u2) @@ -1775,11 +1928,7 @@ def _rotation_matrix(v1, v2): # Create cross-product matrix K kx, ky, kz = axis - K = np.array([ - [0.0, -kz, ky], - [kz, 0.0, -kx], - [-ky, kx, 0.0] - ]) + K = np.array([[0.0, -kz, ky], [kz, 0.0, -kx], [-ky, kx, 0.0]]) # Create rotation matrix using Rodrigues' rotation formula return I + K * sin_angle + (K @ K) * (1 - cos_angle) @@ -1820,10 +1969,17 @@ class ConicalFrustum(CompositeSurface): Plane surface defining the top of the frustum """ - _surface_names = ('cone', 'plane_bottom', 'plane_top') - def __init__(self, center_base: Sequence[float], axis: Sequence[float], - r1: float, r2: float, **kwargs): + _surface_names = ("cone", "plane_bottom", "plane_top") + + def __init__( + self, + center_base: Sequence[float], + axis: Sequence[float], + r1: float, + r2: float, + **kwargs, + ): center_base = np.array(center_base) axis = np.array(axis) @@ -1836,8 +1992,8 @@ def __init__(self, center_base: Sequence[float], axis: Sequence[float], # as a reference. x0, y0, z0 = center_base if r1 != r2: - apex = z0 + r1*h/(r1 - r2) - r_sq = ((r1 - r2)/h)**2 + apex = z0 + r1 * h / (r1 - r2) + r_sq = ((r1 - r2) / h) ** 2 cone = openmc.ZCone(x0, y0, apex, r2=r_sq, **kwargs) else: # In the degenerate case r1 == r2, the cone becomes a cylinder @@ -1848,7 +2004,7 @@ def __init__(self, center_base: Sequence[float], axis: Sequence[float], plane_top = openmc.ZPlane(z0 + h, **kwargs) # Determine rotation matrix corresponding to specified axis - u = np.array([0., 0., 1.]) + u = np.array([0.0, 0.0, 1.0]) rotation = _rotation_matrix(u, axis) # Rotate the surfaces @@ -1889,17 +2045,26 @@ class Vessel(CompositeSurface): """ - _surface_names = ('cyl', 'plane_bottom', 'plane_top', 'bottom', 'top') + _surface_names = ("cyl", "plane_bottom", "plane_top", "bottom", "top") - def __init__(self, r: float, p1: float, p2: float, h1: float, h2: float, - center: Sequence[float] = (0., 0.), axis: str = 'z', **kwargs): + def __init__( + self, + r: float, + p1: float, + p2: float, + h1: float, + h2: float, + center: Sequence[float] = (0.0, 0.0), + axis: str = "z", + **kwargs, + ): if p1 >= p2: - raise ValueError('p1 must be less than p2') - check_value('axis', axis, {'x', 'y', 'z'}) + raise ValueError("p1 must be less than p2") + check_value("axis", axis, {"x", "y", "z"}) c1, c2 = center - cyl_class = getattr(openmc, f'{axis.upper()}Cylinder') - plane_class = getattr(openmc, f'{axis.upper()}Plane') + cyl_class = getattr(openmc, f"{axis.upper()}Cylinder") + plane_class = getattr(openmc, f"{axis.upper()}Plane") self.cyl = cyl_class(c1, c2, r, **kwargs) self.plane_bottom = plane_class(p1) self.plane_top = plane_class(p2) @@ -1911,43 +2076,45 @@ def __init__(self, r: float, p1: float, p2: float, h1: float, h2: float, # (x² - 2xā‚€x + x₀²) + (y² - 2yā‚€y + y₀²) + (z² - 2zā‚€z + z₀²)s² = r² # x² + y² + s²z² - 2xā‚€x - 2yā‚€y - 2s²zā‚€z + (x₀² + y₀² + z₀²s² - r²) = 0 - sb = (r/h1) - st = (r/h2) - kwargs['a'] = kwargs['b'] = kwargs['c'] = 1.0 + sb = r / h1 + st = r / h2 + kwargs["a"] = kwargs["b"] = kwargs["c"] = 1.0 kwargs_bottom = kwargs kwargs_top = kwargs.copy() - sb2 = sb*sb - st2 = st*st - kwargs_bottom['k'] = c1*c1 + c2*c2 + p1*p1*sb2 - r*r - kwargs_top['k'] = c1*c1 + c2*c2 + p2*p2*st2 - r*r - - if axis == 'x': - kwargs_bottom['a'] *= sb2 - kwargs_top['a'] *= st2 - kwargs_bottom['g'] = -2*p1*sb2 - kwargs_top['g'] = -2*p2*st2 - kwargs_top['h'] = kwargs_bottom['h'] = -2*c1 - kwargs_top['j'] = kwargs_bottom['j'] = -2*c2 - elif axis == 'y': - kwargs_bottom['b'] *= sb2 - kwargs_top['b'] *= st2 - kwargs_top['g'] = kwargs_bottom['g'] = -2*c1 - kwargs_bottom['h'] = -2*p1*sb2 - kwargs_top['h'] = -2*p2*st2 - kwargs_top['j'] = kwargs_bottom['j'] = -2*c2 - elif axis == 'z': - kwargs_bottom['c'] *= sb2 - kwargs_top['c'] *= st2 - kwargs_top['g'] = kwargs_bottom['g'] = -2*c1 - kwargs_top['h'] = kwargs_bottom['h'] = -2*c2 - kwargs_bottom['j'] = -2*p1*sb2 - kwargs_top['j'] = -2*p2*st2 + sb2 = sb * sb + st2 = st * st + kwargs_bottom["k"] = c1 * c1 + c2 * c2 + p1 * p1 * sb2 - r * r + kwargs_top["k"] = c1 * c1 + c2 * c2 + p2 * p2 * st2 - r * r + + if axis == "x": + kwargs_bottom["a"] *= sb2 + kwargs_top["a"] *= st2 + kwargs_bottom["g"] = -2 * p1 * sb2 + kwargs_top["g"] = -2 * p2 * st2 + kwargs_top["h"] = kwargs_bottom["h"] = -2 * c1 + kwargs_top["j"] = kwargs_bottom["j"] = -2 * c2 + elif axis == "y": + kwargs_bottom["b"] *= sb2 + kwargs_top["b"] *= st2 + kwargs_top["g"] = kwargs_bottom["g"] = -2 * c1 + kwargs_bottom["h"] = -2 * p1 * sb2 + kwargs_top["h"] = -2 * p2 * st2 + kwargs_top["j"] = kwargs_bottom["j"] = -2 * c2 + elif axis == "z": + kwargs_bottom["c"] *= sb2 + kwargs_top["c"] *= st2 + kwargs_top["g"] = kwargs_bottom["g"] = -2 * c1 + kwargs_top["h"] = kwargs_bottom["h"] = -2 * c2 + kwargs_bottom["j"] = -2 * p1 * sb2 + kwargs_top["j"] = -2 * p2 * st2 self.bottom = openmc.Quadric(**kwargs_bottom) self.top = openmc.Quadric(**kwargs_top) def __neg__(self): - return ((-self.cyl & +self.plane_bottom & -self.plane_top) | - (-self.bottom & -self.plane_bottom) | - (-self.top & +self.plane_top)) + return ( + (-self.cyl & +self.plane_bottom & -self.plane_top) + | (-self.bottom & -self.plane_bottom) + | (-self.top & +self.plane_top) + ) diff --git a/openmc/model/triso.py b/openmc/model/triso.py index f344b8edd42..91c64824023 100644 --- a/openmc/model/triso.py +++ b/openmc/model/triso.py @@ -23,7 +23,7 @@ def _volume_sphere(r): """Return volume of a sphere of radius r""" - return 4/3 * pi * r**3 + return 4 / 3 * pi * r**3 class TRISO(openmc.Cell): @@ -53,7 +53,7 @@ class TRISO(openmc.Cell): """ - def __init__(self, outer_radius, fill, center=(0., 0., 0.)): + def __init__(self, outer_radius, fill, center=(0.0, 0.0, 0.0)): self._surface = openmc.Sphere(r=outer_radius) super().__init__(fill=fill, region=-self._surface) self.center = np.asarray(center) @@ -64,7 +64,7 @@ def center(self): @center.setter def center(self, center): - check_type('TRISO center', center, Iterable, Real) + check_type("TRISO center", center, Iterable, Real) self._surface.x0 = center[0] self._surface.y0 = center[1] self._surface.z0 = center[2] @@ -91,13 +91,15 @@ def classify(self, lattice): if lattice.ndim == 2: (i_min, j_min), p = lattice.find_element(ll) (i_max, j_max), p = lattice.find_element(ur) - return list(np.broadcast(*np.ogrid[ - j_min:j_max+1, i_min:i_max+1])) + return list(np.broadcast(*np.ogrid[j_min : j_max + 1, i_min : i_max + 1])) else: (i_min, j_min, k_min), p = lattice.find_element(ll) (i_max, j_max, k_max), p = lattice.find_element(ur) - return list(np.broadcast(*np.ogrid[ - k_min:k_max+1, j_min:j_max+1, i_min:i_max+1])) + return list( + np.broadcast( + *np.ogrid[k_min : k_max + 1, j_min : j_max + 1, i_min : i_max + 1] + ) + ) class _Container(ABC): @@ -127,7 +129,8 @@ class _Container(ABC): Volume of the container. """ - def __init__(self, sphere_radius, center=(0., 0., 0.)): + + def __init__(self, sphere_radius, center=(0.0, 0.0, 0.0)): self._cell_length = None self._limits = None @@ -164,7 +167,6 @@ def cell_length(self): def volume(self): pass - def mesh_cell(self, p): """Calculate the index of the cell in a mesh overlaid on the domain in which the given sphere center falls. @@ -180,7 +182,7 @@ def mesh_cell(self, p): Indices of mesh cell. """ - return tuple(int(p[i]/self.cell_length[i]) for i in range(3)) + return tuple(int(p[i] / self.cell_length[i]) for i in range(3)) def nearby_mesh_cells(self, p): """Calculates the indices of all cells in a mesh overlaid on the domain @@ -197,9 +199,11 @@ def nearby_mesh_cells(self, p): Indices of mesh cells. """ - d = 2*self.sphere_radius - r = [[a/self.cell_length[i] for a in [p[i]-d, p[i], p[i]+d]] - for i in range(3)] + d = 2 * self.sphere_radius + r = [ + [a / self.cell_length[i] for a in [p[i] - d, p[i], p[i] + d]] + for i in range(3) + ] return list(itertools.product(*({int(x) for x in y} for y in r))) @abstractmethod @@ -291,7 +295,7 @@ class _RectangularPrism(_Container): """ - def __init__(self, width, depth, height, sphere_radius, center=(0., 0., 0.)): + def __init__(self, width, depth, height, sphere_radius, center=(0.0, 0.0, 0.0)): super().__init__(sphere_radius, center) self.width = width self.depth = depth @@ -332,9 +336,11 @@ def limits(self): if self._limits is None: c = self.center r = self.sphere_radius - x, y, z = self.width/2, self.depth/2, self.height/2 - self._limits = [[c[0] - x + r, c[1] - y + r, c[2] - z + r], - [c[0] + x - r, c[1] + y - r, c[2] + z - r]] + x, y, z = self.width / 2, self.depth / 2, self.height / 2 + self._limits = [ + [c[0] - x + r, c[1] - y + r, c[2] - z + r], + [c[0] + x - r, c[1] + y - r, c[2] + z - r], + ] return self._limits @@ -346,17 +352,18 @@ def limits(self, limits): def cell_length(self): if self._cell_length is None: mesh_length = [self.width, self.depth, self.height] - self._cell_length = [x/int(x/(4*self.sphere_radius)) - for x in mesh_length] + self._cell_length = [ + x / int(x / (4 * self.sphere_radius)) for x in mesh_length + ] return self._cell_length @property def volume(self): - return self.width*self.depth*self.height + return self.width * self.depth * self.height @classmethod def from_region(self, region, sphere_radius): - check_type('region', region, openmc.Region) + check_type("region", region, openmc.Region) # Assume the simplest case where the prism volume is the intersection # of the half-spaces of six planes @@ -373,12 +380,14 @@ def from_region(self, region, sphere_radius): px1, px2, py1, py2, pz1, pz2 = sorted(region, key=lambda x: x.surface.type) # Make sure the region consists of the correct surfaces - if (not isinstance(px1.surface, openmc.XPlane) or - not isinstance(px2.surface, openmc.XPlane) or - not isinstance(py1.surface, openmc.YPlane) or - not isinstance(py2.surface, openmc.YPlane) or - not isinstance(pz1.surface, openmc.ZPlane) or - not isinstance(pz2.surface, openmc.ZPlane)): + if ( + not isinstance(px1.surface, openmc.XPlane) + or not isinstance(px2.surface, openmc.XPlane) + or not isinstance(py1.surface, openmc.YPlane) + or not isinstance(py2.surface, openmc.YPlane) + or not isinstance(pz1.surface, openmc.ZPlane) + or not isinstance(pz2.surface, openmc.ZPlane) + ): raise ValueError # Make sure the half-spaces are on the correct side of the surfaces @@ -388,26 +397,24 @@ def from_region(self, region, sphere_radius): # Calculate the parameters for the container width, depth, height = ur - ll - center = ll + [width/2, depth/2, height/2] + center = ll + [width / 2, depth / 2, height / 2] # The region is the volume of a rectangular prism, so create container return _RectangularPrism(width, depth, height, sphere_radius, center) def random_point(self): ll, ul = self.limits - return [uniform(ll[0], ul[0]), - uniform(ll[1], ul[1]), - uniform(ll[2], ul[2])] + return [uniform(ll[0], ul[0]), uniform(ll[1], ul[1]), uniform(ll[2], ul[2])] def repel_spheres(self, p, q, d, d_new): # Moving each sphere distance 's' away from the other along the line # joining the sphere centers will ensure their final distance is # equal to the outer diameter - s = (d_new - d)/2 + s = (d_new - d) / 2 - v = (p - q)/d - p += s*v - q -= s*v + v = (p - q) / d + p += s * v + q -= s * v # Enforce the rigid boundary by moving each sphere back along the # surface normal until it is completely within the container if it @@ -461,7 +468,7 @@ class _Cylinder(_Container): """ - def __init__(self, length, radius, axis, sphere_radius, center=(0., 0., 0.)): + def __init__(self, length, radius, axis, sphere_radius, center=(0.0, 0.0, 0.0)): super().__init__(sphere_radius, center) self._shift = None self.length = length @@ -500,9 +507,9 @@ def axis(self, axis): @property def shift(self): if self._shift is None: - if self.axis == 'x': + if self.axis == "x": self._shift = [1, 2, 0] - elif self.axis == 'y': + elif self.axis == "y": self._shift = [2, 0, 1] else: self._shift = [0, 1, 2] @@ -512,7 +519,7 @@ def shift(self): def limits(self): if self._limits is None: z0 = self.center[self.shift[2]] - z = self.length/2 + z = self.length / 2 r = self.sphere_radius self._limits = [[z0 - z + r], [z0 + z - r, self.radius - r]] return self._limits @@ -524,21 +531,21 @@ def limits(self, limits): @property def cell_length(self): if self._cell_length is None: - h = 4*self.sphere_radius + h = 4 * self.sphere_radius i, j, k = self.shift - self._cell_length = [None]*3 - self._cell_length[i] = 2*self.radius/int(2*self.radius/h) - self._cell_length[j] = 2*self.radius/int(2*self.radius/h) - self._cell_length[k] = self.length/int(self.length/h) + self._cell_length = [None] * 3 + self._cell_length[i] = 2 * self.radius / int(2 * self.radius / h) + self._cell_length[j] = 2 * self.radius / int(2 * self.radius / h) + self._cell_length[k] = self.length / int(self.length / h) return self._cell_length @property def volume(self): - return self.length*pi*self.radius**2 + return self.length * pi * self.radius**2 @classmethod def from_region(self, region, sphere_radius): - check_type('region', region, openmc.Region) + check_type("region", region, openmc.Region) # Assume the simplest case where the cylinder volume is the # intersection of the half-spaces of a cylinder and two planes @@ -557,35 +564,35 @@ def from_region(self, region, sphere_radius): # Make sure the region is composed of a cylinder and two planes on the # same axis count = Counter(node.surface.type for node in region) - if count[axis + '-cylinder'] != 1 or count[axis + '-plane'] != 2: + if count[axis + "-cylinder"] != 1 or count[axis + "-plane"] != 2: raise ValueError # Sort the half-spaces by surface type cyl, p1, p2 = sorted(region, key=lambda x: x.surface.type) # Calculate the parameters for a cylinder along the x-axis - if axis == 'x': + if axis == "x": if p1.surface.x0 > p2.surface.x0: p1, p2 = p2, p1 length = p2.surface.x0 - p1.surface.x0 - center = (p1.surface.x0 + length/2, cyl.surface.y0, cyl.surface.z0) + center = (p1.surface.x0 + length / 2, cyl.surface.y0, cyl.surface.z0) # Calculate the parameters for a cylinder along the y-axis - elif axis == 'y': + elif axis == "y": if p1.surface.y0 > p2.surface.y0: p1, p2 = p2, p1 length = p2.surface.y0 - p1.surface.y0 - center = (cyl.surface.x0, p1.surface.y0 + length/2, cyl.surface.z0) + center = (cyl.surface.x0, p1.surface.y0 + length / 2, cyl.surface.z0) # Calculate the parameters for a cylinder along the z-axis else: if p1.surface.z0 > p2.surface.z0: p1, p2 = p2, p1 length = p2.surface.z0 - p1.surface.z0 - center = (cyl.surface.x0, cyl.surface.y0, p1.surface.z0 + length/2) + center = (cyl.surface.x0, cyl.surface.y0, p1.surface.z0 + length / 2) # Make sure the half-spaces are on the correct side of the surfaces - if cyl.side != '-' or p1.side != '+' or p2.side != '-': + if cyl.side != "-" or p1.side != "+" or p2.side != "-": raise ValueError radius = cyl.surface.r @@ -595,12 +602,12 @@ def from_region(self, region, sphere_radius): def random_point(self): ll, ul = self.limits - r = sqrt(uniform(0, ul[1]**2)) - t = uniform(0, 2*pi) + r = sqrt(uniform(0, ul[1] ** 2)) + t = uniform(0, 2 * pi) i, j, k = self.shift - p = [None]*3 - p[i] = r*cos(t) + self.center[i] - p[j] = r*sin(t) + self.center[j] + p = [None] * 3 + p[i] = r * cos(t) + self.center[i] + p[j] = r * sin(t) + self.center[j] p[k] = uniform(ll[0], ul[0]) return p @@ -608,11 +615,11 @@ def repel_spheres(self, p, q, d, d_new): # Moving each sphere distance 's' away from the other along the line # joining the sphere centers will ensure their final distance is # equal to the outer diameter - s = (d_new - d)/2 + s = (d_new - d) / 2 - v = (p - q)/d - p += s*v - q -= s*v + v = (p - q) / d + p += s * v + q -= s * v # Enforce the rigid boundary by moving each sphere back along the # surface normal until it is completely within the container if it @@ -621,16 +628,16 @@ def repel_spheres(self, p, q, d, d_new): c = self.center i, j, k = self.shift - r = sqrt((p[i] - c[i])**2 + (p[j] - c[j])**2) + r = sqrt((p[i] - c[i]) ** 2 + (p[j] - c[j]) ** 2) if r > ul[1]: - p[i] = (p[i] - c[i])*ul[1]/r + c[i] - p[j] = (p[j] - c[j])*ul[1]/r + c[j] + p[i] = (p[i] - c[i]) * ul[1] / r + c[i] + p[j] = (p[j] - c[j]) * ul[1] / r + c[j] p[k] = np.clip(p[k], ll[0], ul[0]) - r = sqrt((q[i] - c[i])**2 + (q[j] - c[j])**2) + r = sqrt((q[i] - c[i]) ** 2 + (q[j] - c[j]) ** 2) if r > ul[1]: - q[i] = (q[i] - c[i])*ul[1]/r + c[i] - q[j] = (q[j] - c[j])*ul[1]/r + c[j] + q[i] = (q[i] - c[i]) * ul[1] / r + c[i] + q[j] = (q[j] - c[j]) * ul[1] / r + c[j] q[k] = np.clip(q[k], ll[0], ul[0]) @@ -668,8 +675,7 @@ class _SphericalShell(_Container): """ - def __init__(self, radius, inner_radius, sphere_radius, - center=(0., 0., 0.)): + def __init__(self, radius, inner_radius, sphere_radius, center=(0.0, 0.0, 0.0)): super().__init__(sphere_radius, center) self.radius = radius self.inner_radius = inner_radius @@ -711,9 +717,10 @@ def limits(self, limits): @property def cell_length(self): if self._cell_length is None: - mesh_length = 3*[2*self.radius] - self._cell_length = [x/int(x/(4*self.sphere_radius)) - for x in mesh_length] + mesh_length = 3 * [2 * self.radius] + self._cell_length = [ + x / int(x / (4 * self.sphere_radius)) for x in mesh_length + ] return self._cell_length @property @@ -722,20 +729,22 @@ def volume(self): @classmethod def from_region(self, region, sphere_radius): - check_type('region', region, openmc.Region) + check_type("region", region, openmc.Region) # First check if the region is the volume inside a sphere. Assume the # simplest case where the sphere volume is the negative half-space of a # sphere. - if (isinstance(region, openmc.Halfspace) + if ( + isinstance(region, openmc.Halfspace) and isinstance(region.surface, openmc.Sphere) - and region.side == '-'): + and region.side == "-" + ): # The region is the volume of a sphere, so create container radius = region.surface.r center = (region.surface.x0, region.surface.y0, region.surface.z0) - return _SphericalShell(radius, 0., sphere_radius, center) + return _SphericalShell(radius, 0.0, sphere_radius, center) # Next check for a spherical shell volume. Assume the simplest case # where the spherical shell volume is the intersection of the @@ -760,7 +769,7 @@ def from_region(self, region, sphere_radius): if center != (s2.surface.x0, s2.surface.y0, s2.surface.z0): raise ValueError - if s1.side != '+' or s2.side != '-': + if s1.side != "+" or s2.side != "-": raise ValueError # The region is the volume of a spherical shell, so create container @@ -770,18 +779,18 @@ def random_point(self): c = self.center ll, ul = self.limits x, y, z = (gauss(0, 1), gauss(0, 1), gauss(0, 1)) - r = (uniform(ll[0]**3, ul[0]**3)**(1/3)/sqrt(x**2 + y**2 + z**2)) - return [r*x + c[0], r*y + c[1], r*z + c[2]] + r = uniform(ll[0] ** 3, ul[0] ** 3) ** (1 / 3) / sqrt(x**2 + y**2 + z**2) + return [r * x + c[0], r * y + c[1], r * z + c[2]] def repel_spheres(self, p, q, d, d_new): # Moving each sphere distance 's' away from the other along the line # joining the sphere centers will ensure their final distance is # equal to the outer diameter - s = (d_new - d)/2 + s = (d_new - d) / 2 - v = (p - q)/d - p += s*v - q -= s*v + v = (p - q) / d + p += s * v + q -= s * v # Enforce the rigid boundary by moving each sphere back along the # surface normal until it is completely within the container if it @@ -789,17 +798,17 @@ def repel_spheres(self, p, q, d, d_new): c = self.center ll, ul = self.limits - r = sqrt((p[0] - c[0])**2 + (p[1] - c[1])**2 + (p[2] - c[2])**2) + r = sqrt((p[0] - c[0]) ** 2 + (p[1] - c[1]) ** 2 + (p[2] - c[2]) ** 2) if r > ul[0]: - p[:] = (p - c)*ul[0]/r + c + p[:] = (p - c) * ul[0] / r + c elif r < ll[0]: - p[:] = (p - c)*ll[0]/r + c + p[:] = (p - c) * ll[0] / r + c - r = sqrt((q[0] - c[0])**2 + (q[1] - c[1])**2 + (q[2] - c[2])**2) + r = sqrt((q[0] - c[0]) ** 2 + (q[1] - c[1]) ** 2 + (q[2] - c[2]) ** 2) if r > ul[0]: - q[:] = (q - c)*ul[0]/r + c + q[:] = (q - c) * ul[0] / r + c elif r < ll[0]: - q[:] = (q - c)*ll[0]/r + c + q[:] = (q - c) * ll[0] / r + c def create_triso_lattice(trisos, lower_left, pitch, shape, background): @@ -830,7 +839,7 @@ def create_triso_lattice(trisos, lower_left, pitch, shape, background): lattice.lower_left = lower_left lattice.pitch = pitch - indices = list(np.broadcast(*np.ogrid[:shape[2], :shape[1], :shape[0]])) + indices = list(np.broadcast(*np.ogrid[: shape[2], : shape[1], : shape[0]])) triso_locations = {idx: [] for idx in indices} for t in trisos: for idx in t.classify(lattice): @@ -840,15 +849,16 @@ def create_triso_lattice(trisos, lower_left, pitch, shape, background): t_copy = copy.copy(t) t_copy.id = None t_copy.fill = t.fill - t_copy._surface = openmc.Sphere(r=t._surface.r, - x0=t._surface.x0, - y0=t._surface.y0, - z0=t._surface.z0) + t_copy._surface = openmc.Sphere( + r=t._surface.r, x0=t._surface.x0, y0=t._surface.y0, z0=t._surface.z0 + ) t_copy.region = -t_copy._surface triso_locations[idx].append(t_copy) else: - warnings.warn('TRISO particle is partially or completely ' - 'outside of the lattice.') + warnings.warn( + "TRISO particle is partially or completely " + "outside of the lattice." + ) # Create universes universes = np.empty(shape[::-1], dtype=openmc.Universe) @@ -896,7 +906,7 @@ def _random_sequential_pack(domain, num_spheres): """ - sqd = (2*domain.sphere_radius)**2 + sqd = (2 * domain.sphere_radius) ** 2 spheres = [] mesh = defaultdict(list) @@ -905,8 +915,10 @@ def _random_sequential_pack(domain, num_spheres): while True: p = domain.random_point() idx = domain.mesh_cell(p) - if any((p[0]-q[0])**2 + (p[1]-q[1])**2 + (p[2]-q[2])**2 < sqd - for q in mesh[idx]): + if any( + (p[0] - q[0]) ** 2 + (p[1] - q[1]) ** 2 + (p[2] - q[2]) ** 2 < sqd + for q in mesh[idx] + ): continue else: break @@ -1026,8 +1038,7 @@ def create_rod_list(): r = list({tuple(x) for x in a} & {tuple(x) for x in b}) # Remove duplicate rods and sort by distance - r = map(list, set([(x[2], int(min(x[0:2])), int(max(x[0:2]))) - for x in r])) + r = map(list, set([(x[2], int(min(x[0:2])), int(max(x[0:2]))) for x in r])) # Clear priority queue and add rods del rods[:] @@ -1080,12 +1091,14 @@ def reduce_outer_diameter(): """ - inner_pf = _volume_sphere(inner_diameter/2)*num_spheres / domain.volume - outer_pf = _volume_sphere(outer_diameter/2)*num_spheres / domain.volume + inner_pf = _volume_sphere(inner_diameter / 2) * num_spheres / domain.volume + outer_pf = _volume_sphere(outer_diameter / 2) * num_spheres / domain.volume j = floor(-log10(outer_pf - inner_pf)) - return (outer_diameter - 0.5**j * contraction_rate * - initial_outer_diameter / num_spheres) + return ( + outer_diameter + - 0.5**j * contraction_rate * initial_outer_diameter / num_spheres + ) def nearest(i): """Find index of nearest neighbor of sphere i. @@ -1131,23 +1144,27 @@ def update_rod_list(i): # remove the rod currently containing k from the rod list and add rod # k-i, keeping the rod list sorted k, d_ik = nearest(i) - if (k and nearest(k)[0] == i and d_ik < outer_diameter - and not np.isclose(d, outer_diameter, atol=1.0e-14)): + if ( + k + and nearest(k)[0] == i + and d_ik < outer_diameter + and not np.isclose(d, outer_diameter, atol=1.0e-14) + ): remove_rod(k) add_rod(d_ik, i, k) num_spheres = len(spheres) - diameter = 2*domain.sphere_radius + diameter = 2 * domain.sphere_radius # Flag for marking rods that have been removed from priority queue removed = -1 # Outer diameter initially set to arbitrary value that yields pf of 1 - initial_outer_diameter = 2*(domain.volume/(num_spheres*4/3*pi))**(1/3) + initial_outer_diameter = 2 * (domain.volume / (num_spheres * 4 / 3 * pi)) ** (1 / 3) # Inner and outer diameter of spheres will change during packing outer_diameter = initial_outer_diameter - inner_diameter = 0. + inner_diameter = 0.0 # List of rods arranged in a heap and mapping of sphere ids to rods rods = [] @@ -1185,9 +1202,11 @@ def update_rod_list(i): # the number of iterations needed to remove all overlaps is inversely # proportional to the contraction rate. if inner_diameter >= outer_diameter or not rods: - warnings.warn('Close random pack converged before reaching true ' - 'sphere radius; some spheres may overlap. Try ' - 'reducing contraction rate or packing fraction.') + warnings.warn( + "Close random pack converged before reaching true " + "sphere radius; some spheres may overlap. Try " + "reducing contraction rate or packing fraction." + ) break while True: @@ -1207,8 +1226,15 @@ def update_rod_list(i): break -def pack_spheres(radius, region, pf=None, num_spheres=None, initial_pf=0.3, - contraction_rate=1.e-3, seed=None): +def pack_spheres( + radius, + region, + pf=None, + num_spheres=None, + initial_pf=0.3, + contraction_rate=1.0e-3, + seed=None, +): """Generate a random, non-overlapping configuration of spheres within a container. @@ -1292,20 +1318,22 @@ def pack_spheres(radius, region, pf=None, num_spheres=None, initial_pf=0.3, pass if not domain: - raise ValueError('Could not map region {} to a container: supported ' - 'container shapes are rectangular prism, cylinder, ' - 'sphere, and spherical shell.'.format(region)) + raise ValueError( + "Could not map region {} to a container: supported " + "container shapes are rectangular prism, cylinder, " + "sphere, and spherical shell.".format(region) + ) # Determine the packing fraction/number of spheres volume = _volume_sphere(radius) if pf is None and num_spheres is None: - raise ValueError('`pf` or `num_spheres` must be specified.') + raise ValueError("`pf` or `num_spheres` must be specified.") elif pf is None: num_spheres = int(num_spheres) - pf = volume*num_spheres/domain.volume + pf = volume * num_spheres / domain.volume else: pf = float(pf) - num_spheres = int(pf*domain.volume//volume) + num_spheres = int(pf * domain.volume // volume) # Make sure initial packing fraction is less than packing fraction if initial_pf > pf: @@ -1313,24 +1341,32 @@ def pack_spheres(radius, region, pf=None, num_spheres=None, initial_pf=0.3, # Check packing fraction for close random packing if pf > MAX_PF_CRP: - raise ValueError(f'Packing fraction {pf} is greater than the limit for ' - f'close random packing, {MAX_PF_CRP}') + raise ValueError( + f"Packing fraction {pf} is greater than the limit for " + f"close random packing, {MAX_PF_CRP}" + ) # Check packing fraction for random sequential packing if initial_pf > MAX_PF_RSP: - raise ValueError(f'Initial packing fraction {initial_pf} is greater than' - f'the limit for random sequential packing, {MAX_PF_RSP}') + raise ValueError( + f"Initial packing fraction {initial_pf} is greater than" + f"the limit for random sequential packing, {MAX_PF_RSP}" + ) # Calculate the sphere radius used in the initial random sequential # packing from the initial packing fraction - initial_radius = (3/4*initial_pf*domain.volume/(pi*num_spheres))**(1/3) + initial_radius = (3 / 4 * initial_pf * domain.volume / (pi * num_spheres)) ** ( + 1 / 3 + ) domain.sphere_radius = initial_radius # Recalculate the limits for the initial random sequential packing using # the desired final sphere radius to ensure spheres are fully contained # within the domain during the close random pack - domain.limits = [[x - initial_radius + radius for x in domain.limits[0]], - [x + initial_radius - radius for x in domain.limits[1]]] + domain.limits = [ + [x - initial_radius + radius for x in domain.limits[0]], + [x + initial_radius - radius for x in domain.limits[1]], + ] # Generate non-overlapping spheres for an initial inner radius using # random sequential packing algorithm diff --git a/openmc/mpi.py b/openmc/mpi.py index cfc10c0d189..a997478ecc8 100644 --- a/openmc/mpi.py +++ b/openmc/mpi.py @@ -1,8 +1,11 @@ try: from mpi4py import MPI + comm = MPI.COMM_WORLD except ImportError: from unittest.mock import Mock + MPI = Mock() from openmc.dummy_comm import DummyCommunicator + comm = DummyCommunicator() diff --git a/openmc/nuclide.py b/openmc/nuclide.py index d5ae4bddbb4..05644fb236a 100644 --- a/openmc/nuclide.py +++ b/openmc/nuclide.py @@ -20,14 +20,16 @@ def __new__(cls, name): # Initialize class attributes orig_name = name - if '-' in name: - name = name.replace('-', '') - name = name.replace('Nat', '0') - if name.endswith('m'): - name = name[:-1] + '_m1' - - msg = ('OpenMC nuclides follow the GNDS naming convention. ' - f'Nuclide "{orig_name}" is being renamed as "{name}".') + if "-" in name: + name = name.replace("-", "") + name = name.replace("Nat", "0") + if name.endswith("m"): + name = name[:-1] + "_m1" + + msg = ( + "OpenMC nuclides follow the GNDS naming convention. " + f'Nuclide "{orig_name}" is being renamed as "{name}".' + ) warnings.warn(msg) return super().__new__(cls, name) diff --git a/openmc/openmoc_compatible.py b/openmc/openmoc_compatible.py index 3fd54520ba5..4ead1189990 100644 --- a/openmc/openmoc_compatible.py +++ b/openmc/openmoc_compatible.py @@ -72,7 +72,7 @@ def get_openmoc_material(openmc_material): """ - cv.check_type('openmc_material', openmc_material, openmc.Material) + cv.check_type("openmc_material", openmc_material, openmc.Material) material_id = openmc_material.id @@ -108,7 +108,7 @@ def get_openmc_material(openmoc_material): """ - cv.check_type('openmoc_material', openmoc_material, openmoc.Material) + cv.check_type("openmoc_material", openmoc_material, openmoc.Material) material_id = openmoc_material.getId() @@ -144,7 +144,7 @@ def get_openmoc_surface(openmc_surface): """ - cv.check_type('openmc_surface', openmc_surface, openmc.Surface) + cv.check_type("openmc_surface", openmc_surface, openmc.Surface) surface_id = openmc_surface.id @@ -156,16 +156,16 @@ def get_openmoc_surface(openmc_surface): name = openmc_surface.name # Determine the type of boundary conditions applied to the Surface - if openmc_surface.boundary_type == 'vacuum': + if openmc_surface.boundary_type == "vacuum": boundary = openmoc.VACUUM - elif openmc_surface.boundary_type == 'reflective': + elif openmc_surface.boundary_type == "reflective": boundary = openmoc.REFLECTIVE - elif openmc_surface.boundary_type == 'periodic': + elif openmc_surface.boundary_type == "periodic": boundary = openmoc.PERIODIC else: boundary = openmoc.BOUNDARY_NONE - if openmc_surface.type == 'plane': + if openmc_surface.type == "plane": A = openmc_surface.a B = openmc_surface.b C = openmc_surface.c @@ -174,28 +174,30 @@ def get_openmoc_surface(openmc_surface): # OpenMOC uses the opposite sign on D openmoc_surface = openmoc.Plane(A, B, C, -D, surface_id, name) - elif openmc_surface.type == 'x-plane': + elif openmc_surface.type == "x-plane": x0 = openmc_surface.x0 openmoc_surface = openmoc.XPlane(x0, surface_id, name) - elif openmc_surface.type == 'y-plane': + elif openmc_surface.type == "y-plane": y0 = openmc_surface.y0 openmoc_surface = openmoc.YPlane(y0, surface_id, name) - elif openmc_surface.type == 'z-plane': + elif openmc_surface.type == "z-plane": z0 = openmc_surface.z0 openmoc_surface = openmoc.ZPlane(z0, surface_id, name) - elif openmc_surface.type == 'z-cylinder': + elif openmc_surface.type == "z-cylinder": x0 = openmc_surface.x0 y0 = openmc_surface.y0 R = openmc_surface.r openmoc_surface = openmoc.ZCylinder(x0, y0, R, surface_id, name) else: - msg = ('Unable to create an OpenMOC Surface from an OpenMC Surface of ' - f'type "{type(openmc_surface)}" since it is not a compatible ' - 'Surface type in OpenMOC') + msg = ( + "Unable to create an OpenMOC Surface from an OpenMC Surface of " + f'type "{type(openmc_surface)}" since it is not a compatible ' + "Surface type in OpenMOC" + ) raise ValueError(msg) # Set the boundary condition for this Surface @@ -225,7 +227,7 @@ def get_openmc_surface(openmoc_surface): """ - cv.check_type('openmoc_surface', openmoc_surface, openmoc.Surface) + cv.check_type("openmoc_surface", openmoc_surface, openmoc.Surface) surface_id = openmoc_surface.getId() @@ -239,13 +241,13 @@ def get_openmc_surface(openmoc_surface): # Correct for OpenMC's syntax for Surfaces dividing Cells boundary = openmoc_surface.getBoundaryType() if boundary == openmoc.VACUUM: - boundary = 'vacuum' + boundary = "vacuum" elif boundary == openmoc.REFLECTIVE: - boundary = 'reflective' + boundary = "reflective" elif boundary == openmoc.PERIODIC: - boundary = 'periodic' + boundary = "periodic" else: - boundary = 'transmission' + boundary = "transmission" if openmoc_surface.getSurfaceType() == openmoc.PLANE: openmoc_surface = openmoc.castSurfaceToPlane(openmoc_surface) @@ -303,7 +305,7 @@ def get_openmoc_cell(openmc_cell): """ - cv.check_type('openmc_cell', openmc_cell, openmc.Cell) + cv.check_type("openmc_cell", openmc_cell, openmc.Cell) cell_id = openmc_cell.id @@ -317,9 +319,9 @@ def get_openmoc_cell(openmc_cell): fill = openmc_cell.fill - if openmc_cell.fill_type == 'material': + if openmc_cell.fill_type == "material": openmoc_cell.setFill(get_openmoc_material(fill)) - elif openmc_cell.fill_type == 'universe': + elif openmc_cell.fill_type == "universe": openmoc_cell.setFill(get_openmoc_universe(fill)) else: openmoc_cell.setFill(get_openmoc_lattice(fill)) @@ -359,14 +361,13 @@ def get_openmoc_region(openmc_region): """ - cv.check_type('openmc_region', openmc_region, openmc.Region) + cv.check_type("openmc_region", openmc_region, openmc.Region) # Recursively instantiate a region of the appropriate type if isinstance(openmc_region, openmc.Halfspace): surface = openmc_region.surface - halfspace = -1 if openmc_region.side == '-' else 1 - openmoc_region = \ - openmoc.Halfspace(halfspace, get_openmoc_surface(surface)) + halfspace = -1 if openmc_region.side == "-" else 1 + openmoc_region = openmoc.Halfspace(halfspace, get_openmoc_surface(surface)) elif isinstance(openmc_region, openmc.Intersection): openmoc_region = openmoc.Intersection() for openmc_node in openmc_region: @@ -397,13 +398,13 @@ def get_openmc_region(openmoc_region): """ - cv.check_type('openmoc_region', openmoc_region, openmoc.Region) + cv.check_type("openmoc_region", openmoc_region, openmoc.Region) # Recursively instantiate a region of the appropriate type if openmoc_region.getRegionType() == openmoc.HALFSPACE: openmoc_region = openmoc.castRegionToHalfspace(openmoc_region) surface = get_openmc_surface(openmoc_region.getSurface()) - side = '-' if openmoc_region.getHalfspace() == -1 else '+' + side = "-" if openmoc_region.getHalfspace() == -1 else "+" openmc_region = openmc.Halfspace(surface, side) elif openmoc_region.getRegionType() == openmoc.INTERSECTION: openmc_region = openmc.Intersection([]) @@ -438,7 +439,7 @@ def get_openmc_cell(openmoc_cell): """ - cv.check_type('openmoc_cell', openmoc_cell, openmoc.Cell) + cv.check_type("openmoc_cell", openmoc_cell, openmoc.Cell) cell_id = openmoc_cell.getId() @@ -499,7 +500,7 @@ def get_openmoc_universe(openmc_universe): """ - cv.check_type('openmc_universe', openmc_universe, openmc.Universe) + cv.check_type("openmc_universe", openmc_universe, openmc.Universe) universe_id = openmc_universe.id @@ -542,7 +543,7 @@ def get_openmc_universe(openmoc_universe): """ - cv.check_type('openmoc_universe', openmoc_universe, openmoc.Universe) + cv.check_type("openmoc_universe", openmoc_universe, openmoc.Universe) universe_id = openmoc_universe.getId() @@ -583,7 +584,7 @@ def get_openmoc_lattice(openmc_lattice): """ - cv.check_type('openmc_lattice', openmc_lattice, openmc.RectLattice) + cv.check_type("openmc_lattice", openmc_lattice, openmc.RectLattice) lattice_id = openmc_lattice.id @@ -613,7 +614,7 @@ def get_openmoc_lattice(openmc_lattice): # Convert 2D lower left to 3D for OpenMOC if len(lower_left) == 2: new_lower_left = np.ones(3, dtype=np.float64) - new_lower_left *= np.finfo(np.float64).min / 2. + new_lower_left *= np.finfo(np.float64).min / 2.0 new_lower_left[:2] = lower_left lower_left = new_lower_left @@ -643,9 +644,11 @@ def get_openmoc_lattice(openmc_lattice): openmoc_lattice.setWidth(pitch[0], pitch[1], pitch[2]) openmoc_lattice.setUniverses(universe_array.tolist()) - offset = np.array(lower_left, dtype=np.float64) - \ - ((np.array(pitch, dtype=np.float64) * - np.array(dimension, dtype=np.float64))) / -2.0 + offset = ( + np.array(lower_left, dtype=np.float64) + - ((np.array(pitch, dtype=np.float64) * np.array(dimension, dtype=np.float64))) + / -2.0 + ) openmoc_lattice.setOffset(offset[0], offset[1], offset[2]) # Add the OpenMC Lattice to the global collection of all OpenMC Lattices @@ -672,7 +675,7 @@ def get_openmc_lattice(openmoc_lattice): """ - cv.check_type('openmoc_lattice', openmoc_lattice, openmoc.Lattice) + cv.check_type("openmoc_lattice", openmoc_lattice, openmoc.Lattice) lattice_id = openmoc_lattice.getId() @@ -681,21 +684,26 @@ def get_openmc_lattice(openmoc_lattice): return OPENMC_LATTICES[lattice_id] name = openmoc_lattice.getName() - dimension = [openmoc_lattice.getNumX(), - openmoc_lattice.getNumY(), - openmoc_lattice.getNumZ()] - width = [openmoc_lattice.getWidthX(), - openmoc_lattice.getWidthY(), - openmoc_lattice.getWidthZ()] + dimension = [ + openmoc_lattice.getNumX(), + openmoc_lattice.getNumY(), + openmoc_lattice.getNumZ(), + ] + width = [ + openmoc_lattice.getWidthX(), + openmoc_lattice.getWidthY(), + openmoc_lattice.getWidthZ(), + ] offset = openmoc_lattice.getOffset() offset = [offset.getX(), offset.getY(), offset.getZ()] - lower_left = np.array(offset, dtype=np.float64) + \ - ((np.array(width, dtype=np.float64) * - np.array(dimension, dtype=np.float64))) / -2.0 + lower_left = ( + np.array(offset, dtype=np.float64) + + ((np.array(width, dtype=np.float64) * np.array(dimension, dtype=np.float64))) + / -2.0 + ) # Initialize an empty array for the OpenMOC nested Universes in this Lattice - universe_array = np.ndarray(tuple(np.array(dimension)), - dtype=openmoc.Universe) + universe_array = np.ndarray(tuple(np.array(dimension)), dtype=openmoc.Universe) # Create OpenMOC Universes for each unique nested Universe in this Lattice unique_universes = openmoc_lattice.getUniqueUniverses() @@ -709,8 +717,7 @@ def get_openmc_lattice(openmoc_lattice): for z in range(dimension[2]): universe = openmoc_lattice.getUniverse(x, y, z) universe_id = universe.getId() - universe_array[x][y][z] = \ - unique_universes[universe_id] + universe_array[x][y][z] = unique_universes[universe_id] universe_array = np.swapaxes(universe_array, 0, 2) @@ -751,7 +758,7 @@ def get_openmoc_geometry(openmc_geometry): """ - cv.check_type('openmc_geometry', openmc_geometry, openmc.Geometry) + cv.check_type("openmc_geometry", openmc_geometry, openmc.Geometry) # Clear dictionaries and auto-generated IDs OPENMC_SURFACES.clear() @@ -781,10 +788,10 @@ def get_openmoc_geometry(openmc_geometry): max_cell_id = max(all_cells.keys()) max_universe_id = max(all_universes.keys()) - openmoc.maximize_material_id(max_material_id+1) - openmoc.maximize_surface_id(max_surface_id+1) - openmoc.maximize_cell_id(max_cell_id+1) - openmoc.maximize_universe_id(max_universe_id+1) + openmoc.maximize_material_id(max_material_id + 1) + openmoc.maximize_surface_id(max_surface_id + 1) + openmoc.maximize_cell_id(max_cell_id + 1) + openmoc.maximize_universe_id(max_universe_id + 1) return openmoc_geometry @@ -804,7 +811,7 @@ def get_openmc_geometry(openmoc_geometry): """ - cv.check_type('openmoc_geometry', openmoc_geometry, openmoc.Geometry) + cv.check_type("openmoc_geometry", openmoc_geometry, openmoc.Geometry) # Clear dictionaries and auto-generated ID OPENMC_SURFACES.clear() diff --git a/openmc/particle_restart.py b/openmc/particle_restart.py index 317d8761b61..9f19773183d 100644 --- a/openmc/particle_restart.py +++ b/openmc/particle_restart.py @@ -42,19 +42,19 @@ class Particle: """ def __init__(self, filename): - with h5py.File(filename, 'r') as f: + with h5py.File(filename, "r") as f: # Ensure filetype and version are correct - cv.check_filetype_version(f, 'particle restart', _VERSION_PARTICLE_RESTART) - - self.current_batch = f['current_batch'][()] - self.current_generation = f['current_generation'][()] - self.energy = f['energy'][()] - self.generations_per_batch = f['generations_per_batch'][()] - self.id = f['id'][()] - self.type = f['type'][()] - self.n_particles = f['n_particles'][()] - self.run_mode = f['run_mode'][()].decode() - self.uvw = f['uvw'][()] - self.weight = f['weight'][()] - self.xyz = f['xyz'][()] + cv.check_filetype_version(f, "particle restart", _VERSION_PARTICLE_RESTART) + + self.current_batch = f["current_batch"][()] + self.current_generation = f["current_generation"][()] + self.energy = f["energy"][()] + self.generations_per_batch = f["generations_per_batch"][()] + self.id = f["id"][()] + self.type = f["type"][()] + self.n_particles = f["n_particles"][()] + self.run_mode = f["run_mode"][()].decode() + self.uvw = f["uvw"][()] + self.weight = f["weight"][()] + self.xyz = f["xyz"][()] diff --git a/openmc/plots.py b/openmc/plots.py index 7532d9d5cb1..5a9016f3660 100644 --- a/openmc/plots.py +++ b/openmc/plots.py @@ -13,156 +13,156 @@ from ._xml import clean_indentation, get_elem_tuple, reorder_attributes, get_text from .mixin import IDManagerMixin -_BASES = {'xy', 'xz', 'yz'} +_BASES = {"xy", "xz", "yz"} _SVG_COLORS = { - 'aliceblue': (240, 248, 255), - 'antiquewhite': (250, 235, 215), - 'aqua': (0, 255, 255), - 'aquamarine': (127, 255, 212), - 'azure': (240, 255, 255), - 'beige': (245, 245, 220), - 'bisque': (255, 228, 196), - 'black': (0, 0, 0), - 'blanchedalmond': (255, 235, 205), - 'blue': (0, 0, 255), - 'blueviolet': (138, 43, 226), - 'brown': (165, 42, 42), - 'burlywood': (222, 184, 135), - 'cadetblue': (95, 158, 160), - 'chartreuse': (127, 255, 0), - 'chocolate': (210, 105, 30), - 'coral': (255, 127, 80), - 'cornflowerblue': (100, 149, 237), - 'cornsilk': (255, 248, 220), - 'crimson': (220, 20, 60), - 'cyan': (0, 255, 255), - 'darkblue': (0, 0, 139), - 'darkcyan': (0, 139, 139), - 'darkgoldenrod': (184, 134, 11), - 'darkgray': (169, 169, 169), - 'darkgreen': (0, 100, 0), - 'darkgrey': (169, 169, 169), - 'darkkhaki': (189, 183, 107), - 'darkmagenta': (139, 0, 139), - 'darkolivegreen': (85, 107, 47), - 'darkorange': (255, 140, 0), - 'darkorchid': (153, 50, 204), - 'darkred': (139, 0, 0), - 'darksalmon': (233, 150, 122), - 'darkseagreen': (143, 188, 143), - 'darkslateblue': (72, 61, 139), - 'darkslategray': (47, 79, 79), - 'darkslategrey': (47, 79, 79), - 'darkturquoise': (0, 206, 209), - 'darkviolet': (148, 0, 211), - 'deeppink': (255, 20, 147), - 'deepskyblue': (0, 191, 255), - 'dimgray': (105, 105, 105), - 'dimgrey': (105, 105, 105), - 'dodgerblue': (30, 144, 255), - 'firebrick': (178, 34, 34), - 'floralwhite': (255, 250, 240), - 'forestgreen': (34, 139, 34), - 'fuchsia': (255, 0, 255), - 'gainsboro': (220, 220, 220), - 'ghostwhite': (248, 248, 255), - 'gold': (255, 215, 0), - 'goldenrod': (218, 165, 32), - 'gray': (128, 128, 128), - 'green': (0, 128, 0), - 'greenyellow': (173, 255, 47), - 'grey': (128, 128, 128), - 'honeydew': (240, 255, 240), - 'hotpink': (255, 105, 180), - 'indianred': (205, 92, 92), - 'indigo': (75, 0, 130), - 'ivory': (255, 255, 240), - 'khaki': (240, 230, 140), - 'lavender': (230, 230, 250), - 'lavenderblush': (255, 240, 245), - 'lawngreen': (124, 252, 0), - 'lemonchiffon': (255, 250, 205), - 'lightblue': (173, 216, 230), - 'lightcoral': (240, 128, 128), - 'lightcyan': (224, 255, 255), - 'lightgoldenrodyellow': (250, 250, 210), - 'lightgray': (211, 211, 211), - 'lightgreen': (144, 238, 144), - 'lightgrey': (211, 211, 211), - 'lightpink': (255, 182, 193), - 'lightsalmon': (255, 160, 122), - 'lightseagreen': (32, 178, 170), - 'lightskyblue': (135, 206, 250), - 'lightslategray': (119, 136, 153), - 'lightslategrey': (119, 136, 153), - 'lightsteelblue': (176, 196, 222), - 'lightyellow': (255, 255, 224), - 'lime': (0, 255, 0), - 'limegreen': (50, 205, 50), - 'linen': (250, 240, 230), - 'magenta': (255, 0, 255), - 'maroon': (128, 0, 0), - 'mediumaquamarine': (102, 205, 170), - 'mediumblue': (0, 0, 205), - 'mediumorchid': (186, 85, 211), - 'mediumpurple': (147, 112, 219), - 'mediumseagreen': (60, 179, 113), - 'mediumslateblue': (123, 104, 238), - 'mediumspringgreen': (0, 250, 154), - 'mediumturquoise': (72, 209, 204), - 'mediumvioletred': (199, 21, 133), - 'midnightblue': (25, 25, 112), - 'mintcream': (245, 255, 250), - 'mistyrose': (255, 228, 225), - 'moccasin': (255, 228, 181), - 'navajowhite': (255, 222, 173), - 'navy': (0, 0, 128), - 'oldlace': (253, 245, 230), - 'olive': (128, 128, 0), - 'olivedrab': (107, 142, 35), - 'orange': (255, 165, 0), - 'orangered': (255, 69, 0), - 'orchid': (218, 112, 214), - 'palegoldenrod': (238, 232, 170), - 'palegreen': (152, 251, 152), - 'paleturquoise': (175, 238, 238), - 'palevioletred': (219, 112, 147), - 'papayawhip': (255, 239, 213), - 'peachpuff': (255, 218, 185), - 'peru': (205, 133, 63), - 'pink': (255, 192, 203), - 'plum': (221, 160, 221), - 'powderblue': (176, 224, 230), - 'purple': (128, 0, 128), - 'red': (255, 0, 0), - 'rosybrown': (188, 143, 143), - 'royalblue': (65, 105, 225), - 'saddlebrown': (139, 69, 19), - 'salmon': (250, 128, 114), - 'sandybrown': (244, 164, 96), - 'seagreen': (46, 139, 87), - 'seashell': (255, 245, 238), - 'sienna': (160, 82, 45), - 'silver': (192, 192, 192), - 'skyblue': (135, 206, 235), - 'slateblue': (106, 90, 205), - 'slategray': (112, 128, 144), - 'slategrey': (112, 128, 144), - 'snow': (255, 250, 250), - 'springgreen': (0, 255, 127), - 'steelblue': (70, 130, 180), - 'tan': (210, 180, 140), - 'teal': (0, 128, 128), - 'thistle': (216, 191, 216), - 'tomato': (255, 99, 71), - 'turquoise': (64, 224, 208), - 'violet': (238, 130, 238), - 'wheat': (245, 222, 179), - 'white': (255, 255, 255), - 'whitesmoke': (245, 245, 245), - 'yellow': (255, 255, 0), - 'yellowgreen': (154, 205, 50) + "aliceblue": (240, 248, 255), + "antiquewhite": (250, 235, 215), + "aqua": (0, 255, 255), + "aquamarine": (127, 255, 212), + "azure": (240, 255, 255), + "beige": (245, 245, 220), + "bisque": (255, 228, 196), + "black": (0, 0, 0), + "blanchedalmond": (255, 235, 205), + "blue": (0, 0, 255), + "blueviolet": (138, 43, 226), + "brown": (165, 42, 42), + "burlywood": (222, 184, 135), + "cadetblue": (95, 158, 160), + "chartreuse": (127, 255, 0), + "chocolate": (210, 105, 30), + "coral": (255, 127, 80), + "cornflowerblue": (100, 149, 237), + "cornsilk": (255, 248, 220), + "crimson": (220, 20, 60), + "cyan": (0, 255, 255), + "darkblue": (0, 0, 139), + "darkcyan": (0, 139, 139), + "darkgoldenrod": (184, 134, 11), + "darkgray": (169, 169, 169), + "darkgreen": (0, 100, 0), + "darkgrey": (169, 169, 169), + "darkkhaki": (189, 183, 107), + "darkmagenta": (139, 0, 139), + "darkolivegreen": (85, 107, 47), + "darkorange": (255, 140, 0), + "darkorchid": (153, 50, 204), + "darkred": (139, 0, 0), + "darksalmon": (233, 150, 122), + "darkseagreen": (143, 188, 143), + "darkslateblue": (72, 61, 139), + "darkslategray": (47, 79, 79), + "darkslategrey": (47, 79, 79), + "darkturquoise": (0, 206, 209), + "darkviolet": (148, 0, 211), + "deeppink": (255, 20, 147), + "deepskyblue": (0, 191, 255), + "dimgray": (105, 105, 105), + "dimgrey": (105, 105, 105), + "dodgerblue": (30, 144, 255), + "firebrick": (178, 34, 34), + "floralwhite": (255, 250, 240), + "forestgreen": (34, 139, 34), + "fuchsia": (255, 0, 255), + "gainsboro": (220, 220, 220), + "ghostwhite": (248, 248, 255), + "gold": (255, 215, 0), + "goldenrod": (218, 165, 32), + "gray": (128, 128, 128), + "green": (0, 128, 0), + "greenyellow": (173, 255, 47), + "grey": (128, 128, 128), + "honeydew": (240, 255, 240), + "hotpink": (255, 105, 180), + "indianred": (205, 92, 92), + "indigo": (75, 0, 130), + "ivory": (255, 255, 240), + "khaki": (240, 230, 140), + "lavender": (230, 230, 250), + "lavenderblush": (255, 240, 245), + "lawngreen": (124, 252, 0), + "lemonchiffon": (255, 250, 205), + "lightblue": (173, 216, 230), + "lightcoral": (240, 128, 128), + "lightcyan": (224, 255, 255), + "lightgoldenrodyellow": (250, 250, 210), + "lightgray": (211, 211, 211), + "lightgreen": (144, 238, 144), + "lightgrey": (211, 211, 211), + "lightpink": (255, 182, 193), + "lightsalmon": (255, 160, 122), + "lightseagreen": (32, 178, 170), + "lightskyblue": (135, 206, 250), + "lightslategray": (119, 136, 153), + "lightslategrey": (119, 136, 153), + "lightsteelblue": (176, 196, 222), + "lightyellow": (255, 255, 224), + "lime": (0, 255, 0), + "limegreen": (50, 205, 50), + "linen": (250, 240, 230), + "magenta": (255, 0, 255), + "maroon": (128, 0, 0), + "mediumaquamarine": (102, 205, 170), + "mediumblue": (0, 0, 205), + "mediumorchid": (186, 85, 211), + "mediumpurple": (147, 112, 219), + "mediumseagreen": (60, 179, 113), + "mediumslateblue": (123, 104, 238), + "mediumspringgreen": (0, 250, 154), + "mediumturquoise": (72, 209, 204), + "mediumvioletred": (199, 21, 133), + "midnightblue": (25, 25, 112), + "mintcream": (245, 255, 250), + "mistyrose": (255, 228, 225), + "moccasin": (255, 228, 181), + "navajowhite": (255, 222, 173), + "navy": (0, 0, 128), + "oldlace": (253, 245, 230), + "olive": (128, 128, 0), + "olivedrab": (107, 142, 35), + "orange": (255, 165, 0), + "orangered": (255, 69, 0), + "orchid": (218, 112, 214), + "palegoldenrod": (238, 232, 170), + "palegreen": (152, 251, 152), + "paleturquoise": (175, 238, 238), + "palevioletred": (219, 112, 147), + "papayawhip": (255, 239, 213), + "peachpuff": (255, 218, 185), + "peru": (205, 133, 63), + "pink": (255, 192, 203), + "plum": (221, 160, 221), + "powderblue": (176, 224, 230), + "purple": (128, 0, 128), + "red": (255, 0, 0), + "rosybrown": (188, 143, 143), + "royalblue": (65, 105, 225), + "saddlebrown": (139, 69, 19), + "salmon": (250, 128, 114), + "sandybrown": (244, 164, 96), + "seagreen": (46, 139, 87), + "seashell": (255, 245, 238), + "sienna": (160, 82, 45), + "silver": (192, 192, 192), + "skyblue": (135, 206, 235), + "slateblue": (106, 90, 205), + "slategray": (112, 128, 144), + "slategrey": (112, 128, 144), + "snow": (255, 250, 250), + "springgreen": (0, 255, 127), + "steelblue": (70, 130, 180), + "tan": (210, 180, 140), + "teal": (0, 128, 128), + "thistle": (216, 191, 216), + "tomato": (255, 99, 71), + "turquoise": (64, 224, 208), + "violet": (238, 130, 238), + "wheat": (245, 222, 179), + "white": (255, 255, 255), + "whitesmoke": (245, 245, 245), + "yellow": (255, 255, 0), + "yellowgreen": (154, 205, 50), } @@ -170,7 +170,7 @@ def _get_plot_image(plot, cwd): from IPython.display import Image # Make sure .png file was created - png_filename = plot.filename if plot.filename is not None else f'plot_{plot.id}' + png_filename = plot.filename if plot.filename is not None else f"plot_{plot.id}" # Add file extension if not already present. The C++ code added it # automatically if it wasn't present. @@ -181,12 +181,13 @@ def _get_plot_image(plot, cwd): if not png_file.exists(): raise FileNotFoundError( f"Could not find .png image for plot {plot.id}. Your version of " - "OpenMC may not be built against libpng.") + "OpenMC may not be built against libpng." + ) return Image(str(png_file)) -def voxel_to_vtk(voxel_file: PathLike, output: PathLike = 'plot.vti'): +def voxel_to_vtk(voxel_file: PathLike, output: PathLike = "plot.vti"): """Converts a voxel HDF5 file to a VTK file .. versionadded:: 0.14.0 @@ -301,13 +302,13 @@ class PlotBase(IDManagerMixin): next_id = 1 used_ids = set() - def __init__(self, plot_id=None, name=''): + def __init__(self, plot_id=None, name=""): # Initialize Plot class attributes self.id = plot_id self.name = name self._pixels = [400, 400] self._filename = None - self._color_by = 'cell' + self._color_by = "cell" self._background = None self._mask_components = None self._mask_background = None @@ -322,7 +323,7 @@ def name(self): @name.setter def name(self, name): - cv.check_type('plot name', name, str) + cv.check_type("plot name", name, str) self._name = name @property @@ -331,10 +332,10 @@ def pixels(self): @pixels.setter def pixels(self, pixels): - cv.check_type('plot pixels', pixels, Iterable, Integral) - cv.check_length('plot pixels', pixels, 2, 3) + cv.check_type("plot pixels", pixels, Iterable, Integral) + cv.check_length("plot pixels", pixels, 2, 3) for dim in pixels: - cv.check_greater_than('plot pixels', dim, 0) + cv.check_greater_than("plot pixels", dim, 0) self._pixels = pixels @property @@ -343,7 +344,7 @@ def filename(self): @filename.setter def filename(self, filename): - cv.check_type('filename', filename, str) + cv.check_type("filename", filename, str) self._filename = filename @property @@ -352,7 +353,7 @@ def color_by(self): @color_by.setter def color_by(self, color_by): - cv.check_value('plot color_by', color_by, ['cell', 'material']) + cv.check_value("plot color_by", color_by, ["cell", "material"]) self._color_by = color_by @property @@ -361,7 +362,7 @@ def background(self): @background.setter def background(self, background): - self._check_color('plot background', background) + self._check_color("plot background", background) self._background = background @property @@ -370,8 +371,12 @@ def mask_components(self): @mask_components.setter def mask_components(self, mask_components): - cv.check_type('plot mask components', mask_components, Iterable, - (openmc.Cell, openmc.Material, Integral)) + cv.check_type( + "plot mask components", + mask_components, + Iterable, + (openmc.Cell, openmc.Material, Integral), + ) self._mask_components = mask_components @property @@ -380,7 +385,7 @@ def mask_background(self): @mask_background.setter def mask_background(self, mask_background): - self._check_color('plot mask background', mask_background) + self._check_color("plot mask background", mask_background) self._mask_background = mask_background @property @@ -389,8 +394,9 @@ def show_overlaps(self): @show_overlaps.setter def show_overlaps(self, show_overlaps): - cv.check_type(f'Show overlaps flag for Plot ID="{self.id}"', - show_overlaps, bool) + cv.check_type( + f'Show overlaps flag for Plot ID="{self.id}"', show_overlaps, bool + ) self._show_overlaps = show_overlaps @property @@ -399,7 +405,7 @@ def overlap_color(self): @overlap_color.setter def overlap_color(self, overlap_color): - self._check_color('plot overlap color', overlap_color) + self._check_color("plot overlap color", overlap_color) self._overlap_color = overlap_color @property @@ -408,11 +414,12 @@ def colors(self): @colors.setter def colors(self, colors): - cv.check_type('plot colors', colors, Mapping) + cv.check_type("plot colors", colors, Mapping) for key, value in colors.items(): - cv.check_type('plot color key', key, - (openmc.Cell, openmc.Material, Integral)) - self._check_color('plot color value', value) + cv.check_type( + "plot color key", key, (openmc.Cell, openmc.Material, Integral) + ) + self._check_color("plot color value", value) self._colors = colors @property @@ -421,8 +428,8 @@ def level(self): @level.setter def level(self, plot_level): - cv.check_type('plot level', plot_level, Integral) - cv.check_greater_than('plot level', plot_level, 0, equality=True) + cv.check_type("plot level", plot_level, Integral) + cv.check_greater_than("plot level", plot_level, 0, equality=True) self._level = plot_level @staticmethod @@ -435,8 +442,8 @@ def _check_color(err_string, color): cv.check_length(err_string, color, 3) for rgb in color: cv.check_type(err_string, rgb, Real) - cv.check_greater_than('RGB component', rgb, 0, True) - cv.check_less_than('RGB component', rgb, 256) + cv.check_greater_than("RGB component", rgb, 0, True) + cv.check_less_than("RGB component", rgb, 256) # Helper function that returns the domain ID given either a # Cell/Material object or the domain ID itself @@ -459,12 +466,12 @@ def colorize(self, geometry, seed=1): """ - cv.check_type('geometry', geometry, openmc.Geometry) - cv.check_type('seed', seed, Integral) - cv.check_greater_than('seed', seed, 1, equality=True) + cv.check_type("geometry", geometry, openmc.Geometry) + cv.check_type("seed", seed, Integral) + cv.check_greater_than("seed", seed, 1, equality=True) # Get collections of the domains which will be plotted - if self.color_by == 'material': + if self.color_by == "material": domains = geometry.get_all_materials().values() else: domains = geometry.get_all_cells().values() @@ -495,25 +502,26 @@ def to_xml_element(self): element.set("color_by", self._color_by) subelement = ET.SubElement(element, "pixels") - subelement.text = ' '.join(map(str, self._pixels)) + subelement.text = " ".join(map(str, self._pixels)) if self._background is not None: subelement = ET.SubElement(element, "background") color = self._background if isinstance(color, str): color = _SVG_COLORS[color.lower()] - subelement.text = ' '.join(str(x) for x in color) + subelement.text = " ".join(str(x) for x in color) if self._mask_components is not None: subelement = ET.SubElement(element, "mask") - subelement.set("components", ' '.join( - str(PlotBase._get_id(d)) for d in self._mask_components)) + subelement.set( + "components", + " ".join(str(PlotBase._get_id(d)) for d in self._mask_components), + ) color = self._mask_background if color is not None: if isinstance(color, str): color = _SVG_COLORS[color.lower()] - subelement.set("background", ' '.join( - str(x) for x in color)) + subelement.set("background", " ".join(str(x) for x in color)) if self._level is not None: subelement = ET.SubElement(element, "level") @@ -580,12 +588,12 @@ class Plot(PlotBase): """ - def __init__(self, plot_id=None, name=''): + def __init__(self, plot_id=None, name=""): super().__init__(plot_id, name) self._width = [4.0, 4.0] - self._origin = [0., 0., 0.] - self._type = 'slice' - self._basis = 'xy' + self._origin = [0.0, 0.0, 0.0] + self._type = "slice" + self._basis = "xy" self._meshlines = None @property @@ -594,8 +602,8 @@ def width(self): @width.setter def width(self, width): - cv.check_type('plot width', width, Iterable, Real) - cv.check_length('plot width', width, 2, 3) + cv.check_type("plot width", width, Iterable, Real) + cv.check_length("plot width", width, 2, 3) self._width = width @property @@ -604,8 +612,8 @@ def origin(self): @origin.setter def origin(self, origin): - cv.check_type('plot origin', origin, Iterable, Real) - cv.check_length('plot origin', origin, 3) + cv.check_type("plot origin", origin, Iterable, Real) + cv.check_length("plot origin", origin, 3) self._origin = origin @property @@ -614,7 +622,7 @@ def type(self): @type.setter def type(self, plottype): - cv.check_value('plot type', plottype, ['slice', 'voxel']) + cv.check_value("plot type", plottype, ["slice", "voxel"]) self._type = plottype @property @@ -623,7 +631,7 @@ def basis(self): @basis.setter def basis(self, basis): - cv.check_value('plot basis', basis, _BASES) + cv.check_value("plot basis", basis, _BASES) self._basis = basis @property @@ -632,57 +640,57 @@ def meshlines(self): @meshlines.setter def meshlines(self, meshlines): - cv.check_type('plot meshlines', meshlines, dict) - if 'type' not in meshlines: - msg = f'Unable to set the meshlines to "{meshlines}" which ' \ + cv.check_type("plot meshlines", meshlines, dict) + if "type" not in meshlines: + msg = ( + f'Unable to set the meshlines to "{meshlines}" which ' 'does not have a "type" key' + ) raise ValueError(msg) - elif meshlines['type'] not in ['tally', 'entropy', 'ufs', 'cmfd']: + elif meshlines["type"] not in ["tally", "entropy", "ufs", "cmfd"]: msg = f"Unable to set the meshlines with type \"{meshlines['type']}\"" raise ValueError(msg) - if 'id' in meshlines: - cv.check_type('plot meshlines id', meshlines['id'], Integral) - cv.check_greater_than('plot meshlines id', meshlines['id'], 0, - equality=True) + if "id" in meshlines: + cv.check_type("plot meshlines id", meshlines["id"], Integral) + cv.check_greater_than( + "plot meshlines id", meshlines["id"], 0, equality=True + ) - if 'linewidth' in meshlines: - cv.check_type('plot mesh linewidth', - meshlines['linewidth'], Integral) - cv.check_greater_than('plot mesh linewidth', meshlines['linewidth'], - 0, equality=True) + if "linewidth" in meshlines: + cv.check_type("plot mesh linewidth", meshlines["linewidth"], Integral) + cv.check_greater_than( + "plot mesh linewidth", meshlines["linewidth"], 0, equality=True + ) - if 'color' in meshlines: - self._check_color('plot meshlines color', meshlines['color']) + if "color" in meshlines: + self._check_color("plot meshlines color", meshlines["color"]) self._meshlines = meshlines def __repr__(self): - string = 'Plot\n' - string += '{: <16}=\t{}\n'.format('\tID', self._id) - string += '{: <16}=\t{}\n'.format('\tName', self._name) - string += '{: <16}=\t{}\n'.format('\tFilename', self._filename) - string += '{: <16}=\t{}\n'.format('\tType', self._type) - string += '{: <16}=\t{}\n'.format('\tBasis', self._basis) - string += '{: <16}=\t{}\n'.format('\tWidth', self._width) - string += '{: <16}=\t{}\n'.format('\tOrigin', self._origin) - string += '{: <16}=\t{}\n'.format('\tPixels', self._pixels) - string += '{: <16}=\t{}\n'.format('\tColor by', self._color_by) - string += '{: <16}=\t{}\n'.format('\tBackground', self._background) - string += '{: <16}=\t{}\n'.format('\tMask components', - self._mask_components) - string += '{: <16}=\t{}\n'.format('\tMask background', - self._mask_background) - string += '{: <16}=\t{}\n'.format('\tOverlap Color', - self._overlap_color) - string += '{: <16}=\t{}\n'.format('\tColors', self._colors) - string += '{: <16}=\t{}\n'.format('\tLevel', self._level) - string += '{: <16}=\t{}\n'.format('\tMeshlines', self._meshlines) + string = "Plot\n" + string += "{: <16}=\t{}\n".format("\tID", self._id) + string += "{: <16}=\t{}\n".format("\tName", self._name) + string += "{: <16}=\t{}\n".format("\tFilename", self._filename) + string += "{: <16}=\t{}\n".format("\tType", self._type) + string += "{: <16}=\t{}\n".format("\tBasis", self._basis) + string += "{: <16}=\t{}\n".format("\tWidth", self._width) + string += "{: <16}=\t{}\n".format("\tOrigin", self._origin) + string += "{: <16}=\t{}\n".format("\tPixels", self._pixels) + string += "{: <16}=\t{}\n".format("\tColor by", self._color_by) + string += "{: <16}=\t{}\n".format("\tBackground", self._background) + string += "{: <16}=\t{}\n".format("\tMask components", self._mask_components) + string += "{: <16}=\t{}\n".format("\tMask background", self._mask_background) + string += "{: <16}=\t{}\n".format("\tOverlap Color", self._overlap_color) + string += "{: <16}=\t{}\n".format("\tColors", self._colors) + string += "{: <16}=\t{}\n".format("\tLevel", self._level) + string += "{: <16}=\t{}\n".format("\tMeshlines", self._meshlines) return string @classmethod - def from_geometry(cls, geometry, basis='xy', slice_coord=0.): + def from_geometry(cls, geometry, basis="xy", slice_coord=0.0): """Return plot that encompasses a geometry. Parameters @@ -697,17 +705,17 @@ def from_geometry(cls, geometry, basis='xy', slice_coord=0.): origin. """ - cv.check_type('geometry', geometry, openmc.Geometry) - cv.check_value('basis', basis, _BASES) + cv.check_type("geometry", geometry, openmc.Geometry) + cv.check_value("basis", basis, _BASES) # Decide which axes to keep - if basis == 'xy': + if basis == "xy": pick_index = (0, 1) slice_index = 2 - elif basis == 'yz': + elif basis == "yz": pick_index = (1, 2) slice_index = 0 - elif basis == 'xz': + elif basis == "xz": pick_index = (0, 2) slice_index = 1 @@ -717,18 +725,21 @@ def from_geometry(cls, geometry, basis='xy', slice_coord=0.): upper_right = upper_right[np.array(pick_index)] if np.any(np.isinf((lower_left, upper_right))): - raise ValueError('The geometry does not appear to be bounded ' - f'in the {basis} plane.') + raise ValueError( + "The geometry does not appear to be bounded " f"in the {basis} plane." + ) plot = cls() - plot.origin = np.insert((lower_left + upper_right)/2, - slice_index, slice_coord) + plot.origin = np.insert( + (lower_left + upper_right) / 2, slice_index, slice_coord + ) plot.width = upper_right - lower_left plot.basis = basis return plot - def highlight_domains(self, geometry, domains, seed=1, - alpha=0.5, background='gray'): + 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 @@ -750,12 +761,11 @@ def highlight_domains(self, geometry, domains, seed=1, """ - cv.check_type('domains', domains, Iterable, - (openmc.Cell, openmc.Material)) - cv.check_type('alpha', alpha, Real) - cv.check_greater_than('alpha', alpha, 0., equality=True) - cv.check_less_than('alpha', alpha, 1., equality=True) - cv.check_type('background', background, Iterable) + cv.check_type("domains", domains, Iterable, (openmc.Cell, openmc.Material)) + cv.check_type("alpha", alpha, Real) + cv.check_greater_than("alpha", alpha, 0.0, equality=True) + cv.check_less_than("alpha", alpha, 1.0, equality=True) + cv.check_type("background", background, Iterable) # Get a background (R,G,B) tuple to apply in alpha compositing if isinstance(background, str): @@ -773,9 +783,9 @@ def highlight_domains(self, geometry, domains, seed=1, if isinstance(color, str): color = _SVG_COLORS[color.lower()] r, g, b = color - r = int(((1-alpha) * background[0]) + (alpha * r)) - g = int(((1-alpha) * background[1]) + (alpha * g)) - b = int(((1-alpha) * background[2]) + (alpha * b)) + 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._colors[domain] = (r, g, b) def to_xml_element(self): @@ -791,23 +801,24 @@ def to_xml_element(self): element = super().to_xml_element() element.set("type", self._type) - if self._type == 'slice': + if self._type == "slice": element.set("basis", self._basis) subelement = ET.SubElement(element, "origin") - subelement.text = ' '.join(map(str, self._origin)) + subelement.text = " ".join(map(str, self._origin)) subelement = ET.SubElement(element, "width") - subelement.text = ' '.join(map(str, self._width)) + subelement.text = " ".join(map(str, self._width)) if self._colors: - for domain, color in sorted(self._colors.items(), - key=lambda x: PlotBase._get_id(x[0])): + for domain, color in sorted( + self._colors.items(), key=lambda x: PlotBase._get_id(x[0]) + ): subelement = ET.SubElement(element, "color") subelement.set("id", str(PlotBase._get_id(domain))) if isinstance(color, str): color = _SVG_COLORS[color.lower()] - subelement.set("rgb", ' '.join(str(x) for x in color)) + subelement.set("rgb", " ".join(str(x) for x in color)) if self._show_overlaps: subelement = ET.SubElement(element, "show_overlaps") @@ -818,18 +829,17 @@ def to_xml_element(self): if isinstance(color, str): color = _SVG_COLORS[color.lower()] subelement = ET.SubElement(element, "overlap_color") - subelement.text = ' '.join(str(x) for x in color) + subelement.text = " ".join(str(x) for x in color) if self._meshlines is not None: subelement = ET.SubElement(element, "meshlines") - subelement.set("meshtype", self._meshlines['type']) - if 'id' in self._meshlines: - subelement.set("id", str(self._meshlines['id'])) - if 'linewidth' in self._meshlines: - subelement.set("linewidth", str(self._meshlines['linewidth'])) - if 'color' in self._meshlines: - subelement.set("color", ' '.join(map( - str, self._meshlines['color']))) + subelement.set("meshtype", self._meshlines["type"]) + if "id" in self._meshlines: + subelement.set("id", str(self._meshlines["id"])) + if "linewidth" in self._meshlines: + subelement.set("linewidth", str(self._meshlines["linewidth"])) + if "color" in self._meshlines: + subelement.set("color", " ".join(map(str, self._meshlines["color"]))) return element @@ -849,13 +859,13 @@ def from_xml_element(cls, elem): """ plot_id = int(elem.get("id")) - name = get_text(elem, 'name', '') + name = get_text(elem, "name", "") plot = cls(plot_id, name) if "filename" in elem.keys(): plot.filename = elem.get("filename") plot.color_by = elem.get("color_by") plot.type = elem.get("type") - if plot.type == 'slice': + if plot.type == "slice": plot.basis = elem.get("basis") plot.origin = get_elem_tuple(elem, "origin", float) @@ -867,24 +877,21 @@ def from_xml_element(cls, elem): colors = {} for color_elem in elem.findall("color"): uid = int(color_elem.get("id")) - colors[uid] = tuple([int(x) - for x in color_elem.get("rgb").split()]) + colors[uid] = tuple([int(x) for x in color_elem.get("rgb").split()]) plot.colors = colors # Set masking information mask_elem = elem.find("mask") if mask_elem is not None: - plot.mask_components = [ - int(x) for x in mask_elem.get("components").split()] + plot.mask_components = [int(x) for x in mask_elem.get("components").split()] background = mask_elem.get("background") if background is not None: - plot.mask_background = tuple( - [int(x) for x in background.split()]) + plot.mask_background = tuple([int(x) for x in background.split()]) # show overlaps overlap_elem = elem.find("show_overlaps") if overlap_elem is not None: - plot.show_overlaps = (overlap_elem.text in ('true', '1')) + plot.show_overlaps = overlap_elem.text in ("true", "1") overlap_color = get_elem_tuple(elem, "overlap_color") if overlap_color is not None: plot.overlap_color = overlap_color @@ -897,20 +904,20 @@ def from_xml_element(cls, elem): # Set meshlines mesh_elem = elem.find("meshlines") if mesh_elem is not None: - meshlines = {'type': mesh_elem.get('meshtype')} - if 'id' in mesh_elem.keys(): - meshlines['id'] = int(mesh_elem.get('id')) - if 'linewidth' in mesh_elem.keys(): - meshlines['linewidth'] = int(mesh_elem.get('linewidth')) - if 'color' in mesh_elem.keys(): - meshlines['color'] = tuple( - [int(x) for x in mesh_elem.get('color').split()] + meshlines = {"type": mesh_elem.get("meshtype")} + if "id" in mesh_elem.keys(): + meshlines["id"] = int(mesh_elem.get("id")) + if "linewidth" in mesh_elem.keys(): + meshlines["linewidth"] = int(mesh_elem.get("linewidth")) + if "color" in mesh_elem.keys(): + meshlines["color"] = tuple( + [int(x) for x in mesh_elem.get("color").split()] ) plot.meshlines = meshlines return plot - def to_ipython_image(self, openmc_exec='openmc', cwd='.'): + def to_ipython_image(self, openmc_exec="openmc", cwd="."): """Render plot as an image This method runs OpenMC in plotting mode to produce a .png file. @@ -941,8 +948,12 @@ def to_ipython_image(self, openmc_exec='openmc', cwd='.'): # Return produced image return _get_plot_image(self, cwd) - def to_vtk(self, output: PathLike | None = None, - openmc_exec: str = 'openmc', cwd: str = '.'): + def to_vtk( + self, + output: PathLike | None = None, + openmc_exec: str = "openmc", + cwd: str = ".", + ): """Render plot as an voxel image This method runs OpenMC in plotting mode to produce a .vti file. @@ -964,8 +975,8 @@ def to_vtk(self, output: PathLike | None = None, Path of the .vti file produced """ - if self.type != 'voxel': - raise ValueError('Generating a VTK file only works for voxel plots') + if self.type != "voxel": + raise ValueError("Generating a VTK file only works for voxel plots") # Create plots.xml Plots([self]).export_to_xml(cwd) @@ -973,7 +984,9 @@ def to_vtk(self, output: PathLike | None = None, # Run OpenMC in geometry plotting mode and produces a h5 file openmc.plot_geometry(False, openmc_exec, cwd) - h5_voxel_filename = self.filename if self.filename is not None else f'plot_{self.id}' + h5_voxel_filename = ( + self.filename if self.filename is not None else f"plot_{self.id}" + ) # Add file extension if not already present if Path(h5_voxel_filename).suffix != ".h5": @@ -981,7 +994,7 @@ def to_vtk(self, output: PathLike | None = None, h5_voxel_file = Path(cwd) / h5_voxel_filename if output is None: - output = h5_voxel_file.with_suffix('.vti') + output = h5_voxel_file.with_suffix(".vti") return voxel_to_vtk(h5_voxel_file, output) @@ -1044,7 +1057,7 @@ class ProjectionPlot(PlotBase): values can be obtained using the set_transparent method. """ - def __init__(self, plot_id=None, name=''): + def __init__(self, plot_id=None, name=""): # Initialize Plot class attributes super().__init__(plot_id, name) self._horizontal_field_of_view = 70.0 @@ -1053,7 +1066,7 @@ def __init__(self, plot_id=None, name=''): self._up = (0.0, 0.0, 1.0) self._orthographic_width = 0.0 self._wireframe_thickness = 1 - self._wireframe_color = _SVG_COLORS['black'] + self._wireframe_color = _SVG_COLORS["black"] self._wireframe_domains = [] self._xs = {} @@ -1063,8 +1076,7 @@ def horizontal_field_of_view(self): @horizontal_field_of_view.setter def horizontal_field_of_view(self, horizontal_field_of_view): - cv.check_type('plot horizontal field of view', horizontal_field_of_view, - Real) + cv.check_type("plot horizontal field of view", horizontal_field_of_view, Real) assert horizontal_field_of_view > 0.0 assert horizontal_field_of_view < 180.0 self._horizontal_field_of_view = horizontal_field_of_view @@ -1075,8 +1087,8 @@ def camera_position(self): @camera_position.setter def camera_position(self, camera_position): - cv.check_type('plot camera position', camera_position, Iterable, Real) - cv.check_length('plot camera position', camera_position, 3) + cv.check_type("plot camera position", camera_position, Iterable, Real) + cv.check_length("plot camera position", camera_position, 3) self._camera_position = camera_position @property @@ -1085,8 +1097,8 @@ def look_at(self): @look_at.setter def look_at(self, look_at): - cv.check_type('plot look at', look_at, Iterable, Real) - cv.check_length('plot look at', look_at, 3) + cv.check_type("plot look at", look_at, Iterable, Real) + cv.check_length("plot look at", look_at, 3) self._look_at = look_at @property @@ -1095,8 +1107,8 @@ def up(self): @up.setter def up(self, up): - cv.check_type('plot up', up, Iterable, Real) - cv.check_length('plot up', up, 3) + cv.check_type("plot up", up, Iterable, Real) + cv.check_length("plot up", up, 3) self._up = up @property @@ -1105,7 +1117,7 @@ def orthographic_width(self): @orthographic_width.setter def orthographic_width(self, orthographic_width): - cv.check_type('plot orthographic width', orthographic_width, Real) + cv.check_type("plot orthographic width", orthographic_width, Real) assert orthographic_width >= 0.0 self._orthographic_width = orthographic_width @@ -1115,8 +1127,7 @@ def wireframe_thickness(self): @wireframe_thickness.setter def wireframe_thickness(self, wireframe_thickness): - cv.check_type('plot wireframe thickness', - wireframe_thickness, Integral) + cv.check_type("plot wireframe thickness", wireframe_thickness, Integral) assert wireframe_thickness >= 0 self._wireframe_thickness = wireframe_thickness @@ -1126,7 +1137,7 @@ def wireframe_color(self): @wireframe_color.setter def wireframe_color(self, wireframe_color): - self._check_color('plot wireframe color', wireframe_color) + self._check_color("plot wireframe color", wireframe_color) self._wireframe_color = wireframe_color @property @@ -1136,14 +1147,18 @@ def wireframe_domains(self): @wireframe_domains.setter def wireframe_domains(self, wireframe_domains): for region in wireframe_domains: - if self._color_by == 'material': + if self._color_by == "material": if not isinstance(region, openmc.Material): - raise Exception('Must provide a list of materials for \ - wireframe_region if color_by=Material') + raise Exception( + "Must provide a list of materials for \ + wireframe_region if color_by=Material" + ) else: if not isinstance(region, openmc.Cell): - raise Exception('Must provide a list of cells for \ - wireframe_region if color_by=cell') + raise Exception( + "Must provide a list of cells for \ + wireframe_region if color_by=cell" + ) self._wireframe_domains = wireframe_domains @property @@ -1152,10 +1167,10 @@ def xs(self): @xs.setter def xs(self, xs): - cv.check_type('plot xs', xs, Mapping) + cv.check_type("plot xs", xs, Mapping) for key, value in xs.items(): - cv.check_type('plot xs key', key, (openmc.Cell, openmc.Material)) - cv.check_type('plot xs value', value, Real) + cv.check_type("plot xs key", key, (openmc.Cell, openmc.Material)) + cv.check_type("plot xs value", value, Real) assert value >= 0.0 self._xs = xs @@ -1168,10 +1183,10 @@ def set_transparent(self, geometry): The geometry for which the plot is defined """ - cv.check_type('geometry', geometry, openmc.Geometry) + cv.check_type("geometry", geometry, openmc.Geometry) # Get collections of the domains which will be plotted - if self.color_by == 'material': + if self.color_by == "material": domains = geometry.get_all_materials().values() else: domains = geometry.get_all_cells().values() @@ -1194,10 +1209,10 @@ def to_xml_element(self): element.set("type", "projection") subelement = ET.SubElement(element, "camera_position") - subelement.text = ' '.join(map(str, self._camera_position)) + subelement.text = " ".join(map(str, self._camera_position)) subelement = ET.SubElement(element, "look_at") - subelement.text = ' '.join(map(str, self._look_at)) + subelement.text = " ".join(map(str, self._look_at)) subelement = ET.SubElement(element, "wireframe_thickness") subelement.text = str(self._wireframe_thickness) @@ -1206,23 +1221,22 @@ def to_xml_element(self): color = self._wireframe_color if isinstance(color, str): color = _SVG_COLORS[color.lower()] - subelement.text = ' '.join(str(x) for x in color) + subelement.text = " ".join(str(x) for x in color) if self._wireframe_domains: id_list = [x.id for x in self._wireframe_domains] subelement = ET.SubElement(element, "wireframe_ids") - subelement.text = ' '.join([str(x) for x in id_list]) + subelement.text = " ".join([str(x) for x in id_list]) # note that this differs from the slice plot colors # in that "xs" must also be specified if self._colors: - for domain, color in sorted(self._colors.items(), - key=lambda x: x[0].id): + for domain, color in sorted(self._colors.items(), key=lambda x: x[0].id): subelement = ET.SubElement(element, "color") subelement.set("id", str(domain.id)) if isinstance(color, str): color = _SVG_COLORS[color.lower()] - subelement.set("rgb", ' '.join(str(x) for x in color)) + subelement.set("rgb", " ".join(str(x) for x in color)) subelement.set("xs", str(self._xs[domain])) subelement = ET.SubElement(element, "horizontal_field_of_view") @@ -1236,30 +1250,32 @@ def to_xml_element(self): return element def __repr__(self): - string = 'Projection Plot\n' - string += '{: <16}=\t{}\n'.format('\tID', self._id) - string += '{: <16}=\t{}\n'.format('\tName', self._name) - string += '{: <16}=\t{}\n'.format('\tFilename', self._filename) - string += '{: <16}=\t{}\n'.format('\tHorizontal FOV', - self._horizontal_field_of_view) - string += '{: <16}=\t{}\n'.format('\tOrthographic width', - self._orthographic_width) - string += '{: <16}=\t{}\n'.format('\tWireframe thickness', - self._wireframe_thickness) - string += '{: <16}=\t{}\n'.format('\tWireframe color', - self._wireframe_color) - string += '{: <16}=\t{}\n'.format('\tWireframe domains', - self._wireframe_domains) - string += '{: <16}=\t{}\n'.format('\tCamera position', - self._camera_position) - string += '{: <16}=\t{}\n'.format('\tLook at', self._look_at) - string += '{: <16}=\t{}\n'.format('\tUp', self._up) - string += '{: <16}=\t{}\n'.format('\tPixels', self._pixels) - string += '{: <16}=\t{}\n'.format('\tColor by', self._color_by) - string += '{: <16}=\t{}\n'.format('\tBackground', self._background) - string += '{: <16}=\t{}\n'.format('\tColors', self._colors) - string += '{: <16}=\t{}\n'.format('\tTransparencies', self._xs) - string += '{: <16}=\t{}\n'.format('\tLevel', self._level) + string = "Projection Plot\n" + string += "{: <16}=\t{}\n".format("\tID", self._id) + string += "{: <16}=\t{}\n".format("\tName", self._name) + string += "{: <16}=\t{}\n".format("\tFilename", self._filename) + string += "{: <16}=\t{}\n".format( + "\tHorizontal FOV", self._horizontal_field_of_view + ) + string += "{: <16}=\t{}\n".format( + "\tOrthographic width", self._orthographic_width + ) + string += "{: <16}=\t{}\n".format( + "\tWireframe thickness", self._wireframe_thickness + ) + string += "{: <16}=\t{}\n".format("\tWireframe color", self._wireframe_color) + string += "{: <16}=\t{}\n".format( + "\tWireframe domains", self._wireframe_domains + ) + string += "{: <16}=\t{}\n".format("\tCamera position", self._camera_position) + string += "{: <16}=\t{}\n".format("\tLook at", self._look_at) + string += "{: <16}=\t{}\n".format("\tUp", self._up) + string += "{: <16}=\t{}\n".format("\tPixels", self._pixels) + string += "{: <16}=\t{}\n".format("\tColor by", self._color_by) + string += "{: <16}=\t{}\n".format("\tBackground", self._background) + string += "{: <16}=\t{}\n".format("\tColors", self._colors) + string += "{: <16}=\t{}\n".format("\tTransparencies", self._xs) + string += "{: <16}=\t{}\n".format("\tLevel", self._level) return string @classmethod @@ -1315,13 +1331,11 @@ def from_xml_element(cls, elem): # Set masking information mask_elem = elem.find("mask") if mask_elem is not None: - mask_components = [int(x) - for x in mask_elem.get("components").split()] + mask_components = [int(x) for x in mask_elem.get("components").split()] # TODO: set mask components (needs geometry information) background = mask_elem.get("background") if background is not None: - plot.mask_background = tuple( - [int(x) for x in background.split()]) + plot.mask_background = tuple([int(x) for x in background.split()]) # Set universe level level = elem.find("level") @@ -1354,7 +1368,7 @@ class Plots(cv.CheckedList): """ def __init__(self, plots=None): - super().__init__((Plot, ProjectionPlot), 'plots collection') + super().__init__((Plot, ProjectionPlot), "plots collection") self._plots_file = ET.Element("plots") if plots is not None: self += plots @@ -1402,8 +1416,9 @@ def colorize(self, geometry, seed=1): for plot in self: plot.colorize(geometry, seed) - def highlight_domains(self, geometry, domains, seed=1, - alpha=0.5, background='gray'): + 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 @@ -1458,7 +1473,7 @@ def to_xml_element(self): return self._plots_file - def export_to_xml(self, path='plots.xml'): + def export_to_xml(self, path="plots.xml"): """Export plot specifications to an XML file. Parameters @@ -1470,12 +1485,12 @@ def export_to_xml(self, path='plots.xml'): # Check if path is a directory p = Path(path) if p.is_dir(): - p /= 'plots.xml' + p /= "plots.xml" self.to_xml_element() # Write the XML Tree to the plots.xml file tree = ET.ElementTree(self._plots_file) - tree.write(str(p), xml_declaration=True, encoding='utf-8') + tree.write(str(p), xml_declaration=True, encoding="utf-8") @classmethod def from_xml_element(cls, elem): @@ -1494,16 +1509,16 @@ def from_xml_element(cls, elem): """ # Generate each plot plots = cls() - for e in elem.findall('plot'): - plot_type = e.get('type') - if plot_type == 'projection': + for e in elem.findall("plot"): + plot_type = e.get("type") + if plot_type == "projection": plots.append(ProjectionPlot.from_xml_element(e)) else: plots.append(Plot.from_xml_element(e)) return plots @classmethod - def from_xml(cls, path='plots.xml'): + def from_xml(cls, path="plots.xml"): """Generate plots collection from XML file Parameters diff --git a/openmc/plotter.py b/openmc/plotter.py index 85c4963a76f..c4f024ef142 100644 --- a/openmc/plotter.py +++ b/openmc/plotter.py @@ -9,20 +9,45 @@ import openmc.data # Supported keywords for continuous-energy cross section plotting -PLOT_TYPES = {'total', 'scatter', 'elastic', 'inelastic', 'fission', - 'absorption', 'capture', 'nu-fission', 'nu-scatter', 'unity', - 'slowing-down power', 'damage'} +PLOT_TYPES = { + "total", + "scatter", + "elastic", + "inelastic", + "fission", + "absorption", + "capture", + "nu-fission", + "nu-scatter", + "unity", + "slowing-down power", + "damage", +} # Supported keywords for multi-group cross section plotting -PLOT_TYPES_MGXS = {'total', 'absorption', 'scatter', 'fission', - 'kappa-fission', 'nu-fission', 'prompt-nu-fission', - 'deleyed-nu-fission', 'chi', 'chi-prompt', 'chi-delayed', - 'inverse-velocity', 'beta', 'decay-rate', 'unity'} +PLOT_TYPES_MGXS = { + "total", + "absorption", + "scatter", + "fission", + "kappa-fission", + "nu-fission", + "prompt-nu-fission", + "deleyed-nu-fission", + "chi", + "chi-prompt", + "chi-delayed", + "inverse-velocity", + "beta", + "decay-rate", + "unity", +} # Create a dictionary which can be used to convert PLOT_TYPES_MGXS to the # openmc.XSdata attribute name needed to access the data -_PLOT_MGXS_ATTR = {line: line.replace(' ', '_').replace('-', '_') - for line in PLOT_TYPES_MGXS} -_PLOT_MGXS_ATTR['scatter'] = 'scatter_matrix' +_PLOT_MGXS_ATTR = { + line: line.replace(" ", "_").replace("-", "_") for line in PLOT_TYPES_MGXS +} +_PLOT_MGXS_ATTR["scatter"] = "scatter_matrix" # Special MT values UNITY_MT = -1 @@ -31,27 +56,31 @@ # MTs to combine to generate associated plot_types _INELASTIC = [mt for mt in openmc.data.SUM_RULES[3] if mt != 27] PLOT_TYPES_MT = { - 'total': openmc.data.SUM_RULES[1], - 'scatter': [2] + _INELASTIC, - 'elastic': [2], - 'inelastic': _INELASTIC, - 'fission': [18], - 'absorption': [27], - 'capture': [101], - 'nu-fission': [18], - 'nu-scatter': [2] + _INELASTIC, - 'unity': [UNITY_MT], - 'slowing-down power': [2] + [XI_MT], - 'damage': [444] + "total": openmc.data.SUM_RULES[1], + "scatter": [2] + _INELASTIC, + "elastic": [2], + "inelastic": _INELASTIC, + "fission": [18], + "absorption": [27], + "capture": [101], + "nu-fission": [18], + "nu-scatter": [2] + _INELASTIC, + "unity": [UNITY_MT], + "slowing-down power": [2] + [XI_MT], + "damage": [444], } # Types of plots to plot linearly in y -PLOT_TYPES_LINEAR = {'nu-fission / fission', 'nu-scatter / scatter', - 'nu-fission / absorption', 'fission / absorption'} +PLOT_TYPES_LINEAR = { + "nu-fission / fission", + "nu-scatter / scatter", + "nu-fission / absorption", + "fission / absorption", +} # Minimum and maximum energies for plotting (units of eV) -_MIN_E = 1.e-5 -_MAX_E = 20.e6 +_MIN_E = 1.0e-5 +_MAX_E = 20.0e6 ELEMENT_NAMES = list(openmc.data.ELEMENT_SYMBOL.values())[1:] @@ -62,19 +91,19 @@ def _get_legend_label(this, type): if isinstance(this, str): if type in openmc.data.DADZ: if this in ELEMENT_NAMES: - return f'{this} {type}' + return f"{this} {type}" else: # this is a nuclide so the legend can contain more information z, a, m = openmc.data.zam(this) da, dz = openmc.data.DADZ[type] gnds_name = openmc.data.gnds_name(z + dz, a + da, m) # makes a string with nuclide reaction and new nuclide # For example "Be9 (n,2n) Be8" - return f'{this} {type} {gnds_name}' - return f'{this} {type}' - elif this.name == '': - return f'Material {this.id} {type}' + return f"{this} {type} {gnds_name}" + return f"{this} {type}" + elif this.name == "": + return f"Material {this.id} {type}" else: - return f'{this.name} {type}' + return f"{this.name} {type}" def _get_yaxis_label(reactions, divisor_types): @@ -95,7 +124,7 @@ def _get_yaxis_label(reactions, divisor_types): ) stem = "Microscopic" elif all(isinstance(item, openmc.Material) for item in reactions.keys()): - stem = 'Macroscopic' + stem = "Macroscopic" else: msg = "Mixture of openmc.Material and elements/nuclides. Invalid type for plotting" raise TypeError(msg) @@ -110,16 +139,17 @@ def _get_yaxis_label(reactions, divisor_types): "Heating": "[eV-barn]", }[stem] - return f'{stem} {mid} {units}' + return f"{stem} {mid} {units}" + def _get_title(reactions): """Gets a title for the type of data plotted""" if len(reactions) == 1: - this, = reactions + (this,) = reactions name = this.name if isinstance(this, openmc.Material) else this - return f'Cross Section Plot For {name}' + return f"Cross Section Plot For {name}" else: - return 'Cross Section Plot' + return "Cross Section Plot" def plot_xs( @@ -215,13 +245,19 @@ def plot_xs( if plot_CE: cv.check_type("this", this, (str, openmc.Material)) # Calculate for the CE cross sections - E, data = calculate_cexs(this, types, temperature, sab_name, - ce_cross_sections, enrichment) + E, data = calculate_cexs( + this, types, temperature, sab_name, ce_cross_sections, enrichment + ) if divisor_types: - cv.check_length('divisor types', divisor_types, len(types)) - Ediv, data_div = calculate_cexs(this, divisor_types, temperature, - sab_name, ce_cross_sections, - enrichment) + cv.check_length("divisor types", divisor_types, len(types)) + Ediv, data_div = calculate_cexs( + this, + divisor_types, + temperature, + sab_name, + ce_cross_sections, + enrichment, + ) # Create a new union grid, interpolate data and data_div on to that # grid, and then do the actual division @@ -230,47 +266,59 @@ def plot_xs( data_new = np.zeros((len(types), len(E))) for line in range(len(types)): - data_new[line, :] = \ - np.divide(np.interp(E, Enum, data[line, :]), - np.interp(E, Ediv, data_div[line, :])) - if divisor_types[line] != 'unity': - types[line] = types[line] + ' / ' + divisor_types[line] + data_new[line, :] = np.divide( + np.interp(E, Enum, data[line, :]), + np.interp(E, Ediv, data_div[line, :]), + ) + if divisor_types[line] != "unity": + types[line] = types[line] + " / " + divisor_types[line] data = data_new else: # Calculate for MG cross sections - E, data = calculate_mgxs(this, types, orders, temperature, - mg_cross_sections, ce_cross_sections, - enrichment) + E, data = calculate_mgxs( + this, + types, + orders, + temperature, + mg_cross_sections, + ce_cross_sections, + enrichment, + ) if divisor_types: - cv.check_length('divisor types', divisor_types, len(types)) - Ediv, data_div = calculate_mgxs(this, divisor_types, - divisor_orders, temperature, - mg_cross_sections, - ce_cross_sections, enrichment) + cv.check_length("divisor types", divisor_types, len(types)) + Ediv, data_div = calculate_mgxs( + this, + divisor_types, + divisor_orders, + temperature, + mg_cross_sections, + ce_cross_sections, + enrichment, + ) # Perform the division for line in range(len(types)): data[line, :] /= data_div[line, :] - if divisor_types[line] != 'unity': - types[line] += ' / ' + divisor_types[line] + if divisor_types[line] != "unity": + types[line] += " / " + divisor_types[line] E *= axis_scaling_factor[energy_axis_units] # Plot the data for i in range(len(data)): data[i, :] = np.nan_to_num(data[i, :]) - if np.sum(data[i, :]) > 0.: + if np.sum(data[i, :]) > 0.0: ax.plot(E, data[i, :], label=_get_legend_label(this, types[i])) # Set to loglog or semilogx depending on if we are plotting a data # type which we expect to vary linearly if set(all_types).issubset(PLOT_TYPES_LINEAR): - ax.set_xscale('log') - ax.set_yscale('linear') + ax.set_xscale("log") + ax.set_yscale("linear") else: - ax.set_xscale('log') - ax.set_yscale('log') + ax.set_xscale("log") + ax.set_yscale("log") ax.set_xlabel(f"Energy [{energy_axis_units}]") if plot_CE: @@ -282,14 +330,21 @@ def plot_xs( ax.set_xlim(E[-1], E[0]) ax.set_ylabel(_get_yaxis_label(reactions, divisor_types)) - ax.legend(loc='best') + ax.legend(loc="best") ax.set_title(_get_title(reactions)) return fig -def calculate_cexs(this, types, temperature=294., sab_name=None, - cross_sections=None, enrichment=None, ncrystal_cfg=None): +def calculate_cexs( + this, + types, + temperature=294.0, + sab_name=None, + cross_sections=None, + enrichment=None, + ncrystal_cfg=None, +): """Calculates continuous-energy cross sections of a requested type. Parameters @@ -325,12 +380,12 @@ def calculate_cexs(this, types, temperature=294., sab_name=None, """ # Check types - cv.check_type('this', this, (str, openmc.Material)) - cv.check_type('temperature', temperature, Real) + cv.check_type("this", this, (str, openmc.Material)) + cv.check_type("temperature", temperature, Real) if sab_name: - cv.check_type('sab_name', sab_name, str) + cv.check_type("sab_name", sab_name, str) if enrichment: - cv.check_type('enrichment', enrichment, Real) + cv.check_type("enrichment", enrichment, Real) if isinstance(this, str): if this in ELEMENT_NAMES: @@ -339,8 +394,7 @@ def calculate_cexs(this, types, temperature=294., sab_name=None, ) else: energy_grid, xs = _calculate_cexs_nuclide( - this, types, temperature, sab_name, cross_sections, - ncrystal_cfg + this, types, temperature, sab_name, cross_sections, ncrystal_cfg ) # Convert xs (Iterable of Callable) to a grid of cross section values @@ -350,14 +404,21 @@ def calculate_cexs(this, types, temperature=294., sab_name=None, for line in range(len(types)): data[line, :] = xs[line](energy_grid) else: - energy_grid, data = _calculate_cexs_elem_mat(this, types, temperature, - cross_sections) + energy_grid, data = _calculate_cexs_elem_mat( + this, types, temperature, cross_sections + ) return energy_grid, data -def _calculate_cexs_nuclide(this, types, temperature=294., sab_name=None, - cross_sections=None, ncrystal_cfg=None): +def _calculate_cexs_nuclide( + this, + types, + temperature=294.0, + sab_name=None, + cross_sections=None, + ncrystal_cfg=None, +): """Calculates continuous-energy cross sections of a requested type. Parameters @@ -402,7 +463,7 @@ def _calculate_cexs_nuclide(this, types, temperature=294., sab_name=None, xs = [] lib = library.get_by_material(this) if lib is not None: - nuc = openmc.data.IncidentNeutron.from_hdf5(lib['path']) + nuc = openmc.data.IncidentNeutron.from_hdf5(lib["path"]) # Obtain the nearest temperature if strT in nuc.temperatures: nucT = strT @@ -424,7 +485,7 @@ def _calculate_cexs_nuclide(this, types, temperature=294., sab_name=None, # Create an energy grid composed the S(a,b) and the nuclide's grid grid = nuc.energy[nucT] - sab_Emax = 0. + sab_Emax = 0.0 sab_funcs = [] if sab.elastic is not None: elastic = sab.elastic.xs[sabT] @@ -441,7 +502,7 @@ def _calculate_cexs_nuclide(this, types, temperature=294., sab_name=None, inelastic = sab.inelastic.xs[sabT] grid = np.union1d(grid, inelastic.x) if inelastic.x[-1] > sab_Emax: - sab_Emax = inelastic.x[-1] + sab_Emax = inelastic.x[-1] sab_funcs.append(inelastic) energy_grid = grid else: @@ -453,10 +514,13 @@ def _calculate_cexs_nuclide(this, types, temperature=294., sab_name=None, yields = [] for line in types: if line in PLOT_TYPES: - tmp_mts = [mtj for mti in PLOT_TYPES_MT[line] for mtj in - nuc.get_reaction_components(mti)] + tmp_mts = [ + mtj + for mti in PLOT_TYPES_MT[line] + for mtj in nuc.get_reaction_components(mti) + ] mts.append(tmp_mts) - if line.startswith('nu'): + if line.startswith("nu"): yields.append(True) else: yields.append(False) @@ -466,16 +530,16 @@ def _calculate_cexs_nuclide(this, types, temperature=294., sab_name=None, ops.append((np.add,) * (len(tmp_mts) - 1)) elif line in openmc.data.REACTION_MT: mt_number = openmc.data.REACTION_MT[line] - cv.check_type('MT in types', mt_number, Integral) - cv.check_greater_than('MT in types', mt_number, 0) + cv.check_type("MT in types", mt_number, Integral) + cv.check_greater_than("MT in types", mt_number, 0) tmp_mts = nuc.get_reaction_components(mt_number) mts.append(tmp_mts) ops.append((np.add,) * (len(tmp_mts) - 1)) yields.append(False) elif isinstance(line, int): # Not a built-in type, we have to parse it ourselves - cv.check_type('MT in types', line, Integral) - cv.check_greater_than('MT in types', line, 0) + cv.check_type("MT in types", line, Integral) + cv.check_greater_than("MT in types", line, 0) tmp_mts = nuc.get_reaction_components(line) mts.append(tmp_mts) ops.append((np.add,) * (len(tmp_mts) - 1)) @@ -494,21 +558,25 @@ def _calculate_cexs_nuclide(this, types, temperature=294., sab_name=None, # The S(a,b) and non-thermal data sab_sum = openmc.data.Sum(sab_funcs) pw_funcs = openmc.data.Regions1D( - [sab_sum, nuc[mt].xs[nucT]], - [sab_Emax]) + [sab_sum, nuc[mt].xs[nucT]], [sab_Emax] + ) funcs.append(pw_funcs) elif ncrystal_cfg: import NCrystal + nc_scatter = NCrystal.createScatter(ncrystal_cfg) nc_func = nc_scatter.crossSectionNonOriented - nc_emax = 5 # eV # this should be obtained from NCRYSTAL_MAX_ENERGY - energy_grid = np.union1d(np.geomspace(min(energy_grid), - 1.1*nc_emax, - 1000),energy_grid) # NCrystal does not have - # an intrinsic energy grid + nc_emax = ( + 5 # eV # this should be obtained from NCRYSTAL_MAX_ENERGY + ) + energy_grid = np.union1d( + np.geomspace(min(energy_grid), 1.1 * nc_emax, 1000), + energy_grid, + ) # NCrystal does not have + # an intrinsic energy grid pw_funcs = openmc.data.Regions1D( - [nc_func, nuc[mt].xs[nucT]], - [nc_emax]) + [nc_func, nuc[mt].xs[nucT]], [nc_emax] + ) funcs.append(pw_funcs) else: funcs.append(nuc[mt].xs[nucT]) @@ -516,13 +584,14 @@ def _calculate_cexs_nuclide(this, types, temperature=294., sab_name=None, if yields[i]: # Get the total yield first if available. This will be # used primarily for fission. - for prod in chain(nuc[mt].products, - nuc[mt].derived_products): - if prod.particle == 'neutron' and \ - prod.emission_mode == 'total': + for prod in chain(nuc[mt].products, nuc[mt].derived_products): + if ( + prod.particle == "neutron" + and prod.emission_mode == "total" + ): func = openmc.data.Combination( - [nuc[mt].xs[nucT], prod.yield_], - [np.multiply]) + [nuc[mt].xs[nucT], prod.yield_], [np.multiply] + ) funcs.append(func) break else: @@ -530,18 +599,25 @@ def _calculate_cexs_nuclide(this, types, temperature=294., sab_name=None, # prompt and delayed. This is used for scatter # multiplication. func = None - for prod in chain(nuc[mt].products, - nuc[mt].derived_products): - if prod.particle == 'neutron' and \ - prod.emission_mode != 'total': + for prod in chain( + nuc[mt].products, nuc[mt].derived_products + ): + if ( + prod.particle == "neutron" + and prod.emission_mode != "total" + ): if func: func = openmc.data.Combination( - [prod.yield_, func], [np.add]) + [prod.yield_, func], [np.add] + ) else: func = prod.yield_ if func: - funcs.append(openmc.data.Combination( - [func, nuc[mt].xs[nucT]], [np.multiply])) + funcs.append( + openmc.data.Combination( + [func, nuc[mt].xs[nucT]], [np.multiply] + ) + ) else: # If func is still None, then there were no # products. In that case, assume the yield is @@ -551,15 +627,15 @@ def _calculate_cexs_nuclide(this, types, temperature=294., sab_name=None, else: funcs.append(nuc[mt].xs[nucT]) elif mt == UNITY_MT: - funcs.append(lambda x: 1.) + funcs.append(lambda x: 1.0) elif mt == XI_MT: awr = nuc.atomic_weight_ratio - alpha = ((awr - 1.) / (awr + 1.))**2 - xi = 1. + alpha * np.log(alpha) / (1. - alpha) + alpha = ((awr - 1.0) / (awr + 1.0)) ** 2 + xi = 1.0 + alpha * np.log(alpha) / (1.0 - alpha) funcs.append(lambda x: xi) else: - funcs.append(lambda x: 0.) - funcs = funcs if funcs else [lambda x: 0.] + funcs.append(lambda x: 0.0) + funcs = funcs if funcs else [lambda x: 0.0] xs.append(openmc.data.Combination(funcs, op)) else: raise ValueError(this + " not in library") @@ -567,9 +643,9 @@ def _calculate_cexs_nuclide(this, types, temperature=294., sab_name=None, return energy_grid, xs -def _calculate_cexs_elem_mat(this, types, temperature=294., - cross_sections=None, sab_name=None, - enrichment=None): +def _calculate_cexs_elem_mat( + this, types, temperature=294.0, cross_sections=None, sab_name=None, enrichment=None +): """Calculates continuous-energy cross sections of a requested type. Parameters @@ -623,8 +699,9 @@ def _calculate_cexs_elem_mat(this, types, temperature=294., ncrystal_cfg = this.ncrystal_cfg else: # Expand elements in to nuclides with atomic densities - nuclides = openmc.Element(this).expand(1., 'ao', enrichment=enrichment, - cross_sections=cross_sections) + nuclides = openmc.Element(this).expand( + 1.0, "ao", enrichment=enrichment, cross_sections=cross_sections + ) # For ease of processing split out the nuclide and its fraction nuc_fractions = {nuclide[0]: nuclide[1] for nuclide in nuclides} # Create a dict of [nuclide name] = nuclide object to carry forward @@ -638,16 +715,19 @@ def _calculate_cexs_elem_mat(this, types, temperature=294., if isinstance(this, openmc.Material): for sab_name, _ in this._sab: sab = openmc.data.ThermalScattering.from_hdf5( - library.get_by_material(sab_name, data_type='thermal')['path']) + library.get_by_material(sab_name, data_type="thermal")["path"] + ) for nuc in sab.nuclides: - sabs[nuc] = library.get_by_material(sab_name, - data_type='thermal')['path'] + sabs[nuc] = library.get_by_material(sab_name, data_type="thermal")[ + "path" + ] else: if sab_name: sab = openmc.data.ThermalScattering.from_hdf5(sab_name) for nuc in sab.nuclides: - sabs[nuc] = library.get_by_material(sab_name, - data_type='thermal')['path'] + sabs[nuc] = library.get_by_material(sab_name, data_type="thermal")[ + "path" + ] # Now we can create the data sets to be plotted xs = {} @@ -656,14 +736,15 @@ def _calculate_cexs_elem_mat(this, types, temperature=294., name = nuclide[0] nuc = nuclide[1] sab_tab = sabs[name] - temp_E, temp_xs = calculate_cexs(nuc, types, T, sab_tab, cross_sections, - ncrystal_cfg=ncrystal_cfg - ) + temp_E, temp_xs = calculate_cexs( + nuc, types, T, sab_tab, cross_sections, ncrystal_cfg=ncrystal_cfg + ) E.append(temp_E) # Since the energy grids are different, store the cross sections as # a tabulated function so they can be calculated on any grid needed. - xs[name] = [openmc.data.Tabulated1D(temp_E, temp_xs[line]) - for line in range(len(types))] + xs[name] = [ + openmc.data.Tabulated1D(temp_E, temp_xs[line]) for line in range(len(types)) + ] # Condense the data for every nuclide # First create a union energy grid @@ -674,20 +755,25 @@ def _calculate_cexs_elem_mat(this, types, temperature=294., # Now we can combine all the nuclidic data data = np.zeros((len(types), len(energy_grid))) for line in range(len(types)): - if types[line] == 'unity': - data[line, :] = 1. + if types[line] == "unity": + data[line, :] = 1.0 else: for nuclide in nuclides.items(): name = nuclide[0] - data[line, :] += (nuc_fractions[name] * - xs[name][line](energy_grid)) + data[line, :] += nuc_fractions[name] * xs[name][line](energy_grid) return energy_grid, data -def calculate_mgxs(this, types, orders=None, temperature=294., - cross_sections=None, ce_cross_sections=None, - enrichment=None): +def calculate_mgxs( + this, + types, + orders=None, + temperature=294.0, + 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 @@ -728,21 +814,20 @@ def calculate_mgxs(this, types, orders=None, temperature=294., """ # Check types - cv.check_type('temperature', temperature, Real) + cv.check_type("temperature", temperature, Real) if enrichment: - cv.check_type('enrichment', enrichment, Real) - cv.check_iterable_type('types', types, str) + 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 this in ELEMENT_NAMES or isinstance(this, openmc.Material): - mgxs = _calculate_mgxs_elem_mat(this, types, library, orders, - temperature, ce_cross_sections, - enrichment) + mgxs = _calculate_mgxs_elem_mat( + this, types, library, orders, temperature, ce_cross_sections, enrichment + ) elif isinstance(this, str): - mgxs = _calculate_mgxs_nuc_macro(this, types, library, orders, - temperature) + mgxs = _calculate_mgxs_nuc_macro(this, types, library, orders, temperature) else: raise TypeError("Invalid type") @@ -750,21 +835,19 @@ def calculate_mgxs(this, types, orders=None, temperature=294., 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] + 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] + 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.): +def _calculate_mgxs_nuc_macro(this, types, library, orders=None, temperature=294.0): """Determines the multi-group cross sections of a nuclide or macroscopic object. @@ -800,8 +883,9 @@ def _calculate_mgxs_nuc_macro(this, types, library, orders=None, # 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)) + 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): @@ -819,10 +903,10 @@ def _calculate_mgxs_nuc_macro(this, types, library, orders=None, # 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: + if "fission" in line and not xsdata.fissionable: continue - elif line == 'unity': - data[i, :] = 1. + elif line == "unity": + data[i, :] = 1.0 else: # Now we have to get the cross section data and properly # treat it depending on the requested type. @@ -832,14 +916,13 @@ def _calculate_mgxs_nuc_macro(this, types, library, orders=None, # 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': + 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]"]): + 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 @@ -857,8 +940,10 @@ def _calculate_mgxs_nuc_macro(this, types, library, orders=None, 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]"]): + 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 @@ -900,9 +985,15 @@ def _calculate_mgxs_nuc_macro(this, types, library, orders=None, return data -def _calculate_mgxs_elem_mat(this, types, library, orders=None, - temperature=294., ce_cross_sections=None, - enrichment=None): +def _calculate_mgxs_elem_mat( + this, + types, + library, + orders=None, + temperature=294.0, + ce_cross_sections=None, + enrichment=None, +): """Determines the multi-group cross sections of an element or material object. @@ -962,22 +1053,24 @@ def _calculate_mgxs_elem_mat(this, types, library, orders=None, else: T = temperature # Expand elements in to nuclides with atomic densities - nuclides = openmc.Element(this).expand(100., 'ao', enrichment=enrichment, - cross_sections=ce_cross_sections) + nuclides = openmc.Element(this).expand( + 100.0, "ao", enrichment=enrichment, cross_sections=ce_cross_sections + ) # For ease of processing split out nuc and nuc_fractions nuc_fraction = [nuclide[1] for nuclide in nuclides] nuc_data = [] for nuclide in nuclides.items(): - nuc_data.append(_calculate_mgxs_nuc_macro(nuclide[0], types, library, - orders, T)) + nuc_data.append( + _calculate_mgxs_nuc_macro(nuclide[0], types, library, orders, T) + ) # Combine across the nuclides data = np.zeros((len(types), library.energy_groups.num_groups)) for line in range(len(types)): - if types[line] == 'unity': - data[line, :] = 1. + if types[line] == "unity": + data[line, :] = 1.0 else: for n in range(len(nuclides)): data[line, :] += nuc_fraction[n] * nuc_data[n][line, :] diff --git a/openmc/polynomial.py b/openmc/polynomial.py index 1259c61ca26..8aae84d47a7 100644 --- a/openmc/polynomial.py +++ b/openmc/polynomial.py @@ -25,13 +25,13 @@ def legendre_from_expcoef(coef, domain=(-1, 1)): """ n = np.arange(len(coef)) - c = (2*n + 1) * np.asarray(coef) / (domain[1] - domain[0]) + c = (2 * n + 1) * np.asarray(coef) / (domain[1] - domain[0]) return np.polynomial.Legendre(c, domain) class Polynomial: - """Abstract Polynomial Class for creating polynomials. - """ + """Abstract Polynomial Class for creating polynomials.""" + def __init__(self, coef): self.coef = np.asarray(coef) @@ -62,6 +62,7 @@ class ZernikeRadial(Polynomial): normalization. """ + def __init__(self, coef, radius=1): super().__init__(coef) self._order = 2 * (len(self.coef) - 1) @@ -75,11 +76,16 @@ def order(self): def __call__(self, r): import openmc.lib as lib + if isinstance(r, Iterable): - return [np.sum(self._norm_coef * lib.calc_zn_rad(self.order, r_i / self.radius)) - for r_i in r] + return [ + np.sum(self._norm_coef * lib.calc_zn_rad(self.order, r_i / self.radius)) + for r_i in r + ] else: - return np.sum(self._norm_coef * lib.calc_zn_rad(self.order, r / self.radius)) + return np.sum( + self._norm_coef * lib.calc_zn_rad(self.order, r / self.radius) + ) class Zernike(Polynomial): @@ -108,6 +114,7 @@ class Zernike(Polynomial): The list of coefficients of each term in the polynomials after normalization. """ + def __init__(self, coef, radius=1): super().__init__(coef) # Solve order from number of coefficients @@ -117,12 +124,12 @@ def __init__(self, coef, radius=1): norm_vec = np.ones(len(self.coef)) for n in range(self._order + 1): for m in range(-n, n + 1, 2): - j = int((n*(n + 2) + m)/2) + j = int((n * (n + 2) + m) / 2) if m == 0: norm_vec[j] = n + 1 else: - norm_vec[j] = 2*n + 2 - norm_vec /= (math.pi * radius**2) + norm_vec[j] = 2 * n + 2 + norm_vec /= math.pi * radius**2 self._norm_coef = norm_vec * self.coef @property @@ -131,14 +138,33 @@ def order(self): def __call__(self, r, theta=0.0): import openmc.lib as lib + if isinstance(r, Iterable) and isinstance(theta, Iterable): - return [[np.sum(self._norm_coef * lib.calc_zn(self.order, r_i / self.radius, theta_i)) - for r_i in r] for theta_i in theta] + return [ + [ + np.sum( + self._norm_coef + * lib.calc_zn(self.order, r_i / self.radius, theta_i) + ) + for r_i in r + ] + for theta_i in theta + ] elif isinstance(r, Iterable) and not isinstance(theta, Iterable): - return [np.sum(self._norm_coef * lib.calc_zn(self.order, r_i / self.radius, theta)) - for r_i in r] + return [ + np.sum( + self._norm_coef * lib.calc_zn(self.order, r_i / self.radius, theta) + ) + for r_i in r + ] elif not isinstance(r, Iterable) and isinstance(theta, Iterable): - return [np.sum(self._norm_coef * lib.calc_zn(self.order, r / self.radius, theta_i)) - for theta_i in theta] + return [ + np.sum( + self._norm_coef * lib.calc_zn(self.order, r / self.radius, theta_i) + ) + for theta_i in theta + ] else: - return np.sum(self._norm_coef * lib.calc_zn(self.order, r / self.radius, theta)) + return np.sum( + self._norm_coef * lib.calc_zn(self.order, r / self.radius, theta) + ) diff --git a/openmc/region.py b/openmc/region.py index e1cb834757a..6849ec23fa2 100644 --- a/openmc/region.py +++ b/openmc/region.py @@ -25,6 +25,7 @@ class Region(ABC): Axis-aligned bounding box of the region """ + def __and__(self, other): return Intersection((self, other)) @@ -115,7 +116,7 @@ def from_expression(expression, surfaces): i_start = -1 tokens = [] while i < len(expression): - if expression[i] in '()|~ ': + if expression[i] in "()|~ ": # If special character appears immediately after a non-operator, # create a token with the appropriate half-space if i_start >= 0: @@ -127,37 +128,39 @@ def from_expression(expression, surfaces): # When an opening parenthesis appears after a non-operator, # there's an implicit intersection operator between them - if expression[i] == '(': - tokens.append(' ') + if expression[i] == "(": + tokens.append(" ") - if expression[i] in '()|~': + if expression[i] in "()|~": # For everything other than intersection, add the operator # to the list of tokens tokens.append(expression[i]) # If two parentheses appear immediately adjacent to one # another, we need an intersection between them - if expression[i:i+2] == ')(': - tokens.append(' ') + if expression[i : i + 2] == ")(": + tokens.append(" ") else: # Find next non-space character - while expression[i+1] == ' ': + while expression[i + 1] == " ": i += 1 # If previous token is a halfspace or right parenthesis and # next token is not a left parenthesis or union operator, # that implies that the whitespace is to be interpreted as # an intersection operator - if (i_start >= 0 or tokens[-1] == ')') and \ - expression[i+1] not in ')|': - tokens.append(' ') + if (i_start >= 0 or tokens[-1] == ")") and expression[ + i + 1 + ] not in ")|": + tokens.append(" ") i_start = -1 else: # Check for invalid characters - if expression[i] not in '-+0123456789': - raise SyntaxError(f"Invalid character '{expression[i]}' in " - "expression") + if expression[i] not in "-+0123456789": + raise SyntaxError( + f"Invalid character '{expression[i]}' in " "expression" + ) # If we haven't yet reached the start of a word, start one if i_start < 0: @@ -176,11 +179,11 @@ def from_expression(expression, surfaces): # The functions below are used to apply an operator to operands on the # output queue during the shunting yard algorithm. def can_be_combined(region): - return isinstance(region, Complement) or hasattr(region, 'surface') + return isinstance(region, Complement) or hasattr(region, "surface") def apply_operator(output, operator): r2 = output.pop() - if operator == ' ': + if operator == " ": r1 = output.pop() if isinstance(r1, Intersection): r1 &= r2 @@ -190,7 +193,7 @@ def apply_operator(output, operator): output.append(r2) else: output.append(r1 & r2) - elif operator == '|': + elif operator == "|": r1 = output.pop() if isinstance(r1, Union): r1 |= r2 @@ -200,47 +203,52 @@ def apply_operator(output, operator): output.append(r2) else: output.append(r1 | r2) - elif operator == '~': + elif operator == "~": output.append(~r2) # The following is an implementation of the shunting yard algorithm to # generate an abstract syntax tree for the region expression. output = [] stack = [] - precedence = {'|': 1, ' ': 2, '~': 3} - associativity = {'|': 'left', ' ': 'left', '~': 'right'} + precedence = {"|": 1, " ": 2, "~": 3} + associativity = {"|": "left", " ": "left", "~": "right"} for token in tokens: - if token in (' ', '|', '~'): + if token in (" ", "|", "~"): # Normal operators while stack: op = stack[-1] - if (op not in ('(', ')') and - ((associativity[token] == 'right' and - precedence[token] < precedence[op]) or - (associativity[token] == 'left' and - precedence[token] <= precedence[op]))): + if op not in ("(", ")") and ( + ( + associativity[token] == "right" + and precedence[token] < precedence[op] + ) + or ( + associativity[token] == "left" + and precedence[token] <= precedence[op] + ) + ): apply_operator(output, stack.pop()) else: break stack.append(token) - elif token == '(': + elif token == "(": # Left parentheses stack.append(token) - elif token == ')': + elif token == ")": # Right parentheses - while stack[-1] != '(': + while stack[-1] != "(": apply_operator(output, stack.pop()) if len(stack) == 0: - raise SyntaxError('Mismatched parentheses in ' - 'region specification.') + raise SyntaxError( + "Mismatched parentheses in " "region specification." + ) stack.pop() else: # Surface halfspaces output.append(token) while stack: - if stack[-1] in '()': - raise SyntaxError('Mismatched parentheses in region ' - 'specification.') + if stack[-1] in "()": + raise SyntaxError("Mismatched parentheses in region " "specification.") apply_operator(output, stack.pop()) # Since we are generating an abstract syntax tree rather than a reverse @@ -299,8 +307,9 @@ def translate(self, vector, inplace=False, memo=None): memo = {} return type(self)(n.translate(vector, inplace, memo) for n in self) - def rotate(self, rotation, pivot=(0., 0., 0.), order='xyz', inplace=False, - memo=None): + def rotate( + self, rotation, pivot=(0.0, 0.0, 0.0), order="xyz", inplace=False, memo=None + ): r"""Rotate surface by angles provided or by applying matrix directly. .. versionadded:: 0.12 @@ -340,8 +349,10 @@ def rotate(self, rotation, pivot=(0., 0., 0.), order='xyz', inplace=False, """ if memo is None: memo = {} - return type(self)(n.rotate(rotation, pivot=pivot, order=order, - inplace=inplace, memo=memo) for n in self) + return type(self)( + n.rotate(rotation, pivot=pivot, order=order, inplace=inplace, memo=memo) + for n in self + ) def plot(self, *args, **kwargs): """Display a slice plot of the region. @@ -385,9 +396,11 @@ def plot(self, *args, **kwargs): Axes containing resulting image """ - for key in ('color_by', 'colors', 'legend', 'legend_kwargs'): + for key in ("color_by", "colors", "legend", "legend_kwargs"): if key in kwargs: - warnings.warn(f"The '{key}' argument is present but won't be applied in a region plot") + warnings.warn( + f"The '{key}' argument is present but won't be applied in a region plot" + ) # Create cell while not perturbing use of autogenerated IDs next_id = openmc.Cell.next_id @@ -430,7 +443,7 @@ def __init__(self, nodes): self._nodes = list(nodes) for node in nodes: if not isinstance(node, Region): - raise ValueError('Intersection operands must be of type Region') + raise ValueError("Intersection operands must be of type Region") def __and__(self, other): new = Intersection(self) @@ -480,7 +493,7 @@ def __contains__(self, point): return all(point in n for n in self) def __str__(self): - return '(' + ' '.join(map(str, self)) + ')' + return "(" + " ".join(map(str, self)) + ")" @property def bounding_box(self) -> BoundingBox: @@ -521,7 +534,7 @@ def __init__(self, nodes): self._nodes = list(nodes) for node in nodes: if not isinstance(node, Region): - raise ValueError('Union operands must be of type Region') + raise ValueError("Union operands must be of type Region") def __or__(self, other): new = Union(self) @@ -571,12 +584,11 @@ def __contains__(self, point): return any(point in n for n in self) def __str__(self): - return '(' + ' | '.join(map(str, self)) + ')' + return "(" + " | ".join(map(str, self)) + ")" @property def bounding_box(self) -> BoundingBox: - bbox = BoundingBox(np.array([np.inf]*3), - np.array([-np.inf]*3)) + bbox = BoundingBox(np.array([np.inf] * 3), np.array([-np.inf] * 3)) for n in self: bbox |= n.bounding_box return bbox @@ -634,7 +646,7 @@ def __invert__(self) -> Region: return self.node def __str__(self): - return '~' + str(self.node) + return "~" + str(self.node) @property def node(self): @@ -643,7 +655,7 @@ def node(self): @node.setter def node(self, node): if not isinstance(node, Region): - raise ValueError('Complement operand must be of type Region') + raise ValueError("Complement operand must be of type Region") self._node = node @property @@ -698,9 +710,13 @@ def translate(self, vector, inplace=False, memo=None): memo = {} return type(self)(self.node.translate(vector, inplace, memo)) - def rotate(self, rotation, pivot=(0., 0., 0.), order='xyz', inplace=False, - memo=None): + def rotate( + self, rotation, pivot=(0.0, 0.0, 0.0), order="xyz", inplace=False, memo=None + ): if memo is None: memo = {} - return type(self)(self.node.rotate(rotation, pivot=pivot, order=order, - inplace=inplace, memo=memo)) + return type(self)( + self.node.rotate( + rotation, pivot=pivot, order=order, inplace=inplace, memo=memo + ) + ) diff --git a/openmc/search.py b/openmc/search.py index 70ce011b634..76e739f5ddb 100644 --- a/openmc/search.py +++ b/openmc/search.py @@ -8,11 +8,19 @@ import openmc.checkvalue as cv -_SCALAR_BRACKETED_METHODS = {'brentq', 'brenth', 'ridder', 'bisect'} - - -def _search_keff(guess, target, model_builder, model_args, print_iterations, - run_args, guesses, results): +_SCALAR_BRACKETED_METHODS = {"brentq", "brenth", "ridder", "bisect"} + + +def _search_keff( + guess, + target, + model_builder, + model_args, + print_iterations, + run_args, + guesses, + results, +): """Function which will actually create our model, run the calculation, and obtain the result. This function will be passed to the root finding algorithm @@ -60,17 +68,26 @@ def _search_keff(guess, target, model_builder, model_args, print_iterations, results.append(keff) if print_iterations: - text = 'Iteration: {}; Guess of {:.2e} produced a keff of ' + \ - '{:1.5f} +/- {:1.5f}' + text = ( + "Iteration: {}; Guess of {:.2e} produced a keff of " + "{:1.5f} +/- {:1.5f}" + ) print(text.format(len(guesses), guess, keff.n, keff.s)) return keff.n - target -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, - run_args=None, **kwargs): +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, + run_args=None, + **kwargs, +): """Function to perform a keff search by modifying a model parametrized by a single independent variable. @@ -125,25 +142,24 @@ def search_for_keff(model_builder, initial_guess=None, target=1.0, """ if initial_guess is not None: - cv.check_type('initial_guess', initial_guess, Real) + 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]) + 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("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) if run_args is None: run_args = {} else: - cv.check_type('run_args', run_args, dict) - cv.check_type('model_builder', model_builder, Callable) + cv.check_type("run_args", run_args, dict) + cv.check_type("model_builder", model_builder, Callable) # Run the model builder function once to make sure it provides the correct # output type @@ -151,7 +167,7 @@ def search_for_keff(model_builder, initial_guess=None, target=1.0, 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) + cv.check_type("model_builder return", model, openmc.model.Model) # Set the iteration data storage variables guesses = [] @@ -163,37 +179,45 @@ def search_for_keff(model_builder, initial_guess=None, target=1.0, if bracket is not None: # Generate our arguments - args = {'f': search_function, 'a': bracket[0], 'b': bracket[1]} + args = {"f": search_function, "a": bracket[0], "b": bracket[1]} if tol is not None: - args['rtol'] = tol + args["rtol"] = tol # Set the root finding method - if bracketed_method == 'brentq': + if bracketed_method == "brentq": root_finder = sopt.brentq - elif bracketed_method == 'brenth': + elif bracketed_method == "brenth": root_finder = sopt.brenth - elif bracketed_method == 'ridder': + elif bracketed_method == "ridder": root_finder = sopt.ridder - elif bracketed_method == 'bisect': + elif bracketed_method == "bisect": root_finder = sopt.bisect elif initial_guess is not None: # Generate our arguments - args = {'func': search_function, 'x0': initial_guess} + args = {"func": search_function, "x0": initial_guess} if tol is not None: - args['tol'] = tol + 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") + 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, - run_args, guesses, results) + args["args"] = ( + target, + model_builder, + model_args, + print_iterations, + run_args, + guesses, + results, + ) # Create a new dictionary with the arguments from args and kwargs args.update(kwargs) diff --git a/openmc/settings.py b/openmc/settings.py index 77598b204fc..903a22ac4e7 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -19,14 +19,14 @@ class RunMode(Enum): - EIGENVALUE = 'eigenvalue' - FIXED_SOURCE = 'fixed source' - PLOT = 'plot' - VOLUME = 'volume' - PARTICLE_RESTART = 'particle restart' + EIGENVALUE = "eigenvalue" + FIXED_SOURCE = "fixed source" + PLOT = "plot" + VOLUME = "volume" + PARTICLE_RESTART = "particle restart" -_RES_SCAT_METHODS = {'dbrc', 'rvs'} +_RES_SCAT_METHODS = {"dbrc", "rvs"} class Settings: @@ -324,7 +324,7 @@ def __init__(self, **kwargs): self._max_order = None # Source subelement - self._source = cv.CheckedList(SourceBase, 'source distributions') + self._source = cv.CheckedList(SourceBase, "source distributions") self._confidence_intervals = None self._electron_treatment = None @@ -371,7 +371,8 @@ def __init__(self, **kwargs): self._resonance_scattering = {} self._volume_calculations = cv.CheckedList( - VolumeCalculation, 'volume calculations') + VolumeCalculation, "volume calculations" + ) self._create_fission_neutrons = None self._create_delayed_neutrons = None @@ -383,8 +384,10 @@ def __init__(self, **kwargs): self._max_particles_in_flight = None self._max_particle_events = None self._write_initial_source = None - self._weight_windows = cv.CheckedList(WeightWindows, 'weight windows') - self._weight_window_generators = cv.CheckedList(WeightWindowGenerator, 'weight window generators') + self._weight_windows = cv.CheckedList(WeightWindows, "weight windows") + self._weight_window_generators = cv.CheckedList( + WeightWindowGenerator, "weight window generators" + ) self._weight_windows_on = None self._weight_windows_file = None self._weight_window_checkpoints = {} @@ -402,7 +405,7 @@ def run_mode(self) -> str: @run_mode.setter def run_mode(self, run_mode: str): - cv.check_value('run mode', run_mode, {x.value for x in RunMode}) + cv.check_value("run mode", run_mode, {x.value for x in RunMode}) for mode in RunMode: if mode.value == run_mode: self._run_mode = mode @@ -413,8 +416,8 @@ def batches(self) -> int: @batches.setter def batches(self, batches: int): - cv.check_type('batches', batches, Integral) - cv.check_greater_than('batches', batches, 0) + cv.check_type("batches", batches, Integral) + cv.check_greater_than("batches", batches, 0) self._batches = batches @property @@ -423,8 +426,8 @@ def generations_per_batch(self) -> int: @generations_per_batch.setter def generations_per_batch(self, generations_per_batch: int): - cv.check_type('generations per patch', generations_per_batch, Integral) - cv.check_greater_than('generations per batch', generations_per_batch, 0) + cv.check_type("generations per patch", generations_per_batch, Integral) + cv.check_greater_than("generations per batch", generations_per_batch, 0) self._generations_per_batch = generations_per_batch @property @@ -433,8 +436,8 @@ def inactive(self) -> int: @inactive.setter def inactive(self, inactive: int): - cv.check_type('inactive batches', inactive, Integral) - cv.check_greater_than('inactive batches', inactive, 0, True) + cv.check_type("inactive batches", inactive, Integral) + cv.check_greater_than("inactive batches", inactive, 0, True) self._inactive = inactive @property @@ -443,8 +446,8 @@ def max_lost_particles(self) -> int: @max_lost_particles.setter def max_lost_particles(self, max_lost_particles: int): - cv.check_type('max_lost_particles', max_lost_particles, Integral) - cv.check_greater_than('max_lost_particles', max_lost_particles, 0) + cv.check_type("max_lost_particles", max_lost_particles, Integral) + cv.check_greater_than("max_lost_particles", max_lost_particles, 0) self._max_lost_particles = max_lost_particles @property @@ -453,9 +456,9 @@ def rel_max_lost_particles(self) -> float: @rel_max_lost_particles.setter def rel_max_lost_particles(self, rel_max_lost_particles: float): - cv.check_type('rel_max_lost_particles', rel_max_lost_particles, Real) - cv.check_greater_than('rel_max_lost_particles', rel_max_lost_particles, 0) - cv.check_less_than('rel_max_lost_particles', rel_max_lost_particles, 1) + cv.check_type("rel_max_lost_particles", rel_max_lost_particles, Real) + cv.check_greater_than("rel_max_lost_particles", rel_max_lost_particles, 0) + cv.check_less_than("rel_max_lost_particles", rel_max_lost_particles, 1) self._rel_max_lost_particles = rel_max_lost_particles @property @@ -464,8 +467,8 @@ def max_write_lost_particles(self) -> int: @max_write_lost_particles.setter def max_write_lost_particles(self, max_write_lost_particles: int): - cv.check_type('max_write_lost_particles', max_write_lost_particles, Integral) - cv.check_greater_than('max_write_lost_particles', max_write_lost_particles, 0) + cv.check_type("max_write_lost_particles", max_write_lost_particles, Integral) + cv.check_greater_than("max_write_lost_particles", max_write_lost_particles, 0) self._max_write_lost_particles = max_write_lost_particles @property @@ -474,8 +477,8 @@ def particles(self) -> int: @particles.setter def particles(self, particles: int): - cv.check_type('particles', particles, Integral) - cv.check_greater_than('particles', particles, 0) + cv.check_type("particles", particles, Integral) + cv.check_greater_than("particles", particles, 0) self._particles = particles @property @@ -485,28 +488,36 @@ def keff_trigger(self) -> dict: @keff_trigger.setter def keff_trigger(self, keff_trigger: dict): if not isinstance(keff_trigger, dict): - msg = f'Unable to set a trigger on keff from "{keff_trigger}" ' \ - 'which is not a Python dictionary' + msg = ( + f'Unable to set a trigger on keff from "{keff_trigger}" ' + "which is not a Python dictionary" + ) raise ValueError(msg) - elif 'type' not in keff_trigger: - msg = f'Unable to set a trigger on keff from "{keff_trigger}" ' \ - 'which does not have a "type" key' + elif "type" not in keff_trigger: + msg = ( + f'Unable to set a trigger on keff from "{keff_trigger}" ' + 'which does not have a "type" key' + ) raise ValueError(msg) - elif keff_trigger['type'] not in ['variance', 'std_dev', 'rel_err']: - msg = 'Unable to set a trigger on keff with ' \ - 'type "{0}"'.format(keff_trigger['type']) + elif keff_trigger["type"] not in ["variance", "std_dev", "rel_err"]: + msg = "Unable to set a trigger on keff with " 'type "{0}"'.format( + keff_trigger["type"] + ) raise ValueError(msg) - elif 'threshold' not in keff_trigger: - msg = f'Unable to set a trigger on keff from "{keff_trigger}" ' \ - 'which does not have a "threshold" key' + elif "threshold" not in keff_trigger: + msg = ( + f'Unable to set a trigger on keff from "{keff_trigger}" ' + 'which does not have a "threshold" key' + ) raise ValueError(msg) - elif not isinstance(keff_trigger['threshold'], Real): - msg = 'Unable to set a trigger on keff with ' \ - 'threshold "{0}"'.format(keff_trigger['threshold']) + elif not isinstance(keff_trigger["threshold"], Real): + msg = "Unable to set a trigger on keff with " 'threshold "{0}"'.format( + keff_trigger["threshold"] + ) raise ValueError(msg) self._keff_trigger = keff_trigger @@ -517,8 +528,7 @@ def energy_mode(self) -> str: @energy_mode.setter def energy_mode(self, energy_mode: str): - cv.check_value('energy mode', energy_mode, - ['continuous-energy', 'multi-group']) + cv.check_value("energy mode", energy_mode, ["continuous-energy", "multi-group"]) self._energy_mode = energy_mode @property @@ -528,9 +538,8 @@ def max_order(self) -> int: @max_order.setter def max_order(self, max_order: int | None): if max_order is not None: - cv.check_type('maximum scattering order', max_order, Integral) - cv.check_greater_than('maximum scattering order', max_order, 0, - True) + cv.check_type("maximum scattering order", max_order, Integral) + cv.check_greater_than("maximum scattering order", max_order, 0, True) self._max_order = max_order @property @@ -541,7 +550,7 @@ def source(self) -> list[SourceBase]: def source(self, source: SourceBase | Iterable[SourceBase]): if not isinstance(source, MutableSequence): source = [source] - self._source = cv.CheckedList(SourceBase, 'source distributions', source) + self._source = cv.CheckedList(SourceBase, "source distributions", source) @property def confidence_intervals(self) -> bool: @@ -549,7 +558,7 @@ def confidence_intervals(self) -> bool: @confidence_intervals.setter def confidence_intervals(self, confidence_intervals: bool): - cv.check_type('confidence interval', confidence_intervals, bool) + cv.check_type("confidence interval", confidence_intervals, bool) self._confidence_intervals = confidence_intervals @property @@ -558,7 +567,7 @@ def electron_treatment(self) -> str: @electron_treatment.setter def electron_treatment(self, electron_treatment: str): - cv.check_value('electron treatment', electron_treatment, ['led', 'ttb']) + cv.check_value("electron treatment", electron_treatment, ["led", "ttb"]) self._electron_treatment = electron_treatment @property @@ -567,7 +576,7 @@ def ptables(self) -> bool: @ptables.setter def ptables(self, ptables: bool): - cv.check_type('probability tables', ptables, bool) + cv.check_type("probability tables", ptables, bool) self._ptables = ptables @property @@ -576,7 +585,7 @@ def photon_transport(self) -> bool: @photon_transport.setter def photon_transport(self, photon_transport: bool): - cv.check_type('photon transport', photon_transport, bool) + cv.check_type("photon transport", photon_transport, bool) self._photon_transport = photon_transport @property @@ -585,7 +594,7 @@ def uniform_source_sampling(self) -> bool: @uniform_source_sampling.setter def uniform_source_sampling(self, uniform_source_sampling: bool): - cv.check_type('strength as weights', uniform_source_sampling, bool) + cv.check_type("strength as weights", uniform_source_sampling, bool) self._uniform_source_sampling = uniform_source_sampling @property @@ -594,8 +603,8 @@ def plot_seed(self): @plot_seed.setter def plot_seed(self, seed): - cv.check_type('random plot color seed', seed, Integral) - cv.check_greater_than('random plot color seed', seed, 0) + cv.check_type("random plot color seed", seed, Integral) + cv.check_greater_than("random plot color seed", seed, 0) self._plot_seed = seed @property @@ -604,8 +613,8 @@ def seed(self) -> int: @seed.setter def seed(self, seed: int): - cv.check_type('random number generator seed', seed, Integral) - cv.check_greater_than('random number generator seed', seed, 0) + cv.check_type("random number generator seed", seed, Integral) + cv.check_greater_than("random number generator seed", seed, 0) self._seed = seed @property @@ -614,7 +623,7 @@ def survival_biasing(self) -> bool: @survival_biasing.setter def survival_biasing(self, survival_biasing: bool): - cv.check_type('survival biasing', survival_biasing, bool) + cv.check_type("survival biasing", survival_biasing, bool) self._survival_biasing = survival_biasing @property @@ -623,7 +632,7 @@ def entropy_mesh(self) -> RegularMesh: @entropy_mesh.setter def entropy_mesh(self, entropy: RegularMesh): - cv.check_type('entropy mesh', entropy, RegularMesh) + cv.check_type("entropy mesh", entropy, RegularMesh) self._entropy_mesh = entropy @property @@ -632,7 +641,7 @@ def trigger_active(self) -> bool: @trigger_active.setter def trigger_active(self, trigger_active: bool): - cv.check_type('trigger active', trigger_active, bool) + cv.check_type("trigger active", trigger_active, bool) self._trigger_active = trigger_active @property @@ -641,8 +650,8 @@ def trigger_max_batches(self) -> int: @trigger_max_batches.setter def trigger_max_batches(self, trigger_max_batches: int): - cv.check_type('trigger maximum batches', trigger_max_batches, Integral) - cv.check_greater_than('trigger maximum batches', trigger_max_batches, 0) + cv.check_type("trigger maximum batches", trigger_max_batches, Integral) + cv.check_greater_than("trigger maximum batches", trigger_max_batches, 0) self._trigger_max_batches = trigger_max_batches @property @@ -651,8 +660,8 @@ def trigger_batch_interval(self) -> int: @trigger_batch_interval.setter def trigger_batch_interval(self, trigger_batch_interval: int): - cv.check_type('trigger batch interval', trigger_batch_interval, Integral) - cv.check_greater_than('trigger batch interval', trigger_batch_interval, 0) + cv.check_type("trigger batch interval", trigger_batch_interval, Integral) + cv.check_greater_than("trigger batch interval", trigger_batch_interval, 0) self._trigger_batch_interval = trigger_batch_interval @property @@ -661,10 +670,10 @@ def output(self) -> dict: @output.setter def output(self, output: dict): - cv.check_type('output', output, Mapping) + cv.check_type("output", output, Mapping) for key, value in output.items(): - cv.check_value('output key', key, ('summary', 'tallies', 'path')) - if key in ('summary', 'tallies'): + cv.check_value("output key", key, ("summary", "tallies", "path")) + if key in ("summary", "tallies"): cv.check_type(f"output['{key}']", value, bool) else: cv.check_type("output['path']", value, str) @@ -676,23 +685,25 @@ def sourcepoint(self) -> dict: @sourcepoint.setter def sourcepoint(self, sourcepoint: dict): - cv.check_type('sourcepoint options', sourcepoint, Mapping) + cv.check_type("sourcepoint options", sourcepoint, Mapping) for key, value in sourcepoint.items(): - if key == 'batches': - cv.check_type('sourcepoint batches', value, Iterable, Integral) + if key == "batches": + cv.check_type("sourcepoint batches", value, Iterable, Integral) for batch in value: - cv.check_greater_than('sourcepoint batch', batch, 0) - elif key == 'separate': - cv.check_type('sourcepoint separate', value, bool) - elif key == 'write': - cv.check_type('sourcepoint write', value, bool) - elif key == 'overwrite': - cv.check_type('sourcepoint overwrite', value, bool) - elif key == 'mcpl': - cv.check_type('sourcepoint mcpl', value, bool) + cv.check_greater_than("sourcepoint batch", batch, 0) + elif key == "separate": + cv.check_type("sourcepoint separate", value, bool) + elif key == "write": + cv.check_type("sourcepoint write", value, bool) + elif key == "overwrite": + cv.check_type("sourcepoint overwrite", value, bool) + elif key == "mcpl": + cv.check_type("sourcepoint mcpl", value, bool) else: - raise ValueError(f"Unknown key '{key}' encountered when " - "setting sourcepoint options.") + raise ValueError( + f"Unknown key '{key}' encountered when " + "setting sourcepoint options." + ) self._sourcepoint = sourcepoint @property @@ -701,15 +712,17 @@ def statepoint(self) -> dict: @statepoint.setter def statepoint(self, statepoint: dict): - cv.check_type('statepoint options', statepoint, Mapping) + cv.check_type("statepoint options", statepoint, Mapping) for key, value in statepoint.items(): - if key == 'batches': - cv.check_type('statepoint batches', value, Iterable, Integral) + if key == "batches": + cv.check_type("statepoint batches", value, Iterable, Integral) for batch in value: - cv.check_greater_than('statepoint batch', batch, 0) + cv.check_greater_than("statepoint batch", batch, 0) else: - raise ValueError(f"Unknown key '{key}' encountered when " - "setting statepoint options.") + raise ValueError( + f"Unknown key '{key}' encountered when " + "setting statepoint options." + ) self._statepoint = statepoint @property @@ -718,17 +731,16 @@ def surf_source_read(self) -> dict: @surf_source_read.setter def surf_source_read(self, ssr: dict): - cv.check_type('surface source reading options', ssr, Mapping) + cv.check_type("surface source reading options", ssr, Mapping) for key, value in ssr.items(): - cv.check_value('surface source reading key', key, - ('path')) - if key == 'path': - cv.check_type('path to surface source file', value, PathLike) + cv.check_value("surface source reading key", key, ("path")) + if key == "path": + cv.check_type("path to surface source file", value, PathLike) self._surf_source_read = dict(ssr) # Resolve path to surface source file - if 'path' in ssr: - self._surf_source_read['path'] = input_path(ssr['path']) + if "path" in ssr: + self._surf_source_read["path"] = input_path(ssr["path"]) @property def surf_source_write(self) -> dict: @@ -741,7 +753,15 @@ def surf_source_write(self, surf_source_write: dict): cv.check_value( "surface source writing key", key, - ("surface_ids", "max_particles", "max_source_files", "mcpl", "cell", "cellfrom", "cellto"), + ( + "surface_ids", + "max_particles", + "max_source_files", + "mcpl", + "cell", + "cellfrom", + "cellto", + ), ) if key == "surface_ids": cv.check_type( @@ -752,7 +772,13 @@ def surf_source_write(self, surf_source_write: dict): elif key == "mcpl": cv.check_type("write to an MCPL-format file", value, bool) - elif key in ("max_particles", "max_source_files", "cell", "cellfrom", "cellto"): + elif key in ( + "max_particles", + "max_source_files", + "cell", + "cellfrom", + "cellto", + ): name = { "max_particles": "maximum particle banks on surfaces per process", "max_source_files": "maximun surface source files to be written", @@ -771,7 +797,7 @@ def no_reduce(self) -> bool: @no_reduce.setter def no_reduce(self, no_reduce: bool): - cv.check_type('no reduction option', no_reduce, bool) + cv.check_type("no reduction option", no_reduce, bool) self._no_reduce = no_reduce @property @@ -780,9 +806,9 @@ def verbosity(self) -> int: @verbosity.setter def verbosity(self, verbosity: int): - cv.check_type('verbosity', verbosity, Integral) - cv.check_greater_than('verbosity', verbosity, 1, True) - cv.check_less_than('verbosity', verbosity, 10, True) + cv.check_type("verbosity", verbosity, Integral) + cv.check_greater_than("verbosity", verbosity, 1, True) + cv.check_less_than("verbosity", verbosity, 10, True) self._verbosity = verbosity @property @@ -791,15 +817,14 @@ def tabular_legendre(self) -> dict: @tabular_legendre.setter def tabular_legendre(self, tabular_legendre: dict): - cv.check_type('tabular_legendre settings', tabular_legendre, Mapping) + cv.check_type("tabular_legendre settings", tabular_legendre, Mapping) for key, value in tabular_legendre.items(): - cv.check_value('tabular_legendre key', key, - ['enable', 'num_points']) - if key == 'enable': - cv.check_type('enable tabular_legendre', value, bool) - elif key == 'num_points': - cv.check_type('num_points tabular_legendre', value, Integral) - cv.check_greater_than('num_points tabular_legendre', value, 0) + cv.check_value("tabular_legendre key", key, ["enable", "num_points"]) + if key == "enable": + cv.check_type("enable tabular_legendre", value, bool) + elif key == "num_points": + cv.check_type("num_points tabular_legendre", value, Integral) + cv.check_greater_than("num_points tabular_legendre", value, 0) self._tabular_legendre = tabular_legendre @property @@ -809,24 +834,27 @@ def temperature(self) -> dict: @temperature.setter def temperature(self, temperature: dict): - cv.check_type('temperature settings', temperature, Mapping) + cv.check_type("temperature settings", temperature, Mapping) for key, value in temperature.items(): - cv.check_value('temperature key', key, - ['default', 'method', 'tolerance', 'multipole', - 'range']) - if key == 'default': - cv.check_type('default temperature', value, Real) - elif key == 'method': - cv.check_value('temperature method', value, - ['nearest', 'interpolation']) - elif key == 'tolerance': - cv.check_type('temperature tolerance', value, Real) - elif key == 'multipole': - cv.check_type('temperature multipole', value, bool) - elif key == 'range': - cv.check_length('temperature range', value, 2) + cv.check_value( + "temperature key", + key, + ["default", "method", "tolerance", "multipole", "range"], + ) + if key == "default": + cv.check_type("default temperature", value, Real) + elif key == "method": + cv.check_value( + "temperature method", value, ["nearest", "interpolation"] + ) + elif key == "tolerance": + cv.check_type("temperature tolerance", value, Real) + elif key == "multipole": + cv.check_type("temperature multipole", value, bool) + elif key == "range": + cv.check_length("temperature range", value, 2) for T in value: - cv.check_type('temperature', T, Real) + cv.check_type("temperature", T, Real) self._temperature = temperature @@ -836,11 +864,11 @@ def trace(self) -> Iterable: @trace.setter def trace(self, trace: Iterable): - cv.check_type('trace', trace, Iterable, Integral) - cv.check_length('trace', trace, 3) - cv.check_greater_than('trace batch', trace[0], 0) - cv.check_greater_than('trace generation', trace[1], 0) - cv.check_greater_than('trace particle', trace[2], 0) + cv.check_type("trace", trace, Iterable, Integral) + cv.check_length("trace", trace, 3) + cv.check_greater_than("trace batch", trace[0], 0) + cv.check_greater_than("trace generation", trace[1], 0) + cv.check_greater_than("trace particle", trace[2], 0) self._trace = trace @property @@ -849,17 +877,17 @@ def track(self) -> Iterable[Iterable[int]]: @track.setter def track(self, track: Iterable[Iterable[int]]): - cv.check_type('track', track, Sequence) + cv.check_type("track", track, Sequence) for t in track: if len(t) != 3: msg = f'Unable to set the track to "{t}" since its length is not 3' raise ValueError(msg) - cv.check_greater_than('track batch', t[0], 0) - cv.check_greater_than('track generation', t[1], 0) - cv.check_greater_than('track particle', t[2], 0) - cv.check_type('track batch', t[0], Integral) - cv.check_type('track generation', t[1], Integral) - cv.check_type('track particle', t[2], Integral) + cv.check_greater_than("track batch", t[0], 0) + cv.check_greater_than("track generation", t[1], 0) + cv.check_greater_than("track particle", t[2], 0) + cv.check_type("track batch", t[0], Integral) + cv.check_type("track generation", t[1], Integral) + cv.check_type("track particle", t[2], Integral) self._track = track @property @@ -869,24 +897,30 @@ def cutoff(self) -> dict: @cutoff.setter def cutoff(self, cutoff: dict): if not isinstance(cutoff, Mapping): - msg = f'Unable to set cutoff from "{cutoff}" which is not a '\ - 'Python dictionary' + msg = ( + f'Unable to set cutoff from "{cutoff}" which is not a ' + "Python dictionary" + ) raise ValueError(msg) for key in cutoff: - if key == 'weight': - cv.check_type('weight cutoff', cutoff[key], Real) - cv.check_greater_than('weight cutoff', cutoff[key], 0.0) - elif key == 'weight_avg': - cv.check_type('average survival weight', cutoff[key], Real) - cv.check_greater_than('average survival weight', - cutoff[key], 0.0) - elif key in ['energy_neutron', 'energy_photon', 'energy_electron', - 'energy_positron']: - cv.check_type('energy cutoff', cutoff[key], Real) - cv.check_greater_than('energy cutoff', cutoff[key], 0.0) + if key == "weight": + cv.check_type("weight cutoff", cutoff[key], Real) + cv.check_greater_than("weight cutoff", cutoff[key], 0.0) + elif key == "weight_avg": + cv.check_type("average survival weight", cutoff[key], Real) + cv.check_greater_than("average survival weight", cutoff[key], 0.0) + elif key in [ + "energy_neutron", + "energy_photon", + "energy_electron", + "energy_positron", + ]: + cv.check_type("energy cutoff", cutoff[key], Real) + cv.check_greater_than("energy cutoff", cutoff[key], 0.0) else: - msg = f'Unable to set cutoff to "{key}" which is unsupported ' \ - 'by OpenMC' + msg = ( + f'Unable to set cutoff to "{key}" which is unsupported ' "by OpenMC" + ) self._cutoff = cutoff @@ -896,10 +930,10 @@ def ufs_mesh(self) -> RegularMesh: @ufs_mesh.setter def ufs_mesh(self, ufs_mesh: RegularMesh): - cv.check_type('UFS mesh', ufs_mesh, RegularMesh) - cv.check_length('UFS mesh dimension', ufs_mesh.dimension, 3) - cv.check_length('UFS mesh lower-left corner', ufs_mesh.lower_left, 3) - cv.check_length('UFS mesh upper-right corner', ufs_mesh.upper_right, 3) + cv.check_type("UFS mesh", ufs_mesh, RegularMesh) + cv.check_length("UFS mesh dimension", ufs_mesh.dimension, 3) + cv.check_length("UFS mesh lower-left corner", ufs_mesh.lower_left, 3) + cv.check_length("UFS mesh upper-right corner", ufs_mesh.upper_right, 3) self._ufs_mesh = ufs_mesh @property @@ -908,26 +942,24 @@ def resonance_scattering(self) -> dict: @resonance_scattering.setter def resonance_scattering(self, res: dict): - cv.check_type('resonance scattering settings', res, Mapping) - keys = ('enable', 'method', 'energy_min', 'energy_max', 'nuclides') + cv.check_type("resonance scattering settings", res, Mapping) + keys = ("enable", "method", "energy_min", "energy_max", "nuclides") for key, value in res.items(): - cv.check_value('resonance scattering dictionary key', key, keys) - if key == 'enable': - cv.check_type('resonance scattering enable', value, bool) - elif key == 'method': - cv.check_value('resonance scattering method', value, - _RES_SCAT_METHODS) - elif key == 'energy_min': - name = 'resonance scattering minimum energy' + cv.check_value("resonance scattering dictionary key", key, keys) + if key == "enable": + cv.check_type("resonance scattering enable", value, bool) + elif key == "method": + cv.check_value("resonance scattering method", value, _RES_SCAT_METHODS) + elif key == "energy_min": + name = "resonance scattering minimum energy" cv.check_type(name, value, Real) cv.check_greater_than(name, value, 0) - elif key == 'energy_max': - name = 'resonance scattering minimum energy' + elif key == "energy_max": + name = "resonance scattering minimum energy" cv.check_type(name, value, Real) cv.check_greater_than(name, value, 0) - elif key == 'nuclides': - cv.check_type('resonance scattering nuclides', value, - Iterable, str) + elif key == "nuclides": + cv.check_type("resonance scattering nuclides", value, Iterable, str) self._resonance_scattering = res @property @@ -941,7 +973,8 @@ def volume_calculations( if not isinstance(vol_calcs, MutableSequence): vol_calcs = [vol_calcs] self._volume_calculations = cv.CheckedList( - VolumeCalculation, 'stochastic volume calculations', vol_calcs) + VolumeCalculation, "stochastic volume calculations", vol_calcs + ) @property def create_fission_neutrons(self) -> bool: @@ -949,8 +982,7 @@ def create_fission_neutrons(self) -> bool: @create_fission_neutrons.setter def create_fission_neutrons(self, create_fission_neutrons: bool): - cv.check_type('Whether create fission neutrons', - create_fission_neutrons, bool) + cv.check_type("Whether create fission neutrons", create_fission_neutrons, bool) self._create_fission_neutrons = create_fission_neutrons @property @@ -959,8 +991,9 @@ def create_delayed_neutrons(self) -> bool: @create_delayed_neutrons.setter def create_delayed_neutrons(self, create_delayed_neutrons: bool): - cv.check_type('Whether create only prompt neutrons', - create_delayed_neutrons, bool) + cv.check_type( + "Whether create only prompt neutrons", create_delayed_neutrons, bool + ) self._create_delayed_neutrons = create_delayed_neutrons @property @@ -969,7 +1002,7 @@ def delayed_photon_scaling(self) -> bool: @delayed_photon_scaling.setter def delayed_photon_scaling(self, value: bool): - cv.check_type('delayed photon scaling', value, bool) + cv.check_type("delayed photon scaling", value, bool) self._delayed_photon_scaling = value @property @@ -978,7 +1011,7 @@ def material_cell_offsets(self) -> bool: @material_cell_offsets.setter def material_cell_offsets(self, value: bool): - cv.check_type('material cell offsets', value, bool) + cv.check_type("material cell offsets", value, bool) self._material_cell_offsets = value @property @@ -987,8 +1020,8 @@ def log_grid_bins(self) -> int: @log_grid_bins.setter def log_grid_bins(self, log_grid_bins: int): - cv.check_type('log grid bins', log_grid_bins, Real) - cv.check_greater_than('log grid bins', log_grid_bins, 0) + cv.check_type("log grid bins", log_grid_bins, Real) + cv.check_greater_than("log grid bins", log_grid_bins, 0) self._log_grid_bins = log_grid_bins @property @@ -997,7 +1030,7 @@ def event_based(self) -> bool: @event_based.setter def event_based(self, value: bool): - cv.check_type('event based', value, bool) + cv.check_type("event based", value, bool) self._event_based = value @property @@ -1006,8 +1039,8 @@ def max_particles_in_flight(self) -> int: @max_particles_in_flight.setter def max_particles_in_flight(self, value: int): - cv.check_type('max particles in flight', value, Integral) - cv.check_greater_than('max particles in flight', value, 0) + cv.check_type("max particles in flight", value, Integral) + cv.check_greater_than("max particles in flight", value, 0) self._max_particles_in_flight = value @property @@ -1016,8 +1049,8 @@ def max_particle_events(self) -> int: @max_particle_events.setter def max_particle_events(self, value: int): - cv.check_type('max particle events', value, Integral) - cv.check_greater_than('max particle events', value, 0) + cv.check_type("max particle events", value, Integral) + cv.check_greater_than("max particle events", value, 0) self._max_particle_events = value @property @@ -1026,7 +1059,7 @@ def write_initial_source(self) -> bool: @write_initial_source.setter def write_initial_source(self, value: bool): - cv.check_type('write initial source', value, bool) + cv.check_type("write initial source", value, bool) self._write_initial_source = value @property @@ -1037,7 +1070,7 @@ def weight_windows(self) -> list[WeightWindows]: def weight_windows(self, value: WeightWindows | Iterable[WeightWindows]): if not isinstance(value, MutableSequence): value = [value] - self._weight_windows = cv.CheckedList(WeightWindows, 'weight windows', value) + self._weight_windows = cv.CheckedList(WeightWindows, "weight windows", value) @property def weight_windows_on(self) -> bool: @@ -1045,7 +1078,7 @@ def weight_windows_on(self) -> bool: @weight_windows_on.setter def weight_windows_on(self, value: bool): - cv.check_type('weight windows on', value, bool) + cv.check_type("weight windows on", value, bool) self._weight_windows_on = value @property @@ -1055,12 +1088,14 @@ def weight_window_checkpoints(self) -> dict: @weight_window_checkpoints.setter def weight_window_checkpoints(self, weight_window_checkpoints: dict): for key in weight_window_checkpoints.keys(): - cv.check_value('weight_window_checkpoints', key, ('collision', 'surface')) + cv.check_value("weight_window_checkpoints", key, ("collision", "surface")) self._weight_window_checkpoints = weight_window_checkpoints @property def max_splits(self): - raise AttributeError('max_splits has been deprecated. Please use max_history_splits instead') + raise AttributeError( + "max_splits has been deprecated. Please use max_history_splits instead" + ) @property def max_history_splits(self) -> int: @@ -1068,8 +1103,8 @@ def max_history_splits(self) -> int: @max_history_splits.setter def max_history_splits(self, value: int): - cv.check_type('maximum particle splits', value, Integral) - cv.check_greater_than('max particle splits', value, 0) + cv.check_type("maximum particle splits", value, Integral) + cv.check_greater_than("max particle splits", value, 0) self._max_history_splits = value @property @@ -1078,8 +1113,8 @@ def max_tracks(self) -> int: @max_tracks.setter def max_tracks(self, value: int): - cv.check_type('maximum particle tracks', value, Integral) - cv.check_greater_than('maximum particle tracks', value, 0, True) + cv.check_type("maximum particle tracks", value, Integral) + cv.check_greater_than("maximum particle tracks", value, 0, True) self._max_tracks = value @property @@ -1088,7 +1123,7 @@ def weight_windows_file(self) -> PathLike | None: @weight_windows_file.setter def weight_windows_file(self, value: PathLike): - cv.check_type('weight windows file', value, PathLike) + cv.check_type("weight windows file", value, PathLike) self._weight_windows_file = input_path(value) @property @@ -1099,7 +1134,9 @@ def weight_window_generators(self) -> list[WeightWindowGenerator]: def weight_window_generators(self, wwgs): if not isinstance(wwgs, MutableSequence): wwgs = [wwgs] - self._weight_window_generators = cv.CheckedList(WeightWindowGenerator, 'weight window generators', wwgs) + self._weight_window_generators = cv.CheckedList( + WeightWindowGenerator, "weight window generators", wwgs + ) @property def random_ray(self) -> dict: @@ -1108,32 +1145,37 @@ def random_ray(self) -> dict: @random_ray.setter def random_ray(self, random_ray: dict): if not isinstance(random_ray, Mapping): - raise ValueError(f'Unable to set random_ray from "{random_ray}" ' - 'which is not a dict.') + raise ValueError( + f'Unable to set random_ray from "{random_ray}" ' "which is not a dict." + ) for key in random_ray: - if key == 'distance_active': - cv.check_type('active ray length', random_ray[key], Real) - cv.check_greater_than('active ray length', random_ray[key], 0.0) - elif key == 'distance_inactive': - cv.check_type('inactive ray length', random_ray[key], Real) - cv.check_greater_than('inactive ray length', - random_ray[key], 0.0, True) - elif key == 'ray_source': - cv.check_type('random ray source', random_ray[key], SourceBase) - elif key == 'volume_estimator': - cv.check_value('volume estimator', random_ray[key], - ('naive', 'simulation_averaged', - 'hybrid')) - elif key == 'source_shape': - cv.check_value('source shape', random_ray[key], - ('flat', 'linear', 'linear_xy')) - elif key == 'volume_normalized_flux_tallies': - cv.check_type('volume normalized flux tallies', random_ray[key], bool) - elif key == 'adjoint': - cv.check_type('adjoint', random_ray[key], bool) + if key == "distance_active": + cv.check_type("active ray length", random_ray[key], Real) + cv.check_greater_than("active ray length", random_ray[key], 0.0) + elif key == "distance_inactive": + cv.check_type("inactive ray length", random_ray[key], Real) + cv.check_greater_than("inactive ray length", random_ray[key], 0.0, True) + elif key == "ray_source": + cv.check_type("random ray source", random_ray[key], SourceBase) + elif key == "volume_estimator": + cv.check_value( + "volume estimator", + random_ray[key], + ("naive", "simulation_averaged", "hybrid"), + ) + elif key == "source_shape": + cv.check_value( + "source shape", random_ray[key], ("flat", "linear", "linear_xy") + ) + elif key == "volume_normalized_flux_tallies": + cv.check_type("volume normalized flux tallies", random_ray[key], bool) + elif key == "adjoint": + cv.check_type("adjoint", random_ray[key], bool) else: - raise ValueError(f'Unable to set random ray to "{key}" which is ' - 'unsupported by OpenMC') + raise ValueError( + f'Unable to set random ray to "{key}" which is ' + "unsupported by OpenMC" + ) self._random_ray = random_ray @@ -1196,7 +1238,9 @@ def _create_max_order_subelement(self, root): def _create_source_subelement(self, root, mesh_memo=None): for source in self.source: root.append(source.to_xml_element()) - if isinstance(source, IndependentSource) and isinstance(source.space, MeshSpatial): + if isinstance(source, IndependentSource) and isinstance( + source.space, MeshSpatial + ): path = f"./mesh[@id='{source.space.mesh.id}']" if root.find(path) is None: root.append(source.space.mesh.to_xml_element()) @@ -1216,7 +1260,7 @@ def _create_output_subelement(self, root): element = ET.SubElement(root, "output") for key, value in sorted(self._output.items()): subelement = ET.SubElement(element, key) - if key in ('summary', 'tallies'): + if key in ("summary", "tallies"): subelement.text = str(value).lower() else: subelement.text = value @@ -1229,10 +1273,9 @@ def _create_verbosity_subelement(self, root): def _create_statepoint_subelement(self, root): if self._statepoint: element = ET.SubElement(root, "state_point") - if 'batches' in self._statepoint: + if "batches" in self._statepoint: subelement = ET.SubElement(element, "batches") - subelement.text = ' '.join( - str(x) for x in self._statepoint['batches']) + subelement.text = " ".join(str(x) for x in self._statepoint["batches"]) def _create_uniform_source_sampling_subelement(self, root): if self._uniform_source_sampling is not None: @@ -1243,34 +1286,33 @@ def _create_sourcepoint_subelement(self, root): if self._sourcepoint: element = ET.SubElement(root, "source_point") - if 'batches' in self._sourcepoint: + if "batches" in self._sourcepoint: subelement = ET.SubElement(element, "batches") - subelement.text = ' '.join( - str(x) for x in self._sourcepoint['batches']) + subelement.text = " ".join(str(x) for x in self._sourcepoint["batches"]) - if 'separate' in self._sourcepoint: + if "separate" in self._sourcepoint: subelement = ET.SubElement(element, "separate") - subelement.text = str(self._sourcepoint['separate']).lower() + subelement.text = str(self._sourcepoint["separate"]).lower() - if 'write' in self._sourcepoint: + if "write" in self._sourcepoint: subelement = ET.SubElement(element, "write") - subelement.text = str(self._sourcepoint['write']).lower() + subelement.text = str(self._sourcepoint["write"]).lower() # Overwrite latest subelement - if 'overwrite' in self._sourcepoint: + if "overwrite" in self._sourcepoint: subelement = ET.SubElement(element, "overwrite_latest") - subelement.text = str(self._sourcepoint['overwrite']).lower() + subelement.text = str(self._sourcepoint["overwrite"]).lower() - if 'mcpl' in self._sourcepoint: + if "mcpl" in self._sourcepoint: subelement = ET.SubElement(element, "mcpl") - subelement.text = str(self._sourcepoint['mcpl']).lower() + subelement.text = str(self._sourcepoint["mcpl"]).lower() def _create_surf_source_read_subelement(self, root): if self._surf_source_read: element = ET.SubElement(root, "surf_source_read") - if 'path' in self._surf_source_read: + if "path" in self._surf_source_read: subelement = ET.SubElement(element, "path") - subelement.text = str(self._surf_source_read['path']) + subelement.text = str(self._surf_source_read["path"]) def _create_surf_source_write_subelement(self, root): if self._surf_source_write: @@ -1283,7 +1325,13 @@ def _create_surf_source_write_subelement(self, root): if "mcpl" in self._surf_source_write: subelement = ET.SubElement(element, "mcpl") subelement.text = str(self._surf_source_write["mcpl"]).lower() - for key in ("max_particles", "max_source_files", "cell", "cellfrom", "cellto"): + for key in ( + "max_particles", + "max_source_files", + "cell", + "cellfrom", + "cellto", + ): if key in self._surf_source_write: subelement = ET.SubElement(element, key) subelement.text = str(self._surf_source_write[key]) @@ -1337,12 +1385,14 @@ def _create_entropy_mesh_subelement(self, root, mesh_memo=None): # use default heuristic for entropy mesh if not set by user if self.entropy_mesh.dimension is None: if self.particles is None: - raise RuntimeError("Number of particles must be set in order to " \ - "use entropy mesh dimension heuristic") + raise RuntimeError( + "Number of particles must be set in order to " + "use entropy mesh dimension heuristic" + ) else: - n = ceil((self.particles / 20.0)**(1.0 / 3.0)) + n = ceil((self.particles / 20.0) ** (1.0 / 3.0)) d = len(self.entropy_mesh.lower_left) - self.entropy_mesh.dimension = (n,)*d + self.entropy_mesh.dimension = (n,) * d # add mesh ID to this element subelement = ET.SubElement(root, "entropy_mesh") @@ -1383,10 +1433,10 @@ def _create_tabular_legendre_subelements(self, root): if self.tabular_legendre: element = ET.SubElement(root, "tabular_legendre") subelement = ET.SubElement(element, "enable") - subelement.text = str(self._tabular_legendre['enable']).lower() - if 'num_points' in self._tabular_legendre: + subelement.text = str(self._tabular_legendre["enable"]).lower() + if "num_points" in self._tabular_legendre: subelement = ET.SubElement(element, "num_points") - subelement.text = str(self._tabular_legendre['num_points']) + subelement.text = str(self._tabular_legendre["num_points"]) def _create_temperature_subelements(self, root): if self.temperature: @@ -1394,20 +1444,20 @@ def _create_temperature_subelements(self, root): element = ET.SubElement(root, f"temperature_{key}") if isinstance(value, bool): element.text = str(value).lower() - elif key == 'range': - element.text = ' '.join(str(T) for T in value) + elif key == "range": + element.text = " ".join(str(T) for T in value) else: element.text = str(value) def _create_trace_subelement(self, root): if self._trace is not None: element = ET.SubElement(root, "trace") - element.text = ' '.join(map(str, self._trace)) + element.text = " ".join(map(str, self._trace)) def _create_track_subelement(self, root): if self._track is not None: element = ET.SubElement(root, "track") - element.text = ' '.join(map(str, itertools.chain(*self._track))) + element.text = " ".join(map(str, itertools.chain(*self._track))) def _create_ufs_mesh_subelement(self, root, mesh_memo=None): if self.ufs_mesh is None: @@ -1423,27 +1473,28 @@ def _create_ufs_mesh_subelement(self, root, mesh_memo=None): path = f"./mesh[@id='{self.ufs_mesh.id}']" if root.find(path) is None: root.append(self.ufs_mesh.to_xml_element()) - if mesh_memo is not None: mesh_memo.add(self.ufs_mesh.id) + if mesh_memo is not None: + mesh_memo.add(self.ufs_mesh.id) def _create_resonance_scattering_subelement(self, root): res = self.resonance_scattering if res: - elem = ET.SubElement(root, 'resonance_scattering') - if 'enable' in res: - subelem = ET.SubElement(elem, 'enable') - subelem.text = str(res['enable']).lower() - if 'method' in res: - subelem = ET.SubElement(elem, 'method') - subelem.text = res['method'] - if 'energy_min' in res: - subelem = ET.SubElement(elem, 'energy_min') - subelem.text = str(res['energy_min']) - if 'energy_max' in res: - subelem = ET.SubElement(elem, 'energy_max') - subelem.text = str(res['energy_max']) - if 'nuclides' in res: - subelem = ET.SubElement(elem, 'nuclides') - subelem.text = ' '.join(res['nuclides']) + elem = ET.SubElement(root, "resonance_scattering") + if "enable" in res: + subelem = ET.SubElement(elem, "enable") + subelem.text = str(res["enable"]).lower() + if "method" in res: + subelem = ET.SubElement(elem, "method") + subelem.text = res["method"] + if "energy_min" in res: + subelem = ET.SubElement(elem, "energy_min") + subelem.text = str(res["energy_min"]) + if "energy_max" in res: + subelem = ET.SubElement(elem, "energy_max") + subelem.text = str(res["energy_max"]) + if "nuclides" in res: + subelem = ET.SubElement(elem, "nuclides") + subelem.text = " ".join(res["nuclides"]) def _create_create_fission_neutrons_subelement(self, root): if self._create_fission_neutrons is not None: @@ -1514,7 +1565,7 @@ def _create_weight_windows_subelement(self, root, mesh_memo=None): def _create_weight_window_generators_subelement(self, root, mesh_memo=None): if not self.weight_window_generators: return - elem = ET.SubElement(root, 'weight_window_generators') + elem = ET.SubElement(root, "weight_window_generators") for wwg in self.weight_window_generators: elem.append(wwg.to_xml_element()) @@ -1538,13 +1589,13 @@ def _create_weight_window_checkpoints_subelement(self, root): return element = ET.SubElement(root, "weight_window_checkpoints") - if 'collision' in self._weight_window_checkpoints: + if "collision" in self._weight_window_checkpoints: subelement = ET.SubElement(element, "collision") - subelement.text = str(self._weight_window_checkpoints['collision']).lower() + subelement.text = str(self._weight_window_checkpoints["collision"]).lower() - if 'surface' in self._weight_window_checkpoints: + if "surface" in self._weight_window_checkpoints: subelement = ET.SubElement(element, "surface") - subelement.text = str(self._weight_window_checkpoints['surface']).lower() + subelement.text = str(self._weight_window_checkpoints["surface"]).lower() def _create_max_history_splits_subelement(self, root): if self._max_history_splits is not None: @@ -1560,7 +1611,7 @@ def _create_random_ray_subelement(self, root): if self._random_ray: element = ET.SubElement(root, "random_ray") for key, value in self._random_ray.items(): - if key == 'ray_source' and isinstance(value, SourceBase): + if key == "ray_source" and isinstance(value, SourceBase): source_element = value.to_xml_element() element.append(source_element) else: @@ -1568,7 +1619,7 @@ def _create_random_ray_subelement(self, root): subelement.text = str(value) def _eigenvalue_from_xml_element(self, root): - elem = root.find('eigenvalue') + elem = root.find("eigenvalue") if elem is not None: self._run_mode_from_xml_element(elem) self._particles_from_xml_element(elem) @@ -1580,54 +1631,54 @@ def _eigenvalue_from_xml_element(self, root): self._generations_per_batch_from_xml_element(elem) def _run_mode_from_xml_element(self, root): - text = get_text(root, 'run_mode') + text = get_text(root, "run_mode") if text is not None: self.run_mode = text def _particles_from_xml_element(self, root): - text = get_text(root, 'particles') + text = get_text(root, "particles") if text is not None: self.particles = int(text) def _batches_from_xml_element(self, root): - text = get_text(root, 'batches') + text = get_text(root, "batches") if text is not None: self.batches = int(text) def _inactive_from_xml_element(self, root): - text = get_text(root, 'inactive') + text = get_text(root, "inactive") if text is not None: self.inactive = int(text) def _max_lost_particles_from_xml_element(self, root): - text = get_text(root, 'max_lost_particles') + text = get_text(root, "max_lost_particles") if text is not None: self.max_lost_particles = int(text) def _rel_max_lost_particles_from_xml_element(self, root): - text = get_text(root, 'rel_max_lost_particles') + text = get_text(root, "rel_max_lost_particles") if text is not None: self.rel_max_lost_particles = float(text) def _max_write_lost_particles_from_xml_element(self, root): - text = get_text(root, 'max_write_lost_particles') + text = get_text(root, "max_write_lost_particles") if text is not None: self.max_write_lost_particles = int(text) def _generations_per_batch_from_xml_element(self, root): - text = get_text(root, 'generations_per_batch') + text = get_text(root, "generations_per_batch") if text is not None: self.generations_per_batch = int(text) def _keff_trigger_from_xml_element(self, root): - elem = root.find('keff_trigger') + elem = root.find("keff_trigger") if elem is not None: - trigger = get_text(elem, 'type') - threshold = float(get_text(elem, 'threshold')) - self.keff_trigger = {'type': trigger, 'threshold': threshold} + trigger = get_text(elem, "type") + threshold = float(get_text(elem, "threshold")) + self.keff_trigger = {"type": trigger, "threshold": threshold} def _source_from_xml_element(self, root, meshes=None): - for elem in root.findall('source'): + for elem in root.findall("source"): src = SourceBase.from_xml_element(elem, meshes) # add newly constructed source object to the list self.source.append(src) @@ -1635,129 +1686,153 @@ def _source_from_xml_element(self, root, meshes=None): def _volume_calcs_from_xml_element(self, root): volume_elems = root.findall("volume_calc") if volume_elems: - self.volume_calculations = [VolumeCalculation.from_xml_element(elem) - for elem in volume_elems] + self.volume_calculations = [ + VolumeCalculation.from_xml_element(elem) for elem in volume_elems + ] def _output_from_xml_element(self, root): - elem = root.find('output') + elem = root.find("output") if elem is not None: self.output = {} - for key in ('summary', 'tallies', 'path'): + for key in ("summary", "tallies", "path"): value = get_text(elem, key) if value is not None: - if key in ('summary', 'tallies'): - value = value in ('true', '1') + if key in ("summary", "tallies"): + value = value in ("true", "1") self.output[key] = value def _statepoint_from_xml_element(self, root): - elem = root.find('state_point') + elem = root.find("state_point") if elem is not None: - text = get_text(elem, 'batches') + text = get_text(elem, "batches") if text is not None: - self.statepoint['batches'] = [int(x) for x in text.split()] + self.statepoint["batches"] = [int(x) for x in text.split()] def _sourcepoint_from_xml_element(self, root): - elem = root.find('source_point') + elem = root.find("source_point") if elem is not None: - for key in ('separate', 'write', 'overwrite_latest', 'batches', 'mcpl'): + for key in ("separate", "write", "overwrite_latest", "batches", "mcpl"): value = get_text(elem, key) if value is not None: - if key in ('separate', 'write', 'mcpl'): - value = value in ('true', '1') - elif key == 'overwrite_latest': - value = value in ('true', '1') - key = 'overwrite' + if key in ("separate", "write", "mcpl"): + value = value in ("true", "1") + elif key == "overwrite_latest": + value = value in ("true", "1") + key = "overwrite" else: value = [int(x) for x in value.split()] self.sourcepoint[key] = value def _surf_source_read_from_xml_element(self, root): - elem = root.find('surf_source_read') + elem = root.find("surf_source_read") if elem is not None: ssr = {} - value = get_text(elem, 'path') + value = get_text(elem, "path") if value is not None: - ssr['path'] = value + ssr["path"] = value self.surf_source_read = ssr def _surf_source_write_from_xml_element(self, root): - elem = root.find('surf_source_write') + elem = root.find("surf_source_write") if elem is None: return - for key in ('surface_ids', 'max_particles', 'max_source_files', 'mcpl', 'cell', 'cellto', 'cellfrom'): + for key in ( + "surface_ids", + "max_particles", + "max_source_files", + "mcpl", + "cell", + "cellto", + "cellfrom", + ): value = get_text(elem, key) if value is not None: - if key == 'surface_ids': + if key == "surface_ids": value = [int(x) for x in value.split()] - elif key == 'mcpl': - value = value in ('true', '1') - elif key in ('max_particles', 'max_source_files', 'cell', 'cellfrom', 'cellto'): + elif key == "mcpl": + value = value in ("true", "1") + elif key in ( + "max_particles", + "max_source_files", + "cell", + "cellfrom", + "cellto", + ): value = int(value) self.surf_source_write[key] = value def _confidence_intervals_from_xml_element(self, root): - text = get_text(root, 'confidence_intervals') + text = get_text(root, "confidence_intervals") if text is not None: - self.confidence_intervals = text in ('true', '1') + self.confidence_intervals = text in ("true", "1") def _electron_treatment_from_xml_element(self, root): - text = get_text(root, 'electron_treatment') + text = get_text(root, "electron_treatment") if text is not None: self.electron_treatment = text def _energy_mode_from_xml_element(self, root): - text = get_text(root, 'energy_mode') + text = get_text(root, "energy_mode") if text is not None: self.energy_mode = text def _max_order_from_xml_element(self, root): - text = get_text(root, 'max_order') + text = get_text(root, "max_order") if text is not None: self.max_order = int(text) def _photon_transport_from_xml_element(self, root): - text = get_text(root, 'photon_transport') + text = get_text(root, "photon_transport") if text is not None: - self.photon_transport = text in ('true', '1') + self.photon_transport = text in ("true", "1") def _uniform_source_sampling_from_xml_element(self, root): - text = get_text(root, 'uniform_source_sampling') + text = get_text(root, "uniform_source_sampling") if text is not None: - self.uniform_source_sampling = text in ('true', '1') + self.uniform_source_sampling = text in ("true", "1") def _plot_seed_from_xml_element(self, root): - text = get_text(root, 'plot_seed') + text = get_text(root, "plot_seed") if text is not None: self.plot_seed = int(text) def _ptables_from_xml_element(self, root): - text = get_text(root, 'ptables') + text = get_text(root, "ptables") if text is not None: - self.ptables = text in ('true', '1') + self.ptables = text in ("true", "1") def _seed_from_xml_element(self, root): - text = get_text(root, 'seed') + text = get_text(root, "seed") if text is not None: self.seed = int(text) def _survival_biasing_from_xml_element(self, root): - text = get_text(root, 'survival_biasing') + text = get_text(root, "survival_biasing") if text is not None: - self.survival_biasing = text in ('true', '1') + self.survival_biasing = text in ("true", "1") def _cutoff_from_xml_element(self, root): - elem = root.find('cutoff') + elem = root.find("cutoff") if elem is not None: self.cutoff = {} - for key in ('energy_neutron', 'energy_photon', 'energy_electron', - 'energy_positron', 'weight', 'weight_avg', 'time_neutron', - 'time_photon', 'time_electron', 'time_positron'): + for key in ( + "energy_neutron", + "energy_photon", + "energy_electron", + "energy_positron", + "weight", + "weight_avg", + "time_neutron", + "time_photon", + "time_electron", + "time_positron", + ): value = get_text(elem, key) if value is not None: self.cutoff[key] = float(value) def _entropy_mesh_from_xml_element(self, root, meshes): - text = get_text(root, 'entropy_mesh') + text = get_text(root, "entropy_mesh") if text is None: return mesh_id = int(text) @@ -1766,65 +1841,65 @@ def _entropy_mesh_from_xml_element(self, root, meshes): self.entropy_mesh = meshes[mesh_id] def _trigger_from_xml_element(self, root): - elem = root.find('trigger') + elem = root.find("trigger") if elem is not None: - self.trigger_active = get_text(elem, 'active') in ('true', '1') - text = get_text(elem, 'max_batches') + self.trigger_active = get_text(elem, "active") in ("true", "1") + text = get_text(elem, "max_batches") if text is not None: self.trigger_max_batches = int(text) - text = get_text(elem, 'batch_interval') + text = get_text(elem, "batch_interval") if text is not None: self.trigger_batch_interval = int(text) def _no_reduce_from_xml_element(self, root): - text = get_text(root, 'no_reduce') + text = get_text(root, "no_reduce") if text is not None: - self.no_reduce = text in ('true', '1') + self.no_reduce = text in ("true", "1") def _verbosity_from_xml_element(self, root): - text = get_text(root, 'verbosity') + text = get_text(root, "verbosity") if text is not None: self.verbosity = int(text) def _tabular_legendre_from_xml_element(self, root): - elem = root.find('tabular_legendre') + elem = root.find("tabular_legendre") if elem is not None: - text = get_text(elem, 'enable') - self.tabular_legendre['enable'] = text in ('true', '1') - text = get_text(elem, 'num_points') + text = get_text(elem, "enable") + self.tabular_legendre["enable"] = text in ("true", "1") + text = get_text(elem, "num_points") if text is not None: - self.tabular_legendre['num_points'] = int(text) + self.tabular_legendre["num_points"] = int(text) def _temperature_from_xml_element(self, root): - text = get_text(root, 'temperature_default') + text = get_text(root, "temperature_default") if text is not None: - self.temperature['default'] = float(text) - text = get_text(root, 'temperature_tolerance') + self.temperature["default"] = float(text) + text = get_text(root, "temperature_tolerance") if text is not None: - self.temperature['tolerance'] = float(text) - text = get_text(root, 'temperature_method') + self.temperature["tolerance"] = float(text) + text = get_text(root, "temperature_method") if text is not None: - self.temperature['method'] = text - text = get_text(root, 'temperature_range') + self.temperature["method"] = text + text = get_text(root, "temperature_range") if text is not None: - self.temperature['range'] = [float(x) for x in text.split()] - text = get_text(root, 'temperature_multipole') + self.temperature["range"] = [float(x) for x in text.split()] + text = get_text(root, "temperature_multipole") if text is not None: - self.temperature['multipole'] = text in ('true', '1') + self.temperature["multipole"] = text in ("true", "1") def _trace_from_xml_element(self, root): - text = get_text(root, 'trace') + text = get_text(root, "trace") if text is not None: self.trace = [int(x) for x in text.split()] def _track_from_xml_element(self, root): - text = get_text(root, 'track') + text = get_text(root, "track") if text is not None: values = [int(x) for x in text.split()] self.track = list(zip(values[::3], values[1::3], values[2::3])) def _ufs_mesh_from_xml_element(self, root, meshes): - text = get_text(root, 'ufs_mesh') + text = get_text(root, "ufs_mesh") if text is None: return mesh_id = int(text) @@ -1833,121 +1908,120 @@ def _ufs_mesh_from_xml_element(self, root, meshes): self.ufs_mesh = meshes[mesh_id] def _resonance_scattering_from_xml_element(self, root): - elem = root.find('resonance_scattering') + elem = root.find("resonance_scattering") if elem is not None: - keys = ('enable', 'method', 'energy_min', 'energy_max', 'nuclides') + keys = ("enable", "method", "energy_min", "energy_max", "nuclides") for key in keys: value = get_text(elem, key) if value is not None: - if key == 'enable': - value = value in ('true', '1') - elif key in ('energy_min', 'energy_max'): + if key == "enable": + value = value in ("true", "1") + elif key in ("energy_min", "energy_max"): value = float(value) - elif key == 'nuclides': + elif key == "nuclides": value = value.split() self.resonance_scattering[key] = value def _create_fission_neutrons_from_xml_element(self, root): - text = get_text(root, 'create_fission_neutrons') + text = get_text(root, "create_fission_neutrons") if text is not None: - self.create_fission_neutrons = text in ('true', '1') + self.create_fission_neutrons = text in ("true", "1") def _create_delayed_neutrons_from_xml_element(self, root): - text = get_text(root, 'create_delayed_neutrons') + text = get_text(root, "create_delayed_neutrons") if text is not None: - self.create_delayed_neutrons = text in ('true', '1') + self.create_delayed_neutrons = text in ("true", "1") def _delayed_photon_scaling_from_xml_element(self, root): - text = get_text(root, 'delayed_photon_scaling') + text = get_text(root, "delayed_photon_scaling") if text is not None: - self.delayed_photon_scaling = text in ('true', '1') + self.delayed_photon_scaling = text in ("true", "1") def _event_based_from_xml_element(self, root): - text = get_text(root, 'event_based') + text = get_text(root, "event_based") if text is not None: - self.event_based = text in ('true', '1') + self.event_based = text in ("true", "1") def _max_particles_in_flight_from_xml_element(self, root): - text = get_text(root, 'max_particles_in_flight') + text = get_text(root, "max_particles_in_flight") if text is not None: self.max_particles_in_flight = int(text) def _max_particle_events_from_xml_element(self, root): - text = get_text(root, 'max_particle_events') + text = get_text(root, "max_particle_events") if text is not None: self.max_particle_events = int(text) def _material_cell_offsets_from_xml_element(self, root): - text = get_text(root, 'material_cell_offsets') + text = get_text(root, "material_cell_offsets") if text is not None: - self.material_cell_offsets = text in ('true', '1') + self.material_cell_offsets = text in ("true", "1") def _log_grid_bins_from_xml_element(self, root): - text = get_text(root, 'log_grid_bins') + text = get_text(root, "log_grid_bins") if text is not None: self.log_grid_bins = int(text) def _write_initial_source_from_xml_element(self, root): - text = get_text(root, 'write_initial_source') + text = get_text(root, "write_initial_source") if text is not None: - self.write_initial_source = text in ('true', '1') + self.write_initial_source = text in ("true", "1") def _weight_window_generators_from_xml_element(self, root, meshes=None): - for elem in root.iter('weight_windows_generator'): + for elem in root.iter("weight_windows_generator"): wwg = WeightWindowGenerator.from_xml_element(elem, meshes) self.weight_window_generators.append(wwg) def _weight_windows_from_xml_element(self, root, meshes=None): - for elem in root.findall('weight_windows'): + for elem in root.findall("weight_windows"): ww = WeightWindows.from_xml_element(elem, meshes) self.weight_windows.append(ww) - text = get_text(root, 'weight_windows_on') + text = get_text(root, "weight_windows_on") if text is not None: - self.weight_windows_on = text in ('true', '1') + self.weight_windows_on = text in ("true", "1") def _weight_window_checkpoints_from_xml_element(self, root): - elem = root.find('weight_window_checkpoints') + elem = root.find("weight_window_checkpoints") if elem is None: return - for key in ('collision', 'surface'): + for key in ("collision", "surface"): value = get_text(elem, key) if value is not None: - value = value in ('true', '1') + value = value in ("true", "1") self.weight_window_checkpoints[key] = value def _max_history_splits_from_xml_element(self, root): - text = get_text(root, 'max_history_splits') + text = get_text(root, "max_history_splits") if text is not None: self.max_history_splits = int(text) def _max_tracks_from_xml_element(self, root): - text = get_text(root, 'max_tracks') + text = get_text(root, "max_tracks") if text is not None: self.max_tracks = int(text) def _random_ray_from_xml_element(self, root): - elem = root.find('random_ray') + elem = root.find("random_ray") if elem is not None: self.random_ray = {} for child in elem: - if child.tag in ('distance_inactive', 'distance_active'): + if child.tag in ("distance_inactive", "distance_active"): self.random_ray[child.tag] = float(child.text) - elif child.tag == 'source': + elif child.tag == "source": source = SourceBase.from_xml_element(child) - self.random_ray['ray_source'] = source - elif child.tag == 'volume_estimator': - self.random_ray['volume_estimator'] = child.text - elif child.tag == 'source_shape': - self.random_ray['source_shape'] = child.text - elif child.tag == 'volume_normalized_flux_tallies': - self.random_ray['volume_normalized_flux_tallies'] = ( - child.text in ('true', '1') - ) - elif child.tag == 'adjoint': - self.random_ray['adjoint'] = ( - child.text in ('true', '1') + self.random_ray["ray_source"] = source + elif child.tag == "volume_estimator": + self.random_ray["volume_estimator"] = child.text + elif child.tag == "source_shape": + self.random_ray["source_shape"] = child.text + elif child.tag == "volume_normalized_flux_tallies": + self.random_ray["volume_normalized_flux_tallies"] = child.text in ( + "true", + "1", ) + elif child.tag == "adjoint": + self.random_ray["adjoint"] = child.text in ("true", "1") def to_xml_element(self, mesh_memo=None): """Create a 'settings' element to be written to an XML file. @@ -2020,7 +2094,7 @@ def to_xml_element(self, mesh_memo=None): return element - def export_to_xml(self, path: PathLike = 'settings.xml'): + def export_to_xml(self, path: PathLike = "settings.xml"): """Export simulation settings to an XML file. Parameters @@ -2034,11 +2108,11 @@ def export_to_xml(self, path: PathLike = 'settings.xml'): # Check if path is a directory p = Path(path) if p.is_dir(): - p /= 'settings.xml' + p /= "settings.xml" # Write the XML Tree to the settings.xml file tree = ET.ElementTree(root_element) - tree.write(str(p), xml_declaration=True, encoding='utf-8') + tree.write(str(p), xml_declaration=True, encoding="utf-8") @classmethod def from_xml_element(cls, elem, meshes=None): @@ -2123,7 +2197,7 @@ def from_xml_element(cls, elem, meshes=None): return settings @classmethod - def from_xml(cls, path: PathLike = 'settings.xml'): + def from_xml(cls, path: PathLike = "settings.xml"): """Generate settings from XML file .. versionadded:: 0.13.0 diff --git a/openmc/source.py b/openmc/source.py index 878f52c3b22..ce431bb1559 100644 --- a/openmc/source.py +++ b/openmc/source.py @@ -58,9 +58,7 @@ class SourceBase(ABC): """ def __init__( - self, - strength: float | None = 1.0, - constraints: dict[str, Any] | None = None + self, strength: float | None = 1.0, constraints: dict[str, Any] | None = None ): self.strength = strength self.constraints = constraints @@ -71,9 +69,9 @@ def strength(self): @strength.setter def strength(self, strength): - cv.check_type('source strength', strength, Real, none_ok=True) + cv.check_type("source strength", strength, Real, none_ok=True) if strength is not None: - cv.check_greater_than('source strength', strength, 0.0, True) + cv.check_greater_than("source strength", strength, 0.0, True) self._strength = strength @property @@ -87,30 +85,34 @@ def constraints(self, constraints: dict[str, Any] | None): return for key, value in constraints.items(): - if key == 'domains': - cv.check_type('domains', value, Iterable, - (openmc.Cell, openmc.Material, openmc.Universe)) + if key == "domains": + cv.check_type( + "domains", + value, + Iterable, + (openmc.Cell, openmc.Material, openmc.Universe), + ) if isinstance(value[0], openmc.Cell): - self._constraints['domain_type'] = 'cell' + self._constraints["domain_type"] = "cell" elif isinstance(value[0], openmc.Material): - self._constraints['domain_type'] = 'material' + self._constraints["domain_type"] = "material" elif isinstance(value[0], openmc.Universe): - self._constraints['domain_type'] = 'universe' - self._constraints['domain_ids'] = [d.id for d in value] - elif key == 'time_bounds': - cv.check_type('time bounds', value, Iterable, Real) - self._constraints['time_bounds'] = tuple(value) - elif key == 'energy_bounds': - cv.check_type('energy bounds', value, Iterable, Real) - self._constraints['energy_bounds'] = tuple(value) - elif key == 'fissionable': - cv.check_type('fissionable', value, bool) - self._constraints['fissionable'] = value - elif key == 'rejection_strategy': - cv.check_value('rejection strategy', value, ('resample', 'kill')) - self._constraints['rejection_strategy'] = value + self._constraints["domain_type"] = "universe" + self._constraints["domain_ids"] = [d.id for d in value] + elif key == "time_bounds": + cv.check_type("time bounds", value, Iterable, Real) + self._constraints["time_bounds"] = tuple(value) + elif key == "energy_bounds": + cv.check_type("energy bounds", value, Iterable, Real) + self._constraints["energy_bounds"] = tuple(value) + elif key == "fissionable": + cv.check_type("fissionable", value, bool) + self._constraints["fissionable"] = value + elif key == "rejection_strategy": + cv.check_value("rejection strategy", value, ("resample", "kill")) + self._constraints["rejection_strategy"] = value else: - raise ValueError('Unknown key in constraints dictionary: {key}') + raise ValueError("Unknown key in constraints dictionary: {key}") @abstractmethod def populate_xml_element(self, element): @@ -144,13 +146,13 @@ def to_xml_element(self) -> ET.Element: dt_elem = ET.SubElement(constraints_elem, "domain_type") dt_elem.text = constraints["domain_type"] id_elem = ET.SubElement(constraints_elem, "domain_ids") - id_elem.text = ' '.join(str(uid) for uid in constraints["domain_ids"]) + id_elem.text = " ".join(str(uid) for uid in constraints["domain_ids"]) if "time_bounds" in constraints: dt_elem = ET.SubElement(constraints_elem, "time_bounds") - dt_elem.text = ' '.join(str(t) for t in constraints["time_bounds"]) + dt_elem.text = " ".join(str(t) for t in constraints["time_bounds"]) if "energy_bounds" in constraints: dt_elem = ET.SubElement(constraints_elem, "energy_bounds") - dt_elem.text = ' '.join(str(E) for E in constraints["energy_bounds"]) + dt_elem.text = " ".join(str(E) for E in constraints["energy_bounds"]) if "fissionable" in constraints: dt_elem = ET.SubElement(constraints_elem, "fissionable") dt_elem.text = str(constraints["fissionable"]).lower() @@ -178,28 +180,28 @@ def from_xml_element(cls, elem: ET.Element, meshes=None) -> SourceBase: Source generated from XML element """ - source_type = get_text(elem, 'type') + source_type = get_text(elem, "type") if source_type is None: # attempt to determine source type based on attributes # for backward compatibility - if get_text(elem, 'file') is not None: + if get_text(elem, "file") is not None: return FileSource.from_xml_element(elem) - elif get_text(elem, 'library') is not None: + elif get_text(elem, "library") is not None: return CompiledSource.from_xml_element(elem) else: return IndependentSource.from_xml_element(elem) else: - if source_type == 'independent': + if source_type == "independent": return IndependentSource.from_xml_element(elem, meshes) - elif source_type == 'compiled': + elif source_type == "compiled": return CompiledSource.from_xml_element(elem) - elif source_type == 'file': + elif source_type == "file": return FileSource.from_xml_element(elem) - elif source_type == 'mesh': + elif source_type == "mesh": return MeshSource.from_xml_element(elem, meshes) else: - raise ValueError(f'Source type {source_type} is not recognized') + raise ValueError(f"Source type {source_type} is not recognized") @staticmethod def _get_constraints(elem: ET.Element) -> dict[str, Any]: @@ -215,30 +217,30 @@ def _get_constraints(elem: ET.Element) -> dict[str, Any]: # Instantiate some throw-away domains that are used by the # constructor to assign IDs with warnings.catch_warnings(): - warnings.simplefilter('ignore', openmc.IDWarning) - if domain_type == 'cell': + warnings.simplefilter("ignore", openmc.IDWarning) + if domain_type == "cell": domains = [openmc.Cell(uid) for uid in domain_ids] - elif domain_type == 'material': + elif domain_type == "material": domains = [openmc.Material(uid) for uid in domain_ids] - elif domain_type == 'universe': + elif domain_type == "universe": domains = [openmc.Universe(uid) for uid in domain_ids] - constraints['domains'] = domains + constraints["domains"] = domains time_bounds = get_text(elem, "time_bounds") if time_bounds is not None: - constraints['time_bounds'] = [float(x) for x in time_bounds.split()] + constraints["time_bounds"] = [float(x) for x in time_bounds.split()] energy_bounds = get_text(elem, "energy_bounds") if energy_bounds is not None: - constraints['energy_bounds'] = [float(x) for x in energy_bounds.split()] + constraints["energy_bounds"] = [float(x) for x in energy_bounds.split()] fissionable = get_text(elem, "fissionable") if fissionable is not None: - constraints['fissionable'] = fissionable in ('true', '1') + constraints["fissionable"] = fissionable in ("true", "1") rejection_strategy = get_text(elem, "rejection_strategy") if rejection_strategy is not None: - constraints['rejection_strategy'] = rejection_strategy + constraints["rejection_strategy"] = rejection_strategy return constraints @@ -315,14 +317,19 @@ def __init__( energy: openmc.stats.Univariate | None = None, time: openmc.stats.Univariate | None = None, strength: float = 1.0, - particle: str = 'neutron', - domains: Sequence[openmc.Cell | openmc.Material | openmc.Universe] | None = None, - constraints: dict[str, Any] | None = None + particle: str = "neutron", + domains: ( + Sequence[openmc.Cell | openmc.Material | openmc.Universe] | None + ) = None, + constraints: dict[str, Any] | None = None, ): if domains is not None: - warnings.warn("The 'domains' arguments has been replaced by the " - "'constraints' argument.", FutureWarning) - constraints = {'domains': domains} + warnings.warn( + "The 'domains' arguments has been replaced by the " + "'constraints' argument.", + FutureWarning, + ) + constraints = {"domains": domains} super().__init__(strength=strength, constraints=constraints) @@ -343,20 +350,24 @@ def __init__( @property def type(self) -> str: - return 'independent' + return "independent" def __getattr__(self, name): - cls_names = {'file': 'FileSource', 'library': 'CompiledSource', - 'parameters': 'CompiledSource'} + cls_names = { + "file": "FileSource", + "library": "CompiledSource", + "parameters": "CompiledSource", + } if name in cls_names: raise AttributeError( f'The "{name}" attribute has been deprecated on the ' - f'IndependentSource class. Please use the {cls_names[name]} class.') + f"IndependentSource class. Please use the {cls_names[name]} class." + ) else: super().__getattribute__(name) def __setattr__(self, name, value): - if name in ('file', 'library', 'parameters'): + if name in ("file", "library", "parameters"): # Ensure proper AttributeError is thrown getattr(self, name) else: @@ -368,7 +379,7 @@ def space(self): @space.setter def space(self, space): - cv.check_type('spatial distribution', space, Spatial) + cv.check_type("spatial distribution", space, Spatial) self._space = space @property @@ -377,7 +388,7 @@ def angle(self): @angle.setter def angle(self, angle): - cv.check_type('angular distribution', angle, UnitSphere) + cv.check_type("angular distribution", angle, UnitSphere) self._angle = angle @property @@ -386,7 +397,7 @@ def energy(self): @energy.setter def energy(self, energy): - cv.check_type('energy distribution', energy, Univariate) + cv.check_type("energy distribution", energy, Univariate) self._energy = energy @property @@ -395,7 +406,7 @@ def time(self): @time.setter def time(self, time): - cv.check_type('time distribution', time, Univariate) + cv.check_type("time distribution", time, Univariate) self._time = time @property @@ -404,7 +415,7 @@ def particle(self): @particle.setter def particle(self, particle): - cv.check_value('source particle', particle, ['neutron', 'photon']) + cv.check_value("source particle", particle, ["neutron", "photon"]) self._particle = particle def populate_xml_element(self, element): @@ -422,9 +433,9 @@ def populate_xml_element(self, element): if self.angle is not None: element.append(self.angle.to_xml_element()) if self.energy is not None: - element.append(self.energy.to_xml_element('energy')) + element.append(self.energy.to_xml_element("energy")) if self.time is not None: - element.append(self.time.to_xml_element('time')) + element.append(self.time.to_xml_element("time")) @classmethod def from_xml_element(cls, elem: ET.Element, meshes=None) -> SourceBase: @@ -447,27 +458,27 @@ def from_xml_element(cls, elem: ET.Element, meshes=None) -> SourceBase: constraints = cls._get_constraints(elem) source = cls(constraints=constraints) - strength = get_text(elem, 'strength') + strength = get_text(elem, "strength") if strength is not None: source.strength = float(strength) - particle = get_text(elem, 'particle') + particle = get_text(elem, "particle") if particle is not None: source.particle = particle - space = elem.find('space') + space = elem.find("space") if space is not None: source.space = Spatial.from_xml_element(space, meshes) - angle = elem.find('angle') + angle = elem.find("angle") if angle is not None: source.angle = UnitSphere.from_xml_element(angle) - energy = elem.find('energy') + energy = elem.find("energy") if energy is not None: source.energy = Univariate.from_xml_element(energy) - time = elem.find('time') + time = elem.find("time") if time is not None: source.time = Univariate.from_xml_element(time) @@ -526,11 +537,12 @@ class MeshSource(SourceBase): 'fissionable', and 'rejection_strategy'. """ + def __init__( - self, - mesh: MeshBase, - sources: Sequence[SourceBase], - constraints: dict[str, Any] | None = None, + self, + mesh: MeshBase, + sources: Sequence[SourceBase], + constraints: dict[str, Any] | None = None, ): super().__init__(strength=None, constraints=constraints) self.mesh = mesh @@ -554,41 +566,44 @@ def sources(self) -> np.ndarray: @mesh.setter def mesh(self, m): - cv.check_type('source mesh', m, MeshBase) + cv.check_type("source mesh", m, MeshBase) self._mesh = m @sources.setter def sources(self, s): - cv.check_iterable_type('mesh sources', s, SourceBase, max_depth=3) + cv.check_iterable_type("mesh sources", s, SourceBase, max_depth=3) s = np.asarray(s) if isinstance(self.mesh, StructuredMesh): if s.size != self.mesh.num_mesh_cells: raise ValueError( - f'The length of the source array ({s.size}) does not match ' - f'the number of mesh elements ({self.mesh.num_mesh_cells}).') + f"The length of the source array ({s.size}) does not match " + f"the number of mesh elements ({self.mesh.num_mesh_cells})." + ) # If user gave a multidimensional array, flatten in the order # of the mesh indices if s.ndim > 1: - s = s.ravel(order='F') + s = s.ravel(order="F") elif isinstance(self.mesh, UnstructuredMesh): if s.ndim > 1: - raise ValueError('Sources must be a 1-D array for unstructured mesh') + raise ValueError("Sources must be a 1-D array for unstructured mesh") self._sources = s for src in self._sources: if isinstance(src, IndependentSource) and src.space is not None: - warnings.warn('Some sources on the mesh have spatial ' - 'distributions that will be ignored at runtime.') + warnings.warn( + "Some sources on the mesh have spatial " + "distributions that will be ignored at runtime." + ) break @strength.setter def strength(self, val): if val is not None: - cv.check_type('mesh source strength', val, Real) + cv.check_type("mesh source strength", val, Real) self.set_total_strength(val) def set_total_strength(self, strength: float): @@ -642,10 +657,10 @@ def from_xml_element(cls, elem: ET.Element, meshes) -> openmc.MeshSource: openmc.MeshSource MeshSource generated from the XML element """ - mesh_id = int(get_text(elem, 'mesh')) + mesh_id = int(get_text(elem, "mesh")) mesh = meshes[mesh_id] - sources = [SourceBase.from_xml_element(e) for e in elem.iterchildren('source')] + sources = [SourceBase.from_xml_element(e) for e in elem.iterchildren("source")] constraints = cls._get_constraints(elem) return cls(mesh, sources, constraints=constraints) @@ -655,7 +670,9 @@ def Source(*args, **kwargs): A function for backward compatibility of sources. Will be removed in the future. Please update to IndependentSource. """ - warnings.warn("This class is deprecated in favor of 'IndependentSource'", FutureWarning) + warnings.warn( + "This class is deprecated in favor of 'IndependentSource'", FutureWarning + ) return openmc.IndependentSource(*args, **kwargs) @@ -702,12 +719,13 @@ class CompiledSource(SourceBase): 'fissionable', and 'rejection_strategy'. """ + def __init__( self, library: PathLike, parameters: str | None = None, strength: float = 1.0, - constraints: dict[str, Any] | None = None + constraints: dict[str, Any] | None = None, ) -> None: super().__init__(strength=strength, constraints=constraints) self.library = library @@ -725,7 +743,7 @@ def library(self) -> Path: @library.setter def library(self, library_name: PathLike): - cv.check_type('library', library_name, PathLike) + cv.check_type("library", library_name, PathLike) self._library = input_path(library_name) @property @@ -734,7 +752,7 @@ def parameters(self) -> str: @parameters.setter def parameters(self, parameters_path): - cv.check_type('parameters', parameters_path, str) + cv.check_type("parameters", parameters_path, str) self._parameters = parameters_path def populate_xml_element(self, element): @@ -769,16 +787,16 @@ def from_xml_element(cls, elem: ET.Element) -> openmc.CompiledSource: Source generated from XML element """ - kwargs = {'constraints': cls._get_constraints(elem)} - kwargs['library'] = get_text(elem, 'library') + kwargs = {"constraints": cls._get_constraints(elem)} + kwargs["library"] = get_text(elem, "library") source = cls(**kwargs) - strength = get_text(elem, 'strength') + strength = get_text(elem, "strength") if strength is not None: source.strength = float(strength) - parameters = get_text(elem, 'parameters') + parameters = get_text(elem, "parameters") if parameters is not None: source.parameters = parameters @@ -829,7 +847,7 @@ def __init__( self, path: PathLike, strength: float = 1.0, - constraints: dict[str, Any] | None = None + constraints: dict[str, Any] | None = None, ): super().__init__(strength=strength, constraints=constraints) self.path = path @@ -844,7 +862,7 @@ def path(self) -> PathLike: @path.setter def path(self, p: PathLike): - cv.check_type('source file', p, PathLike) + cv.check_type("source file", p, PathLike) self._path = input_path(p) def populate_xml_element(self, element): @@ -877,11 +895,11 @@ def from_xml_element(cls, elem: ET.Element) -> openmc.FileSource: Source generated from XML element """ - kwargs = {'constraints': cls._get_constraints(elem)} - kwargs['path'] = get_text(elem, 'file') - strength = get_text(elem, 'strength') + kwargs = {"constraints": cls._get_constraints(elem)} + kwargs["path"] = get_text(elem, "file") + strength = get_text(elem, "strength") if strength is not None: - kwargs['strength'] = float(strength) + kwargs["strength"] = float(strength) return cls(**kwargs) @@ -891,6 +909,7 @@ class ParticleType(IntEnum): IntEnum class representing a particle type. Type values mirror those found in the C++ class. """ + NEUTRON = 0 PHOTON = 1 ELECTRON = 2 @@ -983,16 +1002,17 @@ class SourceParticle: Type of the particle """ + def __init__( self, - r: Iterable[float] = (0., 0., 0.), - u: Iterable[float] = (0., 0., 1.), + r: Iterable[float] = (0.0, 0.0, 0.0), + u: Iterable[float] = (0.0, 0.0, 1.0), E: float = 1.0e6, time: float = 0.0, wgt: float = 1.0, delayed_group: int = 0, surf_id: int = 0, - particle: ParticleType = ParticleType.NEUTRON + particle: ParticleType = ParticleType.NEUTRON, ): self.r = tuple(r) @@ -1006,7 +1026,7 @@ def __init__( def __repr__(self): name = self.particle.name.lower() - return f'' + return f"" def to_tuple(self) -> tuple: """Return source particle attributes as a tuple @@ -1017,13 +1037,20 @@ def to_tuple(self) -> tuple: Source particle attributes """ - return (self.r, self.u, self.E, self.time, self.wgt, - self.delayed_group, self.surf_id, self.particle.value) + return ( + self.r, + self.u, + self.E, + self.time, + self.wgt, + self.delayed_group, + self.surf_id, + self.particle.value, + ) def write_source_file( - source_particles: Iterable[SourceParticle], - filename: PathLike, **kwargs + source_particles: Iterable[SourceParticle], filename: PathLike, **kwargs ): """Write a source file using a collection of source particles @@ -1055,6 +1082,7 @@ class ParticleList(list): Particles to collect into the list """ + @classmethod def from_hdf5(cls, filename: PathLike) -> ParticleList: """Create particle list from an HDF5 file. @@ -1069,16 +1097,15 @@ def from_hdf5(cls, filename: PathLike) -> ParticleList: ParticleList instance """ - with h5py.File(filename, 'r') as fh: - filetype = fh.attrs['filetype'] - arr = fh['source_bank'][...] + with h5py.File(filename, "r") as fh: + filetype = fh.attrs["filetype"] + arr = fh["source_bank"][...] - if filetype != b'source': - raise ValueError(f'File {filename} is not a source file') + if filetype != b"source": + raise ValueError(f"File {filename} is not a source file") source_particles = [ - SourceParticle(*params, ParticleType(particle)) - for *params, particle in arr + SourceParticle(*params, ParticleType(particle)) for *params, particle in arr ] return cls(source_particles) @@ -1097,6 +1124,7 @@ def from_mcpl(cls, filename: PathLike) -> ParticleList: """ import mcpl + # Process .mcpl file particles = [] with mcpl.MCPLFile(filename) as f: @@ -1112,10 +1140,10 @@ def from_mcpl(cls, filename: PathLike) -> ParticleList: source_particle = SourceParticle( r=tuple(particle.position), u=tuple(particle.direction), - E=1.0e6*particle.ekin, - time=1.0e-3*particle.time, + E=1.0e6 * particle.ekin, + time=1.0e-3 * particle.time, wgt=particle.weight, - particle=particle_type + particle=particle_type, ) particles.append(source_particle) @@ -1149,8 +1177,10 @@ def __getitem__(self, index): # use super() here, so we call list.__getitem__ directly. return ParticleList([list.__getitem__(self, i) for i in index]) else: - raise TypeError(f"Invalid index type: {type(index)}. Must be int, " - "slice, or list of int.") + raise TypeError( + f"Invalid index type: {type(index)}. Must be int, " + "slice, or list of int." + ) def to_dataframe(self) -> pd.DataFrame: """A dataframe representing the source particles @@ -1161,13 +1191,39 @@ def to_dataframe(self) -> pd.DataFrame: DataFrame containing the source particles attributes. """ # Extract the attributes of the source particles into a list of tuples - data = [(sp.r[0], sp.r[1], sp.r[2], sp.u[0], sp.u[1], sp.u[2], - sp.E, sp.time, sp.wgt, sp.delayed_group, sp.surf_id, - sp.particle.name.lower()) for sp in self] + data = [ + ( + sp.r[0], + sp.r[1], + sp.r[2], + sp.u[0], + sp.u[1], + sp.u[2], + sp.E, + sp.time, + sp.wgt, + sp.delayed_group, + sp.surf_id, + sp.particle.name.lower(), + ) + for sp in self + ] # Define the column names for the DataFrame - columns = ['x', 'y', 'z', 'u_x', 'u_y', 'u_z', 'E', 'time', 'wgt', - 'delayed_group', 'surf_id', 'particle'] + columns = [ + "x", + "y", + "z", + "u_x", + "u_y", + "u_z", + "E", + "time", + "wgt", + "delayed_group", + "surf_id", + "particle", + ] # Create the pandas DataFrame from the data return pd.DataFrame(data, columns=columns) @@ -1191,26 +1247,28 @@ def export_to_hdf5(self, filename: PathLike, **kwargs): """ # Create compound datatype for source particles - pos_dtype = np.dtype([('x', ' ParticleList: @@ -1233,10 +1291,10 @@ def read_source_file(filename: PathLike) -> ParticleList: """ filename = Path(filename) - if filename.suffix not in ('.h5', '.mcpl'): - raise ValueError('Source file must have a .h5 or .mcpl extension.') + if filename.suffix not in (".h5", ".mcpl"): + raise ValueError("Source file must have a .h5 or .mcpl extension.") - if filename.suffix == '.h5': + if filename.suffix == ".h5": return ParticleList.from_hdf5(filename) else: return ParticleList.from_mcpl(filename) diff --git a/openmc/statepoint.py b/openmc/statepoint.py index 13aac0caf7e..9fff81d0493 100644 --- a/openmc/statepoint.py +++ b/openmc/statepoint.py @@ -124,14 +124,14 @@ class StatePoint: def __init__(self, filepath, autolink=True): filename = str(filepath) # in case it's a Path - self._f = h5py.File(filename, 'r') + self._f = h5py.File(filename, "r") self._meshes = {} self._filters = {} self._tallies = {} self._derivs = {} # Check filetype and version - cv.check_filetype_version(self._f, 'statepoint', _VERSION_STATEPOINT) + cv.check_filetype_version(self._f, "statepoint", _VERSION_STATEPOINT) # Set flags for what data has been read self._meshes_read = False @@ -144,14 +144,14 @@ def __init__(self, filepath, autolink=True): # Automatically link in a summary file if one exists if autolink: - path_summary = os.path.join(os.path.dirname(filename), 'summary.h5') + path_summary = os.path.join(os.path.dirname(filename), "summary.h5") if os.path.exists(path_summary): su = openmc.Summary(path_summary) self.link_with_summary(su) - path_volume = os.path.join(os.path.dirname(filename), 'volume_*.h5') + path_volume = os.path.join(os.path.dirname(filename), "volume_*.h5") for path_i in glob.glob(path_volume): - if re.search(r'volume_\d+\.h5', path_i): + if re.search(r"volume_\d+\.h5", path_i): vol = openmc.VolumeCalculation.from_hdf5(path_i) self.add_volume_information(vol) @@ -163,56 +163,56 @@ def __exit__(self, *exc): @property def cmfd_on(self): - return self._f.attrs['cmfd_on'] > 0 + return self._f.attrs["cmfd_on"] > 0 @property def cmfd_balance(self): - return self._f['cmfd/cmfd_balance'][()] if self.cmfd_on else None + return self._f["cmfd/cmfd_balance"][()] if self.cmfd_on else None @property def cmfd_dominance(self): - return self._f['cmfd/cmfd_dominance'][()] if self.cmfd_on else None + return self._f["cmfd/cmfd_dominance"][()] if self.cmfd_on else None @property def cmfd_entropy(self): - return self._f['cmfd/cmfd_entropy'][()] if self.cmfd_on else None + return self._f["cmfd/cmfd_entropy"][()] if self.cmfd_on else None @property def cmfd_indices(self): - return self._f['cmfd/indices'][()] if self.cmfd_on else None + return self._f["cmfd/indices"][()] if self.cmfd_on else None @property def cmfd_src(self): if self.cmfd_on: - data = self._f['cmfd/cmfd_src'][()] - return np.reshape(data, tuple(self.cmfd_indices), order='F') + data = self._f["cmfd/cmfd_src"][()] + return np.reshape(data, tuple(self.cmfd_indices), order="F") else: return None @property def cmfd_srccmp(self): - return self._f['cmfd/cmfd_srccmp'][()] if self.cmfd_on else None + return self._f["cmfd/cmfd_srccmp"][()] if self.cmfd_on else None @property def current_batch(self): - return self._f['current_batch'][()] + return self._f["current_batch"][()] @property def date_and_time(self): - s = self._f.attrs['date_and_time'].decode() - return datetime.strptime(s, '%Y-%m-%d %H:%M:%S') + s = self._f.attrs["date_and_time"].decode() + return datetime.strptime(s, "%Y-%m-%d %H:%M:%S") @property def entropy(self): - if self.run_mode == 'eigenvalue': - return self._f['entropy'][()] + if self.run_mode == "eigenvalue": + return self._f["entropy"][()] else: return None @property def filters(self): if not self._filters_read: - filters_group = self._f['tallies/filters'] + filters_group = self._f["tallies/filters"] # Iterate over all Filters for group in filters_group.values(): @@ -225,27 +225,33 @@ def filters(self): @property def generations_per_batch(self): - if self.run_mode == 'eigenvalue': - return self._f['generations_per_batch'][()] + if self.run_mode == "eigenvalue": + return self._f["generations_per_batch"][()] else: return None @property def global_tallies(self): if self._global_tallies is None: - data = self._f['global_tallies'][()] - gt = np.zeros(data.shape[0], dtype=[ - ('name', 'S14'), ('sum', 'f8'), ('sum_sq', 'f8'), - ('mean', 'f8'), ('std_dev', 'f8')]) - gt['name'] = ['k-collision', 'k-absorption', 'k-tracklength', - 'leakage'] - gt['sum'] = data[:,1] - gt['sum_sq'] = data[:,2] + data = self._f["global_tallies"][()] + gt = np.zeros( + data.shape[0], + dtype=[ + ("name", "S14"), + ("sum", "f8"), + ("sum_sq", "f8"), + ("mean", "f8"), + ("std_dev", "f8"), + ], + ) + gt["name"] = ["k-collision", "k-absorption", "k-tracklength", "leakage"] + gt["sum"] = data[:, 1] + gt["sum_sq"] = data[:, 2] # Calculate mean and sample standard deviation of mean n = self.n_realizations - gt['mean'] = gt['sum']/n - gt['std_dev'] = np.sqrt((gt['sum_sq']/n - gt['mean']**2)/(n - 1)) + gt["mean"] = gt["sum"] / n + gt["std_dev"] = np.sqrt((gt["sum_sq"] / n - gt["mean"] ** 2) / (n - 1)) self._global_tallies = gt @@ -254,21 +260,21 @@ def global_tallies(self): @property def k_cmfd(self): if self.cmfd_on: - return self._f['cmfd/k_cmfd'][()] + return self._f["cmfd/k_cmfd"][()] else: return None @property def k_generation(self): - if self.run_mode == 'eigenvalue': - return self._f['k_generation'][()] + if self.run_mode == "eigenvalue": + return self._f["k_generation"][()] else: return None @property def keff(self): - if self.run_mode == 'eigenvalue': - return ufloat(*self._f['k_combined'][()]) + if self.run_mode == "eigenvalue": + return ufloat(*self._f["k_combined"][()]) else: return None @@ -276,35 +282,36 @@ def keff(self): def k_combined(self): warnings.warn( "The 'k_combined' property has been renamed to 'keff' and will be " - "removed in a future version of OpenMC.", FutureWarning + "removed in a future version of OpenMC.", + FutureWarning, ) return self.keff @property def k_col_abs(self): - if self.run_mode == 'eigenvalue': - return self._f['k_col_abs'][()] + if self.run_mode == "eigenvalue": + return self._f["k_col_abs"][()] else: return None @property def k_col_tra(self): - if self.run_mode == 'eigenvalue': - return self._f['k_col_tra'][()] + if self.run_mode == "eigenvalue": + return self._f["k_col_tra"][()] else: return None @property def k_abs_tra(self): - if self.run_mode == 'eigenvalue': - return self._f['k_abs_tra'][()] + if self.run_mode == "eigenvalue": + return self._f["k_abs_tra"][()] else: return None @property def meshes(self): if not self._meshes_read: - mesh_group = self._f['tallies/meshes'] + mesh_group = self._f["tallies/meshes"] # Iterate over all meshes for group in mesh_group.values(): @@ -317,51 +324,50 @@ def meshes(self): @property def n_batches(self): - return self._f['n_batches'][()] + return self._f["n_batches"][()] @property def n_inactive(self): - if self.run_mode == 'eigenvalue': - return self._f['n_inactive'][()] + if self.run_mode == "eigenvalue": + return self._f["n_inactive"][()] else: return None @property def n_particles(self): - return self._f['n_particles'][()] + return self._f["n_particles"][()] @property def n_realizations(self): - return self._f['n_realizations'][()] + return self._f["n_realizations"][()] @property def path(self): - return self._f.attrs['path'].decode() + return self._f.attrs["path"].decode() @property def photon_transport(self): - return self._f.attrs['photon_transport'] > 0 + return self._f.attrs["photon_transport"] > 0 @property def run_mode(self): - return self._f['run_mode'][()].decode() + return self._f["run_mode"][()].decode() @property def runtime(self): - return {name: dataset[()] - for name, dataset in self._f['runtime'].items()} + return {name: dataset[()] for name, dataset in self._f["runtime"].items()} @property def seed(self): - return self._f['seed'][()] + return self._f["seed"][()] @property def source(self): - return self._f['source_bank'][()] if self.source_present else None + return self._f["source_bank"][()] if self.source_present else None @property def source_present(self): - return self._f.attrs['source_present'] > 0 + return self._f.attrs["source_present"] > 0 @property def sparse(self): @@ -379,7 +385,7 @@ def sparse(self, sparse): """ - cv.check_type('sparse', sparse, bool) + cv.check_type("sparse", sparse, bool) self._sparse = sparse # Update tally sparsities @@ -391,23 +397,23 @@ def sparse(self, sparse): def tallies(self): if self.tallies_present and not self._tallies_read: # Read the number of tallies - tallies_group = self._f['tallies'] - n_tallies = tallies_group.attrs['n_tallies'] + tallies_group = self._f["tallies"] + n_tallies = tallies_group.attrs["n_tallies"] # Read a list of the IDs for each Tally if n_tallies > 0: # Tally user-defined IDs - tally_ids = tallies_group.attrs['ids'] + tally_ids = tallies_group.attrs["ids"] else: tally_ids = [] # Ignore warnings about duplicate IDs with warnings.catch_warnings(): - warnings.simplefilter('ignore', openmc.IDWarning) + warnings.simplefilter("ignore", openmc.IDWarning) # Iterate over all tallies for tally_id in tally_ids: - group = tallies_group[f'tally {tally_id}'] + group = tallies_group[f"tally {tally_id}"] # Check if tally is internal and therefore has no data if group.attrs.get("internal"): @@ -416,36 +422,39 @@ def tallies(self): # Create Tally object and assign basic properties tally = openmc.Tally(tally_id) tally._sp_filename = self._f.filename - tally.name = group['name'][()].decode() if 'name' in group else '' + tally.name = group["name"][()].decode() if "name" in group else "" # Check if tally has multiply_density attribute if "multiply_density" in group.attrs: - tally.multiply_density = group.attrs["multiply_density"].item() > 0 + tally.multiply_density = ( + group.attrs["multiply_density"].item() > 0 + ) # Read the number of realizations - n_realizations = group['n_realizations'][()] + n_realizations = group["n_realizations"][()] - tally.estimator = group['estimator'][()].decode() + tally.estimator = group["estimator"][()].decode() tally.num_realizations = n_realizations # Read derivative information. - if 'derivative' in group: - deriv_id = group['derivative'][()] + if "derivative" in group: + deriv_id = group["derivative"][()] tally.derivative = self.tally_derivatives[deriv_id] # Read all filters - n_filters = group['n_filters'][()] + n_filters = group["n_filters"][()] if n_filters > 0: - filter_ids = group['filters'][()] - filters_group = self._f['tallies/filters'] + filter_ids = group["filters"][()] + filters_group = self._f["tallies/filters"] for filter_id in filter_ids: - filter_group = filters_group[f'filter {filter_id}'] + filter_group = filters_group[f"filter {filter_id}"] new_filter = openmc.Filter.from_hdf5( - filter_group, meshes=self.meshes) + filter_group, meshes=self.meshes + ) tally.filters.append(new_filter) # Read nuclide bins - nuclide_names = group['nuclides'][()] + nuclide_names = group["nuclides"][()] # Add all nuclides to the Tally for name in nuclide_names: @@ -453,7 +462,7 @@ def tallies(self): tally.nuclides.append(nuclide) # Add the scores to the Tally - scores = group['score_bins'][()] + scores = group["score_bins"][()] for score in scores: tally.scores.append(score.decode()) @@ -467,29 +476,29 @@ def tallies(self): @property def tallies_present(self): - return self._f.attrs['tallies_present'] > 0 + return self._f.attrs["tallies_present"] > 0 @property def tally_derivatives(self): if not self._derivs_read: # Populate the dictionary if any derivatives are present. - if 'derivatives' in self._f['tallies']: + if "derivatives" in self._f["tallies"]: # Read the derivative ids. - base = 'tallies/derivatives' - deriv_ids = [int(k.split(' ')[1]) for k in self._f[base]] + base = "tallies/derivatives" + deriv_ids = [int(k.split(" ")[1]) for k in self._f[base]] # Create each derivative object and add it to the dictionary. for d_id in deriv_ids: - group = self._f[f'tallies/derivatives/derivative {d_id}'] + group = self._f[f"tallies/derivatives/derivative {d_id}"] deriv = openmc.TallyDerivative(derivative_id=d_id) - deriv.variable = group['independent variable'][()].decode() - if deriv.variable == 'density': - deriv.material = group['material'][()] - elif deriv.variable == 'nuclide_density': - deriv.material = group['material'][()] - deriv.nuclide = group['nuclide'][()].decode() - elif deriv.variable == 'temperature': - deriv.material = group['material'][()] + deriv.variable = group["independent variable"][()].decode() + if deriv.variable == "density": + deriv.material = group["material"][()] + elif deriv.variable == "nuclide_density": + deriv.material = group["material"][()] + deriv.nuclide = group["nuclide"][()].decode() + elif deriv.variable == "temperature": + deriv.material = group["material"][()] self._derivs[d_id] = deriv self._derivs_read = True @@ -498,7 +507,7 @@ def tally_derivatives(self): @property def version(self): - return tuple(self._f.attrs['openmc_version']) + return tuple(self._f.attrs["openmc_version"]) @property def summary(self): @@ -524,9 +533,18 @@ def add_volume_information(self, volume_calc): if self.summary is not None: self.summary.add_volume_information(volume_calc) - def get_tally(self, scores=[], filters=[], nuclides=[], - name=None, id=None, estimator=None, exact_filters=False, - exact_nuclides=False, exact_scores=False): + def get_tally( + self, + scores=[], + filters=[], + nuclides=[], + name=None, + id=None, + estimator=None, + exact_filters=False, + exact_nuclides=False, + exact_scores=False, + ): """Finds and returns a Tally object with certain properties. This routine searches the list of Tallies and returns the first Tally @@ -640,7 +658,7 @@ def get_tally(self, scores=[], filters=[], nuclides=[], # If we did not find the Tally, return an error message if tally is None: - raise LookupError('Unable to get Tally') + raise LookupError("Unable to get Tally") return tally @@ -668,13 +686,14 @@ def link_with_summary(self, summary): """ if self.summary is not None: - warnings.warn('A Summary object has already been linked.', - RuntimeWarning) + warnings.warn("A Summary object has already been linked.", RuntimeWarning) return if not isinstance(summary, openmc.Summary): - msg = f'Unable to link statepoint with "{summary}" which is not a' \ - 'Summary object' + msg = ( + f'Unable to link statepoint with "{summary}" which is not a' + "Summary object" + ) raise ValueError(msg) cells = summary.geometry.get_all_cells() diff --git a/openmc/stats/multivariate.py b/openmc/stats/multivariate.py index cd474fa9b28..9c38e3531f0 100644 --- a/openmc/stats/multivariate.py +++ b/openmc/stats/multivariate.py @@ -32,6 +32,7 @@ class UnitSphere(ABC): Direction from which polar angle is measured """ + def __init__(self, reference_uvw=None): self._reference_uvw = None if reference_uvw is not None: @@ -43,23 +44,23 @@ def reference_uvw(self): @reference_uvw.setter def reference_uvw(self, uvw): - cv.check_type('reference direction', uvw, Iterable, Real) + cv.check_type("reference direction", uvw, Iterable, Real) uvw = np.asarray(uvw) - self._reference_uvw = uvw/np.linalg.norm(uvw) + self._reference_uvw = uvw / np.linalg.norm(uvw) @abstractmethod def to_xml_element(self): - return '' + return "" @classmethod @abstractmethod def from_xml_element(cls, elem): - distribution = get_text(elem, 'type') - if distribution == 'mu-phi': + distribution = get_text(elem, "type") + if distribution == "mu-phi": return PolarAzimuthal.from_xml_element(elem) - elif distribution == 'isotropic': + elif distribution == "isotropic": return Isotropic.from_xml_element(elem) - elif distribution == 'monodirectional': + elif distribution == "monodirectional": return Monodirectional.from_xml_element(elem) @@ -89,17 +90,17 @@ class PolarAzimuthal(UnitSphere): """ - def __init__(self, mu=None, phi=None, reference_uvw=(0., 0., 1.)): + def __init__(self, mu=None, phi=None, reference_uvw=(0.0, 0.0, 1.0)): super().__init__(reference_uvw) if mu is not None: self.mu = mu else: - self.mu = Uniform(-1., 1.) + self.mu = Uniform(-1.0, 1.0) if phi is not None: self.phi = phi else: - self.phi = Uniform(0., 2*pi) + self.phi = Uniform(0.0, 2 * pi) @property def mu(self): @@ -107,7 +108,7 @@ def mu(self): @mu.setter def mu(self, mu): - cv.check_type('cosine of polar angle', mu, Univariate) + cv.check_type("cosine of polar angle", mu, Univariate) self._mu = mu @property @@ -116,7 +117,7 @@ def phi(self): @phi.setter def phi(self, phi): - cv.check_type('azimuthal angle', phi, Univariate) + cv.check_type("azimuthal angle", phi, Univariate) self._phi = phi def to_xml_element(self): @@ -128,12 +129,12 @@ def to_xml_element(self): XML element containing angular distribution data """ - element = ET.Element('angle') + element = ET.Element("angle") element.set("type", "mu-phi") if self.reference_uvw is not None: - element.set("reference_uvw", ' '.join(map(str, self.reference_uvw))) - element.append(self.mu.to_xml_element('mu')) - element.append(self.phi.to_xml_element('phi')) + element.set("reference_uvw", " ".join(map(str, self.reference_uvw))) + element.append(self.mu.to_xml_element("mu")) + element.append(self.phi.to_xml_element("phi")) return element @classmethod @@ -152,11 +153,11 @@ def from_xml_element(cls, elem): """ mu_phi = cls() - uvw = get_text(elem, 'reference_uvw') + uvw = get_text(elem, "reference_uvw") if uvw is not None: mu_phi.reference_uvw = [float(x) for x in uvw.split()] - mu_phi.mu = Univariate.from_xml_element(elem.find('mu')) - mu_phi.phi = Univariate.from_xml_element(elem.find('phi')) + mu_phi.mu = Univariate.from_xml_element(elem.find("mu")) + mu_phi.phi = Univariate.from_xml_element(elem.find("phi")) return mu_phi @@ -175,7 +176,7 @@ def to_xml_element(self): XML element containing isotropic distribution data """ - element = ET.Element('angle') + element = ET.Element("angle") element.set("type", "isotropic") return element @@ -212,7 +213,7 @@ class Monodirectional(UnitSphere): """ - def __init__(self, reference_uvw: Sequence[float] = [1., 0., 0.]): + def __init__(self, reference_uvw: Sequence[float] = [1.0, 0.0, 0.0]): super().__init__(reference_uvw) def to_xml_element(self): @@ -224,10 +225,10 @@ def to_xml_element(self): XML element containing monodirectional distribution data """ - element = ET.Element('angle') + element = ET.Element("angle") element.set("type", "monodirectional") if self.reference_uvw is not None: - element.set("reference_uvw", ' '.join(map(str, self.reference_uvw))) + element.set("reference_uvw", " ".join(map(str, self.reference_uvw))) return element @classmethod @@ -246,7 +247,7 @@ def from_xml_element(cls, elem: ET.Element): """ monodirectional = cls() - uvw = get_text(elem, 'reference_uvw') + uvw = get_text(elem, "reference_uvw") if uvw is not None: monodirectional.reference_uvw = [float(x) for x in uvw.split()] return monodirectional @@ -259,27 +260,28 @@ class Spatial(ABC): distributions of source sites. """ + @abstractmethod def to_xml_element(self): - return '' + return "" @classmethod @abstractmethod def from_xml_element(cls, elem, meshes=None): - distribution = get_text(elem, 'type') - if distribution == 'cartesian': + distribution = get_text(elem, "type") + if distribution == "cartesian": return CartesianIndependent.from_xml_element(elem) - elif distribution == 'cylindrical': + elif distribution == "cylindrical": return CylindricalIndependent.from_xml_element(elem) - elif distribution == 'spherical': + elif distribution == "spherical": return SphericalIndependent.from_xml_element(elem) - elif distribution == 'box' or distribution == 'fission': + elif distribution == "box" or distribution == "fission": return Box.from_xml_element(elem) - elif distribution == 'point': + elif distribution == "point": return Point.from_xml_element(elem) - elif distribution == 'mesh': + elif distribution == "mesh": return MeshSpatial.from_xml_element(elem, meshes) - elif distribution == 'cloud': + elif distribution == "cloud": return PointCloud.from_xml_element(elem) @@ -313,7 +315,7 @@ def __init__( self, x: openmc.stats.Univariate, y: openmc.stats.Univariate, - z: openmc.stats.Univariate + z: openmc.stats.Univariate, ): self.x = x self.y = y @@ -325,7 +327,7 @@ def x(self): @x.setter def x(self, x): - cv.check_type('x coordinate', x, Univariate) + cv.check_type("x coordinate", x, Univariate) self._x = x @property @@ -334,7 +336,7 @@ def y(self): @y.setter def y(self, y): - cv.check_type('y coordinate', y, Univariate) + cv.check_type("y coordinate", y, Univariate) self._y = y @property @@ -343,7 +345,7 @@ def z(self): @z.setter def z(self, z): - cv.check_type('z coordinate', z, Univariate) + cv.check_type("z coordinate", z, Univariate) self._z = z def to_xml_element(self): @@ -355,11 +357,11 @@ def to_xml_element(self): XML element containing spatial distribution data """ - element = ET.Element('space') - element.set('type', 'cartesian') - element.append(self.x.to_xml_element('x')) - element.append(self.y.to_xml_element('y')) - element.append(self.z.to_xml_element('z')) + element = ET.Element("space") + element.set("type", "cartesian") + element.append(self.x.to_xml_element("x")) + element.append(self.y.to_xml_element("y")) + element.append(self.z.to_xml_element("z")) return element @classmethod @@ -377,9 +379,9 @@ def from_xml_element(cls, elem: ET.Element): Spatial distribution generated from XML element """ - x = Univariate.from_xml_element(elem.find('x')) - y = Univariate.from_xml_element(elem.find('y')) - z = Univariate.from_xml_element(elem.find('z')) + x = Univariate.from_xml_element(elem.find("x")) + y = Univariate.from_xml_element(elem.find("y")) + z = Univariate.from_xml_element(elem.find("z")) return cls(x, y, z) @@ -438,7 +440,7 @@ def r(self): @r.setter def r(self, r): - cv.check_type('r coordinate', r, Univariate) + cv.check_type("r coordinate", r, Univariate) self._r = r @property @@ -447,7 +449,7 @@ def cos_theta(self): @cos_theta.setter def cos_theta(self, cos_theta): - cv.check_type('cos_theta coordinate', cos_theta, Univariate) + cv.check_type("cos_theta coordinate", cos_theta, Univariate) self._cos_theta = cos_theta @property @@ -456,7 +458,7 @@ def phi(self): @phi.setter def phi(self, phi): - cv.check_type('phi coordinate', phi, Univariate) + cv.check_type("phi coordinate", phi, Univariate) self._phi = phi @property @@ -465,7 +467,7 @@ def origin(self): @origin.setter def origin(self, origin): - cv.check_type('origin coordinates', origin, Iterable, Real) + cv.check_type("origin coordinates", origin, Iterable, Real) origin = np.asarray(origin) self._origin = origin @@ -478,12 +480,12 @@ def to_xml_element(self): XML element containing spatial distribution data """ - element = ET.Element('space') - element.set('type', 'spherical') - element.append(self.r.to_xml_element('r')) - element.append(self.cos_theta.to_xml_element('cos_theta')) - element.append(self.phi.to_xml_element('phi')) - element.set("origin", ' '.join(map(str, self.origin))) + element = ET.Element("space") + element.set("type", "spherical") + element.append(self.r.to_xml_element("r")) + element.append(self.cos_theta.to_xml_element("cos_theta")) + element.append(self.phi.to_xml_element("phi")) + element.set("origin", " ".join(map(str, self.origin))) return element @classmethod @@ -501,10 +503,10 @@ def from_xml_element(cls, elem: ET.Element): Spatial distribution generated from XML element """ - r = Univariate.from_xml_element(elem.find('r')) - cos_theta = Univariate.from_xml_element(elem.find('cos_theta')) - phi = Univariate.from_xml_element(elem.find('phi')) - origin = [float(x) for x in elem.get('origin').split()] + r = Univariate.from_xml_element(elem.find("r")) + cos_theta = Univariate.from_xml_element(elem.find("cos_theta")) + phi = Univariate.from_xml_element(elem.find("phi")) + origin = [float(x) for x in elem.get("origin").split()] return cls(r, cos_theta, phi, origin=origin) @@ -560,7 +562,7 @@ def r(self): @r.setter def r(self, r): - cv.check_type('r coordinate', r, Univariate) + cv.check_type("r coordinate", r, Univariate) self._r = r @property @@ -569,7 +571,7 @@ def phi(self): @phi.setter def phi(self, phi): - cv.check_type('phi coordinate', phi, Univariate) + cv.check_type("phi coordinate", phi, Univariate) self._phi = phi @property @@ -578,7 +580,7 @@ def z(self): @z.setter def z(self, z): - cv.check_type('z coordinate', z, Univariate) + cv.check_type("z coordinate", z, Univariate) self._z = z @property @@ -587,7 +589,7 @@ def origin(self): @origin.setter def origin(self, origin): - cv.check_type('origin coordinates', origin, Iterable, Real) + cv.check_type("origin coordinates", origin, Iterable, Real) origin = np.asarray(origin) self._origin = origin @@ -600,12 +602,12 @@ def to_xml_element(self): XML element containing spatial distribution data """ - element = ET.Element('space') - element.set('type', 'cylindrical') - element.append(self.r.to_xml_element('r')) - element.append(self.phi.to_xml_element('phi')) - element.append(self.z.to_xml_element('z')) - element.set("origin", ' '.join(map(str, self.origin))) + element = ET.Element("space") + element.set("type", "cylindrical") + element.append(self.r.to_xml_element("r")) + element.append(self.phi.to_xml_element("phi")) + element.append(self.z.to_xml_element("z")) + element.set("origin", " ".join(map(str, self.origin))) return element @classmethod @@ -623,10 +625,10 @@ def from_xml_element(cls, elem: ET.Element): Spatial distribution generated from XML element """ - r = Univariate.from_xml_element(elem.find('r')) - phi = Univariate.from_xml_element(elem.find('phi')) - z = Univariate.from_xml_element(elem.find('z')) - origin = [float(x) for x in elem.get('origin').split()] + r = Univariate.from_xml_element(elem.find("r")) + phi = Univariate.from_xml_element(elem.find("phi")) + z = Univariate.from_xml_element(elem.find("z")) + origin = [float(x) for x in elem.get("origin").split()] return cls(r, phi, z, origin=origin) @@ -673,7 +675,7 @@ def mesh(self): @mesh.setter def mesh(self, mesh): if mesh is not None: - cv.check_type('mesh instance', mesh, MeshBase) + cv.check_type("mesh instance", mesh, MeshBase) self._mesh = mesh @property @@ -682,7 +684,7 @@ def volume_normalized(self): @volume_normalized.setter def volume_normalized(self, volume_normalized): - cv.check_type('Multiply strengths by element volumes', volume_normalized, bool) + cv.check_type("Multiply strengths by element volumes", volume_normalized, bool) self._volume_normalized = volume_normalized @property @@ -692,7 +694,7 @@ def strengths(self): @strengths.setter def strengths(self, given_strengths): if given_strengths is not None: - cv.check_type('strengths array passed in', given_strengths, Iterable, Real) + cv.check_type("strengths array passed in", given_strengths, Iterable, Real) self._strengths = np.asarray(given_strengths, dtype=float).flatten() else: self._strengths = None @@ -700,7 +702,7 @@ def strengths(self, given_strengths): @property def num_strength_bins(self): if self.strengths is None: - raise ValueError('Strengths are not set') + raise ValueError("Strengths are not set") return self.strengths.size def to_xml_element(self): @@ -712,15 +714,15 @@ def to_xml_element(self): XML element containing spatial distribution data """ - element = ET.Element('space') + element = ET.Element("space") - element.set('type', 'mesh') + element.set("type", "mesh") element.set("mesh_id", str(self.mesh.id)) element.set("volume_normalized", str(self.volume_normalized)) if self.strengths is not None: - subelement = ET.SubElement(element, 'strengths') - subelement.text = ' '.join(str(e) for e in self.strengths) + subelement = ET.SubElement(element, "strengths") + subelement.text = " ".join(str(e) for e in self.strengths) return element @@ -743,17 +745,17 @@ def from_xml_element(cls, elem, meshes): """ - mesh_id = int(elem.get('mesh_id')) + mesh_id = int(elem.get("mesh_id")) # check if this mesh has been read in from another location already if mesh_id not in meshes: raise ValueError(f'Could not locate mesh with ID "{mesh_id}"') volume_normalized = elem.get("volume_normalized") - volume_normalized = get_text(elem, 'volume_normalized').lower() == 'true' - strengths = get_text(elem, 'strengths') + volume_normalized = get_text(elem, "volume_normalized").lower() == "true" + strengths = get_text(elem, "strengths") if strengths is not None: - strengths = [float(b) for b in get_text(elem, 'strengths').split()] + strengths = [float(b) for b in get_text(elem, "strengths").split()] return cls(meshes[mesh_id], strengths, volume_normalized) @@ -785,7 +787,7 @@ class PointCloud(Spatial): def __init__( self, positions: Sequence[Sequence[float]], - strengths: Sequence[float] | None = None + strengths: Sequence[float] | None = None, ): self.positions = positions self.strengths = strengths @@ -798,9 +800,9 @@ def positions(self) -> np.ndarray: def positions(self, positions): positions = np.array(positions, dtype=float) if positions.ndim != 2: - raise ValueError('positions must be a 2D array') + raise ValueError("positions must be a 2D array") elif positions.shape[1] != 3: - raise ValueError('Each position must have 3 values') + raise ValueError("Each position must have 3 values") self._positions = positions @property @@ -812,15 +814,15 @@ def strengths(self, strengths): if strengths is not None: strengths = np.array(strengths, dtype=float) if strengths.ndim != 1: - raise ValueError('strengths must be a 1D array') + raise ValueError("strengths must be a 1D array") elif strengths.size != self.positions.shape[0]: - raise ValueError('strengths must have the same length as positions') + raise ValueError("strengths must have the same length as positions") self._strengths = strengths @property def num_strength_bins(self) -> int: if self.strengths is None: - raise ValueError('Strengths are not set') + raise ValueError("Strengths are not set") return self.strengths.size def to_xml_element(self) -> ET.Element: @@ -832,15 +834,15 @@ def to_xml_element(self) -> ET.Element: XML element containing spatial distribution data """ - element = ET.Element('space') - element.set('type', 'cloud') + element = ET.Element("space") + element.set("type", "cloud") - subelement = ET.SubElement(element, 'coords') - subelement.text = ' '.join(str(e) for e in self.positions.flatten()) + subelement = ET.SubElement(element, "coords") + subelement.text = " ".join(str(e) for e in self.positions.flatten()) if self.strengths is not None: - subelement = ET.SubElement(element, 'strengths') - subelement.text = ' '.join(str(e) for e in self.strengths) + subelement = ET.SubElement(element, "strengths") + subelement.text = " ".join(str(e) for e in self.strengths) return element @@ -860,10 +862,10 @@ def from_xml_element(cls, elem: ET.Element) -> PointCloud: """ - coord_data = get_text(elem, 'coords') + coord_data = get_text(elem, "coords") positions = np.array([float(b) for b in coord_data.split()]).reshape((-1, 3)) - strengths = get_text(elem, 'strengths') + strengths = get_text(elem, "strengths") if strengths is not None: strengths = [float(b) for b in strengths.split()] @@ -905,7 +907,7 @@ def __init__( self, lower_left: Sequence[float], upper_right: Sequence[float], - only_fissionable: bool = False + only_fissionable: bool = False, ): self.lower_left = lower_left self.upper_right = upper_right @@ -917,8 +919,8 @@ def lower_left(self): @lower_left.setter def lower_left(self, lower_left): - cv.check_type('lower left coordinate', lower_left, Iterable, Real) - cv.check_length('lower left coordinate', lower_left, 3) + cv.check_type("lower left coordinate", lower_left, Iterable, Real) + cv.check_length("lower left coordinate", lower_left, 3) self._lower_left = lower_left @property @@ -927,8 +929,8 @@ def upper_right(self): @upper_right.setter def upper_right(self, upper_right): - cv.check_type('upper right coordinate', upper_right, Iterable, Real) - cv.check_length('upper right coordinate', upper_right, 3) + cv.check_type("upper right coordinate", upper_right, Iterable, Real) + cv.check_length("upper right coordinate", upper_right, 3) self._upper_right = upper_right @property @@ -937,12 +939,14 @@ def only_fissionable(self): @only_fissionable.setter def only_fissionable(self, only_fissionable): - cv.check_type('only fissionable', only_fissionable, bool) + cv.check_type("only fissionable", only_fissionable, bool) self._only_fissionable = only_fissionable if only_fissionable: - warn("The 'only_fissionable' has been deprecated. Use the " - "'constraints' argument when defining a source instead.", - FutureWarning) + warn( + "The 'only_fissionable' has been deprecated. Use the " + "'constraints' argument when defining a source instead.", + FutureWarning, + ) def to_xml_element(self): """Return XML representation of the box distribution @@ -953,14 +957,17 @@ def to_xml_element(self): XML element containing box distribution data """ - element = ET.Element('space') + element = ET.Element("space") if self.only_fissionable: element.set("type", "fission") else: element.set("type", "box") params = ET.SubElement(element, "parameters") - params.text = ' '.join(map(str, self.lower_left)) + ' ' + \ - ' '.join(map(str, self.upper_right)) + params.text = ( + " ".join(map(str, self.lower_left)) + + " " + + " ".join(map(str, self.upper_right)) + ) return element @classmethod @@ -978,10 +985,10 @@ def from_xml_element(cls, elem: ET.Element): Box distribution generated from XML element """ - only_fissionable = get_text(elem, 'type') == 'fission' - params = [float(x) for x in get_text(elem, 'parameters').split()] - lower_left = params[:len(params)//2] - upper_right = params[len(params)//2:] + only_fissionable = get_text(elem, "type") == "fission" + params = [float(x) for x in get_text(elem, "parameters").split()] + lower_left = params[: len(params) // 2] + upper_right = params[len(params) // 2 :] return cls(lower_left, upper_right, only_fissionable) @@ -1003,7 +1010,7 @@ class Point(Spatial): """ - def __init__(self, xyz: Sequence[float] = (0., 0., 0.)): + def __init__(self, xyz: Sequence[float] = (0.0, 0.0, 0.0)): self.xyz = xyz @property @@ -1012,8 +1019,8 @@ def xyz(self): @xyz.setter def xyz(self, xyz): - cv.check_type('coordinate', xyz, Iterable, Real) - cv.check_length('coordinate', xyz, 3) + cv.check_type("coordinate", xyz, Iterable, Real) + cv.check_length("coordinate", xyz, 3) self._xyz = xyz def to_xml_element(self): @@ -1025,10 +1032,10 @@ def to_xml_element(self): XML element containing point distribution location """ - element = ET.Element('space') + element = ET.Element("space") element.set("type", "point") params = ET.SubElement(element, "parameters") - params.text = ' '.join(map(str, self.xyz)) + params.text = " ".join(map(str, self.xyz)) return element @classmethod @@ -1046,17 +1053,17 @@ def from_xml_element(cls, elem: ET.Element): Point distribution generated from XML element """ - xyz = [float(x) for x in get_text(elem, 'parameters').split()] + xyz = [float(x) for x in get_text(elem, "parameters").split()] return cls(xyz) def spherical_uniform( - r_outer: float, - r_inner: float = 0.0, - thetas: Sequence[float] = (0., pi), - phis: Sequence[float] = (0., 2*pi), - origin: Sequence[float] = (0., 0., 0.) - ): + r_outer: float, + r_inner: float = 0.0, + thetas: Sequence[float] = (0.0, pi), + phis: Sequence[float] = (0.0, 2 * pi), + origin: Sequence[float] = (0.0, 0.0, 0.0), +): """Return a uniform spatial distribution over a spherical shell. This function provides a uniform spatial distribution over a spherical diff --git a/openmc/stats/univariate.py b/openmc/stats/univariate.py index 0dc6f385685..5b436603906 100644 --- a/openmc/stats/univariate.py +++ b/openmc/stats/univariate.py @@ -16,11 +16,11 @@ from ..mixin import EqualityMixin _INTERPOLATION_SCHEMES = { - 'histogram', - 'linear-linear', - 'linear-log', - 'log-linear', - 'log-log' + "histogram", + "linear-linear", + "linear-log", + "log-linear", + "log-log", } @@ -31,9 +31,10 @@ class Univariate(EqualityMixin, ABC): specific probability distribution. """ + @abstractmethod def to_xml_element(self, element_name): - return '' + return "" @abstractmethod def __len__(self): @@ -42,28 +43,28 @@ def __len__(self): @classmethod @abstractmethod def from_xml_element(cls, elem): - distribution = get_text(elem, 'type') - if distribution == 'discrete': + distribution = get_text(elem, "type") + if distribution == "discrete": return Discrete.from_xml_element(elem) - elif distribution == 'uniform': + elif distribution == "uniform": return Uniform.from_xml_element(elem) - elif distribution == 'powerlaw': + elif distribution == "powerlaw": return PowerLaw.from_xml_element(elem) - elif distribution == 'maxwell': + elif distribution == "maxwell": return Maxwell.from_xml_element(elem) - elif distribution == 'watt': + elif distribution == "watt": return Watt.from_xml_element(elem) - elif distribution == 'normal': + elif distribution == "normal": return Normal.from_xml_element(elem) - elif distribution == 'muir': + elif distribution == "muir": # Support older files where Muir had its own class - params = [float(x) for x in get_text(elem, 'parameters').split()] + params = [float(x) for x in get_text(elem, "parameters").split()] return muir(*params) - elif distribution == 'tabular': + elif distribution == "tabular": return Tabular.from_xml_element(elem) - elif distribution == 'legendre': + elif distribution == "legendre": return Legendre.from_xml_element(elem) - elif distribution == 'mixture': + elif distribution == "mixture": return Mixture.from_xml_element(elem) @abstractmethod @@ -129,7 +130,7 @@ def _intensity_clip(intensity: Sequence[float], tolerance: float = 1e-6) -> np.n index_cutoff = np.searchsorted(cumsum, 1.0 - tolerance) # Now get indices up to cutoff - new_indices = index_sort[:index_cutoff + 1] + new_indices = index_sort[: index_cutoff + 1] # Put back in the order of the original array and return new_indices.sort() @@ -174,7 +175,7 @@ def x(self): def x(self, x): if isinstance(x, Real): x = [x] - cv.check_type('discrete values', x, Iterable, Real) + cv.check_type("discrete values", x, Iterable, Real) self._x = np.array(x, dtype=float) @property @@ -185,9 +186,9 @@ def p(self): def p(self, p): if isinstance(p, Real): p = [p] - cv.check_type('discrete probabilities', p, Iterable, Real) + cv.check_type("discrete probabilities", p, Iterable, Real) for pk in p: - cv.check_greater_than('discrete probability', pk, 0.0, True) + cv.check_greater_than("discrete probability", pk, 0.0, True) self._p = np.array(p, dtype=float) def cdf(self): @@ -221,7 +222,7 @@ def to_xml_element(self, element_name): element.set("type", "discrete") params = ET.SubElement(element, "parameters") - params.text = ' '.join(map(str, self.x)) + ' ' + ' '.join(map(str, self.p)) + params.text = " ".join(map(str, self.x)) + " " + " ".join(map(str, self.p)) return element @@ -240,17 +241,13 @@ def from_xml_element(cls, elem: ET.Element): Discrete distribution generated from XML element """ - params = [float(x) for x in get_text(elem, 'parameters').split()] - x = params[:len(params)//2] - p = params[len(params)//2:] + params = [float(x) for x in get_text(elem, "parameters").split()] + x = params[: len(params) // 2] + p = params[len(params) // 2 :] return cls(x, p) @classmethod - def merge( - cls, - dists: Sequence[Discrete], - probs: Sequence[int] - ): + def merge(cls, dists: Sequence[Discrete], probs: Sequence[int]): """Merge multiple discrete distributions into a single distribution .. versionadded:: 0.13.1 @@ -277,7 +274,7 @@ def merge( for dist, p_dist in zip(dists, probs): for x, p in zip(dist.x, dist.p): x_merged.add(x) - p_merged[x] += p*p_dist + p_merged[x] += p * p_dist # Create values and probabilities as arrays x_arr = np.array(sorted(x_merged)) @@ -392,7 +389,7 @@ def a(self): @a.setter def a(self, a): - cv.check_type('Uniform a', a, Real) + cv.check_type("Uniform a", a, Real) self._a = a @property @@ -401,13 +398,13 @@ def b(self): @b.setter def b(self, b): - cv.check_type('Uniform b', b, Real) + cv.check_type("Uniform b", b, Real) self._b = b def to_tabular(self): - prob = 1./(self.b - self.a) - t = Tabular([self.a, self.b], [prob, prob], 'histogram') - t.c = [0., 1.] + prob = 1.0 / (self.b - self.a) + t = Tabular([self.a, self.b], [prob, prob], "histogram") + t.c = [0.0, 1.0] return t def sample(self, n_samples=1, seed=None): @@ -430,7 +427,7 @@ def to_xml_element(self, element_name: str): """ element = ET.Element(element_name) element.set("type", "uniform") - element.set("parameters", f'{self.a} {self.b}') + element.set("parameters", f"{self.a} {self.b}") return element @classmethod @@ -448,7 +445,7 @@ def from_xml_element(cls, elem: ET.Element): Uniform distribution generated from XML element """ - params = get_text(elem, 'parameters').split() + params = get_text(elem, "parameters").split() return cls(*map(float, params)) @@ -480,7 +477,7 @@ class PowerLaw(Univariate): """ - def __init__(self, a: float = 0.0, b: float = 1.0, n: float = 0.): + def __init__(self, a: float = 0.0, b: float = 1.0, n: float = 0.0): self.a = a self.b = b self.n = n @@ -494,7 +491,7 @@ def a(self): @a.setter def a(self, a): - cv.check_type('interval lower bound', a, Real) + cv.check_type("interval lower bound", a, Real) self._a = a @property @@ -503,7 +500,7 @@ def b(self): @b.setter def b(self, b): - cv.check_type('interval upper bound', b, Real) + cv.check_type("interval upper bound", b, Real) self._b = b @property @@ -512,7 +509,7 @@ def n(self): @n.setter def n(self, n): - cv.check_type('power law exponent', n, Real) + cv.check_type("power law exponent", n, Real) self._n = n def sample(self, n_samples=1, seed=None): @@ -521,7 +518,7 @@ def sample(self, n_samples=1, seed=None): pwr = self.n + 1 offset = self.a**pwr span = self.b**pwr - offset - return np.power(offset + xi * span, 1/pwr) + return np.power(offset + xi * span, 1 / pwr) def to_xml_element(self, element_name: str): """Return XML representation of the power law distribution @@ -539,7 +536,7 @@ def to_xml_element(self, element_name: str): """ element = ET.Element(element_name) element.set("type", "powerlaw") - element.set("parameters", f'{self.a} {self.b} {self.n}') + element.set("parameters", f"{self.a} {self.b} {self.n}") return element @classmethod @@ -557,7 +554,7 @@ def from_xml_element(cls, elem: ET.Element): Distribution generated from XML element """ - params = get_text(elem, 'parameters').split() + params = get_text(elem, "parameters").split() return cls(*map(float, params)) @@ -592,8 +589,8 @@ def theta(self): @theta.setter def theta(self, theta): - cv.check_type('Maxwell temperature', theta, Real) - cv.check_greater_than('Maxwell temperature', theta, 0.0) + cv.check_type("Maxwell temperature", theta, Real) + cv.check_greater_than("Maxwell temperature", theta, 0.0) self._theta = theta def sample(self, n_samples=1, seed=None): @@ -642,7 +639,7 @@ def from_xml_element(cls, elem: ET.Element): Maxwellian distribution generated from XML element """ - theta = float(get_text(elem, 'parameters')) + theta = float(get_text(elem, "parameters")) return cls(theta) @@ -682,8 +679,8 @@ def a(self): @a.setter def a(self, a): - cv.check_type('Watt a', a, Real) - cv.check_greater_than('Watt a', a, 0.0) + cv.check_type("Watt a", a, Real) + cv.check_greater_than("Watt a", a, 0.0) self._a = a @property @@ -692,16 +689,16 @@ def b(self): @b.setter def b(self, b): - cv.check_type('Watt b', b, Real) - cv.check_greater_than('Watt b', b, 0.0) + cv.check_type("Watt b", b, Real) + cv.check_greater_than("Watt b", b, 0.0) self._b = b def sample(self, n_samples=1, seed=None): rng = np.random.RandomState(seed) w = Maxwell.sample_maxwell(self.a, n_samples, rng=rng) - u = rng.uniform(-1., 1., n_samples) + u = rng.uniform(-1.0, 1.0, n_samples) aab = self.a * self.a * self.b - return w + 0.25*aab + u*np.sqrt(aab*w) + return w + 0.25 * aab + u * np.sqrt(aab * w) def to_xml_element(self, element_name: str): """Return XML representation of the Watt distribution @@ -719,7 +716,7 @@ def to_xml_element(self, element_name: str): """ element = ET.Element(element_name) element.set("type", "watt") - element.set("parameters", f'{self.a} {self.b}') + element.set("parameters", f"{self.a} {self.b}") return element @classmethod @@ -737,7 +734,7 @@ def from_xml_element(cls, elem: ET.Element): Watt distribution generated from XML element """ - params = get_text(elem, 'parameters').split() + params = get_text(elem, "parameters").split() return cls(*map(float, params)) @@ -776,7 +773,7 @@ def mean_value(self): @mean_value.setter def mean_value(self, mean_value): - cv.check_type('Normal mean_value', mean_value, Real) + cv.check_type("Normal mean_value", mean_value, Real) self._mean_value = mean_value @property @@ -785,8 +782,8 @@ def std_dev(self): @std_dev.setter def std_dev(self, std_dev): - cv.check_type('Normal std_dev', std_dev, Real) - cv.check_greater_than('Normal std_dev', std_dev, 0.0) + cv.check_type("Normal std_dev", std_dev, Real) + cv.check_greater_than("Normal std_dev", std_dev, 0.0) self._std_dev = std_dev def sample(self, n_samples=1, seed=None): @@ -809,7 +806,7 @@ def to_xml_element(self, element_name: str): """ element = ET.Element(element_name) element.set("type", "normal") - element.set("parameters", f'{self.mean_value} {self.std_dev}') + element.set("parameters", f"{self.mean_value} {self.std_dev}") return element @classmethod @@ -827,7 +824,7 @@ def from_xml_element(cls, elem: ET.Element): Normal distribution generated from XML element """ - params = get_text(elem, 'parameters').split() + params = get_text(elem, "parameters").split() return cls(*map(float, params)) @@ -867,7 +864,7 @@ def Muir(*args, **kwargs): warn( "The Muir(...) class has been replaced by the muir(...) function and " "will be removed in a future version of OpenMC. Use muir(...) instead.", - FutureWarning + FutureWarning, ) return muir(*args, **kwargs) @@ -915,29 +912,31 @@ class Tabular(Univariate): """ def __init__( - self, - x: Sequence[float], - p: Sequence[float], - interpolation: str = 'linear-linear', - ignore_negative: bool = False - ): + self, + x: Sequence[float], + p: Sequence[float], + interpolation: str = "linear-linear", + ignore_negative: bool = False, + ): self.interpolation = interpolation - cv.check_type('tabulated values', x, Iterable, Real) - cv.check_type('tabulated probabilities', p, Iterable, Real) + cv.check_type("tabulated values", x, Iterable, Real) + cv.check_type("tabulated probabilities", p, Iterable, Real) x = np.array(x, dtype=float) p = np.array(p, dtype=float) if p.size > x.size: - raise ValueError('Number of probabilities exceeds number of table values.') - if self.interpolation != 'histogram' and x.size != p.size: - raise ValueError(f'Tabulated values ({x.size}) and probabilities ' - f'({p.size}) should have the same length') + raise ValueError("Number of probabilities exceeds number of table values.") + if self.interpolation != "histogram" and x.size != p.size: + raise ValueError( + f"Tabulated values ({x.size}) and probabilities " + f"({p.size}) should have the same length" + ) if not ignore_negative: for pk in p: - cv.check_greater_than('tabulated probability', pk, 0.0, True) + cv.check_greater_than("tabulated probability", pk, 0.0, True) self._x = x self._p = p @@ -959,7 +958,7 @@ def interpolation(self): @interpolation.setter def interpolation(self, interpolation): - cv.check_value('interpolation', interpolation, _INTERPOLATION_SCHEMES) + cv.check_value("interpolation", interpolation, _INTERPOLATION_SCHEMES) self._interpolation = interpolation def cdf(self): @@ -967,44 +966,47 @@ def cdf(self): x = self.x p = self.p - if self.interpolation == 'histogram': - c[1:] = p[:x.size-1] * np.diff(x) - elif self.interpolation == 'linear-linear': + if self.interpolation == "histogram": + c[1:] = p[: x.size - 1] * np.diff(x) + elif self.interpolation == "linear-linear": c[1:] = 0.5 * (p[:-1] + p[1:]) * np.diff(x) else: - raise NotImplementedError('Can only generate CDFs for tabular ' - 'distributions using histogram or ' - 'linear-linear interpolation') - + raise NotImplementedError( + "Can only generate CDFs for tabular " + "distributions using histogram or " + "linear-linear interpolation" + ) return np.cumsum(c) def mean(self): """Compute the mean of the tabular distribution""" - if self.interpolation == 'linear-linear': + if self.interpolation == "linear-linear": mean = 0.0 for i in range(1, len(self.x)): - y_min = self.p[i-1] + y_min = self.p[i - 1] y_max = self.p[i] - x_min = self.x[i-1] + x_min = self.x[i - 1] x_max = self.x[i] m = (y_max - y_min) / (x_max - x_min) - exp_val = (1./3.) * m * (x_max**3 - x_min**3) + exp_val = (1.0 / 3.0) * m * (x_max**3 - x_min**3) exp_val += 0.5 * m * x_min * (x_min**2 - x_max**2) exp_val += 0.5 * y_min * (x_max**2 - x_min**2) mean += exp_val - elif self.interpolation == 'histogram': + elif self.interpolation == "histogram": x_l = self.x[:-1] x_r = self.x[1:] - p_l = self.p[:self.x.size-1] + p_l = self.p[: self.x.size - 1] mean = (0.5 * (x_l + x_r) * (x_r - x_l) * p_l).sum() else: - raise NotImplementedError('Can only compute mean for tabular ' - 'distributions using histogram ' - 'or linear-linear interpolation.') + raise NotImplementedError( + "Can only compute mean for tabular " + "distributions using histogram " + "or linear-linear interpolation." + ) # Normalize for when integral of distribution is not 1 mean /= self.integral() @@ -1039,19 +1041,20 @@ def sample(self, n_samples: int = 1, seed: int | None = None): x_i = self.x[cdf_idx] p_i = p[cdf_idx] - if self.interpolation == 'histogram': + if self.interpolation == "histogram": # mask where probability is greater than zero pos_mask = p_i > 0.0 # probabilities greater than zero are set proportional to the # position of the random numebers in relation to the cdf value - p_i[pos_mask] = x_i[pos_mask] + (xi[pos_mask] - c_i[pos_mask]) \ - / p_i[pos_mask] + p_i[pos_mask] = ( + x_i[pos_mask] + (xi[pos_mask] - c_i[pos_mask]) / p_i[pos_mask] + ) # probabilities smaller than zero are set to the random number value p_i[~pos_mask] = x_i[~pos_mask] samples_out = p_i - elif self.interpolation == 'linear-linear': + elif self.interpolation == "linear-linear": # get variable and probability values for the # next entry x_i1 = self.x[cdf_idx + 1] @@ -1063,15 +1066,19 @@ def sample(self, n_samples: int = 1, seed: int | None = None): m[zero] = x_i[zero] + (xi[zero] - c_i[zero]) / p_i[zero] # set values for non-zero slope non_zero = ~zero - quad = np.power(p_i[non_zero], 2) + 2.0 * m[non_zero] * (xi[non_zero] - c_i[non_zero]) + quad = np.power(p_i[non_zero], 2) + 2.0 * m[non_zero] * ( + xi[non_zero] - c_i[non_zero] + ) quad[quad < 0.0] = 0.0 m[non_zero] = x_i[non_zero] + (np.sqrt(quad) - p_i[non_zero]) / m[non_zero] samples_out = m else: - raise NotImplementedError('Can only sample tabular distributions ' - 'using histogram or ' - 'linear-linear interpolation') + raise NotImplementedError( + "Can only sample tabular distributions " + "using histogram or " + "linear-linear interpolation" + ) assert all(samples_out < self.x[-1]) return samples_out @@ -1095,7 +1102,7 @@ def to_xml_element(self, element_name: str): element.set("interpolation", self.interpolation) params = ET.SubElement(element, "parameters") - params.text = ' '.join(map(str, self.x)) + ' ' + ' '.join(map(str, self.p)) + params.text = " ".join(map(str, self.x)) + " " + " ".join(map(str, self.p)) return element @@ -1114,10 +1121,10 @@ def from_xml_element(cls, elem: ET.Element): Tabular distribution generated from XML element """ - interpolation = get_text(elem, 'interpolation') - params = [float(x) for x in get_text(elem, 'parameters').split()] - x = params[:len(params)//2] - p = params[len(params)//2:] + interpolation = get_text(elem, "interpolation") + params = [float(x) for x in get_text(elem, "parameters").split()] + x = params[: len(params) // 2] + p = params[len(params) // 2 :] return cls(x, p, interpolation) def integral(self): @@ -1130,13 +1137,14 @@ def integral(self): float Integral of tabular distrbution """ - if self.interpolation == 'histogram': - return np.sum(np.diff(self.x) * self.p[:self.x.size-1]) - elif self.interpolation == 'linear-linear': + if self.interpolation == "histogram": + return np.sum(np.diff(self.x) * self.p[: self.x.size - 1]) + elif self.interpolation == "linear-linear": return trapezoid(self.p, self.x) else: raise NotImplementedError( - f'integral() not supported for {self.inteprolation} interpolation') + f"integral() not supported for {self.inteprolation} interpolation" + ) class Legendre(Univariate): @@ -1165,7 +1173,7 @@ def __call__(self, x): # Create Legendre polynomial if we haven't yet if self._legendre_poly is None: l = np.arange(len(self._coefficients)) - coeffs = (2.*l + 1.)/2. * self._coefficients + coeffs = (2.0 * l + 1.0) / 2.0 * self._coefficients self._legendre_poly = np.polynomial.Legendre(coeffs) return self._legendre_poly(x) @@ -1212,9 +1220,7 @@ class Mixture(Univariate): """ def __init__( - self, - probability: Sequence[float], - distribution: Sequence[Univariate] + self, probability: Sequence[float], distribution: Sequence[Univariate] ): self.probability = probability self.distribution = distribution @@ -1228,11 +1234,9 @@ def probability(self): @probability.setter def probability(self, probability): - cv.check_type('mixture distribution probabilities', probability, - Iterable, Real) + cv.check_type("mixture distribution probabilities", probability, Iterable, Real) for p in probability: - cv.check_greater_than('mixture distribution probabilities', - p, 0.0, True) + cv.check_greater_than("mixture distribution probabilities", p, 0.0, True) self._probability = np.array(probability, dtype=float) @property @@ -1241,8 +1245,9 @@ def distribution(self): @distribution.setter def distribution(self, distribution): - cv.check_type('mixture distribution components', distribution, - Iterable, Univariate) + cv.check_type( + "mixture distribution components", distribution, Iterable, Univariate + ) self._distribution = distribution def cdf(self): @@ -1252,8 +1257,12 @@ def sample(self, n_samples=1, seed=None): rng = np.random.RandomState(seed) # Get probability of each distribution accounting for its intensity - p = np.array([prob*dist.integral() for prob, dist in - zip(self.probability, self.distribution)]) + p = np.array( + [ + prob * dist.integral() + for prob, dist in zip(self.probability, self.distribution) + ] + ) p /= p.sum() # Sample from the distributions @@ -1292,9 +1301,9 @@ def to_xml_element(self, element_name: str): element.set("type", "mixture") for p, d in zip(self.probability, self.distribution): - data = ET.SubElement(element, "pair") - data.set("probability", str(p)) - data.append(d.to_xml_element("dist")) + data = ET.SubElement(element, "pair") + data.set("probability", str(p)) + data.append(d.to_xml_element("dist")) return element @@ -1317,8 +1326,8 @@ def from_xml_element(cls, elem: ET.Element): """ probability = [] distribution = [] - for pair in elem.findall('pair'): - probability.append(float(get_text(pair, 'probability'))) + for pair in elem.findall("pair"): + probability.append(float(get_text(pair, "probability"))) distribution.append(Univariate.from_xml_element(pair.find("dist"))) return cls(probability, distribution) @@ -1333,10 +1342,12 @@ def integral(self): float Integral of the distribution """ - return sum([ - p*dist.integral() - for p, dist in zip(self.probability, self.distribution) - ]) + return sum( + [ + p * dist.integral() + for p, dist in zip(self.probability, self.distribution) + ] + ) def clip(self, tolerance: float = 1e-6, inplace: bool = False) -> Mixture: r"""Remove low-importance points / distributions @@ -1365,8 +1376,10 @@ def clip(self, tolerance: float = 1e-6, inplace: bool = False) -> Mixture: # Determine indices for any distributions that contribute non-negligibly # to overall intensity - intensities = [prob*dist.integral() for prob, dist in - zip(self.probability, self.distribution)] + intensities = [ + prob * dist.integral() + for prob, dist in zip(self.probability, self.distribution) + ] indices = _intensity_clip(intensities, tolerance=tolerance) # Clip mixture of distributions @@ -1390,18 +1403,17 @@ def clip(self, tolerance: float = 1e-6, inplace: bool = False) -> Mixture: # Show warning if integral of new distribution is not within # tolerance of original - diff = (original_integral - new_dist.integral())/original_integral + diff = (original_integral - new_dist.integral()) / original_integral if diff > tolerance: - warn("Clipping mixture distribution resulted in an integral that is " - f"lower by a fraction of {diff} when tolerance={tolerance}.") + warn( + "Clipping mixture distribution resulted in an integral that is " + f"lower by a fraction of {diff} when tolerance={tolerance}." + ) return new_dist -def combine_distributions( - dists: Sequence[Univariate], - probs: Sequence[float] -): +def combine_distributions(dists: Sequence[Univariate], probs: Sequence[float]): """Combine distributions with specified probabilities This function can be used to combine multiple instances of @@ -1445,7 +1457,7 @@ def combine_distributions( # Combine discrete and continuous if present if len(dist_list) > 1: - probs = [1.0]*len(dist_list) + probs = [1.0] * len(dist_list) dist_list[:] = [Mixture(probs, dist_list.copy())] return dist_list[0] diff --git a/openmc/summary.py b/openmc/summary.py index 6423f8ce1d6..24be43c2766 100644 --- a/openmc/summary.py +++ b/openmc/summary.py @@ -39,12 +39,12 @@ class Summary: def __init__(self, filename): filename = str(filename) - if not filename.endswith(('.h5', '.hdf5')): + if not filename.endswith((".h5", ".hdf5")): msg = f'Unable to open "{filename}" which is not an HDF5 summary file' raise ValueError(msg) - self._f = h5py.File(filename, 'r') - cv.check_filetype_version(self._f, 'summary', _VERSION_SUMMARY) + self._f = h5py.File(filename, "r") + cv.check_filetype_version(self._f, "summary", _VERSION_SUMMARY) self._geometry = openmc.Geometry() @@ -64,7 +64,7 @@ def __init__(self, filename): @property def date_and_time(self): - return self._f.attrs['date_and_time'].decode() + return self._f.attrs["date_and_time"].decode() @property def geometry(self): @@ -84,18 +84,18 @@ def macroscopics(self): @property def version(self): - return tuple(self._f.attrs['openmc_version']) + return tuple(self._f.attrs["openmc_version"]) def _read_nuclides(self): - if 'nuclides/names' in self._f: - names = self._f['nuclides/names'][()] - awrs = self._f['nuclides/awrs'][()] + if "nuclides/names" in self._f: + names = self._f["nuclides/names"][()] + awrs = self._f["nuclides/awrs"][()] for name, awr in zip(names, awrs): self._nuclides[name.decode()] = awr def _read_macroscopics(self): - if 'macroscopics/names' in self._f: - names = self._f['macroscopics/names'][()] + if "macroscopics/names" in self._f: + names = self._f["macroscopics/names"][()] self._macroscopics = [name.decode() for name in names] def _read_geometry(self): @@ -109,7 +109,7 @@ def _read_geometry(self): self._read_materials() # Read native geometry only - if "dagmc" not in self._f['geometry'].attrs.keys(): + if "dagmc" not in self._f["geometry"].attrs.keys(): self._read_surfaces() cell_fills = self._read_cells() self._read_universes() @@ -117,7 +117,7 @@ def _read_geometry(self): self._finalize_geometry(cell_fills) def _read_materials(self): - for group in self._f['materials'].values(): + for group in self._f["materials"].values(): material = openmc.Material.from_hdf5(group) # Add the material to the Materials collection @@ -127,7 +127,7 @@ def _read_materials(self): self._fast_materials[material.id] = material def _read_surfaces(self): - for group in self._f['geometry/surfaces'].values(): + for group in self._f["geometry/surfaces"].values(): surface = openmc.Surface.from_hdf5(group) # surface may be None for DAGMC surfaces if surface: @@ -138,37 +138,37 @@ def _read_cells(self): # Initialize dictionary for each Cell's fill cell_fills = {} - for key, group in self._f['geometry/cells'].items(): - cell_id = int(key.lstrip('cell ')) - name = group['name'][()].decode() if 'name' in group else '' - fill_type = group['fill_type'][()].decode() + for key, group in self._f["geometry/cells"].items(): + cell_id = int(key.lstrip("cell ")) + name = group["name"][()].decode() if "name" in group else "" + fill_type = group["fill_type"][()].decode() - if fill_type == 'material': - fill_id = group['material'][()] - elif fill_type == 'universe': - fill_id = group['fill'][()] + if fill_type == "material": + fill_id = group["material"][()] + elif fill_type == "universe": + fill_id = group["fill"][()] else: - fill_id = group['lattice'][()] + fill_id = group["lattice"][()] - region = group['region'][()].decode() if 'region' in group else '' + region = group["region"][()].decode() if "region" in group else "" # Create this Cell cell = openmc.Cell(cell_id=cell_id, name=name) - if fill_type == 'universe': - if 'translation' in group: - translation = group['translation'][()] + if fill_type == "universe": + if "translation" in group: + translation = group["translation"][()] translation = np.asarray(translation, dtype=np.float64) cell.translation = translation - if 'rotation' in group: - rotation = group['rotation'][()] + if "rotation" in group: + rotation = group["rotation"][()] if rotation.size == 9: rotation.shape = (3, 3) cell.rotation = rotation - elif fill_type == 'material': - cell.temperature = group['temperature'][()] + elif fill_type == "material": + cell.temperature = group["temperature"][()] # Store Cell fill information for after Universe/Lattice creation cell_fills[cell.id] = (fill_type, fill_id) @@ -183,16 +183,16 @@ def _read_cells(self): return cell_fills def _read_universes(self): - for group in self._f['geometry/universes'].values(): - geom_type = group.get('geom_type') - if geom_type and geom_type[()].decode() == 'dagmc': + for group in self._f["geometry/universes"].values(): + geom_type = group.get("geom_type") + if geom_type and geom_type[()].decode() == "dagmc": universe = openmc.DAGMCUniverse.from_hdf5(group) else: universe = openmc.Universe.from_hdf5(group, self._fast_cells) self._fast_universes[universe.id] = universe def _read_lattices(self): - for group in self._f['geometry/lattices'].values(): + for group in self._f["geometry/lattices"].values(): lattice = openmc.Lattice.from_hdf5(group, self._fast_universes) self._fast_lattices[lattice.id] = lattice @@ -206,13 +206,15 @@ def _finalize_geometry(self, cell_fills): # Iterate over all Cells and add fill Materials, Universes and Lattices for cell_id, (fill_type, fill_id) in cell_fills.items(): # Retrieve the object corresponding to the fill type and ID - if fill_type == 'material': + if fill_type == "material": if isinstance(fill_id, Iterable): - fill = [self._fast_materials[mat] if mat > 0 else None - for mat in fill_id] + fill = [ + self._fast_materials[mat] if mat > 0 else None + for mat in fill_id + ] else: fill = self._fast_materials[fill_id] if fill_id > 0 else None - elif fill_type == 'universe': + elif fill_type == "universe": fill = self._fast_universes[fill_id] fill_univ_ids.add(fill_id) else: diff --git a/openmc/surface.py b/openmc/surface.py index c95025f949b..a5334cec15d 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -15,8 +15,8 @@ from .bounding_box import BoundingBox -_BOUNDARY_TYPES = {'transmission', 'vacuum', 'reflective', 'periodic', 'white'} -_ALBEDO_BOUNDARIES = {'reflective', 'periodic', 'white'} +_BOUNDARY_TYPES = {"transmission", "vacuum", "reflective", "periodic", "white"} +_ALBEDO_BOUNDARIES = {"reflective", "periodic", "white"} _WARNING_UPPER = """\ "{}(...) accepts an argument named '{}', not '{}'. Future versions of OpenMC \ @@ -39,6 +39,7 @@ class SurfaceCoefficient: it is equivalent to (str). """ + def __init__(self, value): self.value = value @@ -53,21 +54,21 @@ def __get__(self, instance, owner=None): def __set__(self, instance, value): if isinstance(self.value, Real): - raise AttributeError('This coefficient is read-only') - check_type(f'{self.value} coefficient', value, Real) + raise AttributeError("This coefficient is read-only") + check_type(f"{self.value} coefficient", value, Real) instance._coefficients[self.value] = value def _future_kwargs_warning_helper(cls, *args, **kwargs): # Warn if Surface parameters are passed by position, not by keyword - argsdict = dict(zip(('boundary_type', 'name', 'surface_id'), args)) + argsdict = dict(zip(("boundary_type", "name", "surface_id"), args)) for k in argsdict: warn(_WARNING_KWARGS.format(cls.__name__, k), FutureWarning) kwargs.update(argsdict) return kwargs -def get_rotation_matrix(rotation, order='xyz'): +def get_rotation_matrix(rotation, order="xyz"): r"""Generate a 3x3 rotation matrix from input angles .. versionadded:: 0.12 @@ -90,17 +91,17 @@ def get_rotation_matrix(rotation, order='xyz'): intrinsic rotation using Tait-Bryan angles :math:`(\phi, \theta, \psi)`. """ - check_type('surface rotation', rotation, Iterable, Real) - check_length('surface rotation', rotation, 3) + check_type("surface rotation", rotation, Iterable, Real) + check_length("surface rotation", rotation, 3) - phi, theta, psi = np.array(rotation)*(math.pi/180.) + phi, theta, psi = np.array(rotation) * (math.pi / 180.0) cx, sx = math.cos(phi), math.sin(phi) cy, sy = math.cos(theta), math.sin(theta) cz, sz = math.cos(psi), math.sin(psi) R = { - 'x': np.array([[1., 0., 0.], [0., cx, -sx], [0., sx, cx]]), - 'y': np.array([[cy, 0., sy], [0., 1., 0.], [-sy, 0., cy]]), - 'z': np.array([[cz, -sz, 0.], [sz, cz, 0.], [0., 0., 1.]]), + "x": np.array([[1.0, 0.0, 0.0], [0.0, cx, -sx], [0.0, sx, cx]]), + "y": np.array([[cy, 0.0, sy], [0.0, 1.0, 0.0], [-sy, 0.0, cy]]), + "z": np.array([[cz, -sz, 0.0], [sz, cz, 0.0], [0.0, 0.0, 1.0]]), } R1, R2, R3 = (R[xi] for xi in order) @@ -153,10 +154,11 @@ class Surface(IDManagerMixin, ABC): next_id = 1 used_ids = set() - _atol = 1.e-12 + _atol = 1.0e-12 - def __init__(self, surface_id=None, boundary_type='transmission', - albedo=1., name=''): + def __init__( + self, surface_id=None, boundary_type="transmission", albedo=1.0, name="" + ): self.id = surface_id self.name = name self.boundary_type = boundary_type @@ -168,27 +170,28 @@ def __init__(self, surface_id=None, boundary_type='transmission', self._coefficients = {} def __neg__(self): - return Halfspace(self, '-') + return Halfspace(self, "-") def __pos__(self): - return Halfspace(self, '+') + return Halfspace(self, "+") def __repr__(self): - string = 'Surface\n' - string += '{0: <20}{1}{2}\n'.format('\tID', '=\t', self._id) - string += '{0: <20}{1}{2}\n'.format('\tName', '=\t', self._name) - string += '{0: <20}{1}{2}\n'.format('\tType', '=\t', self._type) - string += '{0: <20}{1}{2}\n'.format('\tBoundary', '=\t', - self._boundary_type) - if (self._boundary_type in _ALBEDO_BOUNDARIES and - not math.isclose(self._albedo, 1.0)): - string += '{0: <20}{1}{2}\n'.format('\tBoundary Albedo', '=\t', - self._albedo) - - coefficients = '{0: <20}'.format('\tCoefficients') + '\n' + string = "Surface\n" + string += "{0: <20}{1}{2}\n".format("\tID", "=\t", self._id) + string += "{0: <20}{1}{2}\n".format("\tName", "=\t", self._name) + string += "{0: <20}{1}{2}\n".format("\tType", "=\t", self._type) + string += "{0: <20}{1}{2}\n".format("\tBoundary", "=\t", self._boundary_type) + if self._boundary_type in _ALBEDO_BOUNDARIES and not math.isclose( + self._albedo, 1.0 + ): + string += "{0: <20}{1}{2}\n".format( + "\tBoundary Albedo", "=\t", self._albedo + ) + + coefficients = "{0: <20}".format("\tCoefficients") + "\n" for coeff in self._coefficients: - coefficients += f'{coeff: <20}=\t{self._coefficients[coeff]}\n' + coefficients += f"{coeff: <20}=\t{self._coefficients[coeff]}\n" string += coefficients @@ -201,10 +204,10 @@ def name(self): @name.setter def name(self, name): if name is not None: - check_type('surface name', name, str) + check_type("surface name", name, str) self._name = name else: - self._name = '' + self._name = "" @property def type(self): @@ -216,8 +219,8 @@ def boundary_type(self): @boundary_type.setter def boundary_type(self, boundary_type): - check_type('boundary type', boundary_type, str) - check_value('boundary type', boundary_type, _BOUNDARY_TYPES) + check_type("boundary type", boundary_type, str) + check_value("boundary type", boundary_type, _BOUNDARY_TYPES) self._boundary_type = boundary_type @property @@ -226,8 +229,8 @@ def albedo(self): @albedo.setter def albedo(self, albedo): - check_type('albedo', albedo, Real) - check_greater_than('albedo', albedo, 0.0) + check_type("albedo", albedo, Real) + check_greater_than("albedo", albedo, 0.0) self._albedo = float(albedo) @property @@ -308,9 +311,9 @@ def normalize(self, coeffs=None): if coeffs is None: coeffs = self._get_base_coeffs() coeffs = np.asarray(coeffs) - nonzeros = ~np.isclose(coeffs, 0., rtol=0., atol=self._atol) + nonzeros = ~np.isclose(coeffs, 0.0, rtol=0.0, atol=self._atol) norm_factor = np.abs(coeffs[nonzeros][0]) - return tuple([c/norm_factor for c in coeffs]) + return tuple([c / norm_factor for c in coeffs]) def is_equal(self, other): """Determine if this Surface is equivalent to another @@ -325,7 +328,7 @@ def is_equal(self, other): coeffs1 = self.normalize(self._get_base_coeffs()) coeffs2 = self.normalize(other._get_base_coeffs()) - return np.allclose(coeffs1, coeffs2, rtol=0., atol=self._atol) + return np.allclose(coeffs1, coeffs2, rtol=0.0, atol=self._atol) @abstractmethod def _get_base_coeffs(self): @@ -371,7 +374,7 @@ def translate(self, vector, inplace=False): """ @abstractmethod - def rotate(self, rotation, pivot=(0., 0., 0.), order='xyz', inplace=False): + def rotate(self, rotation, pivot=(0.0, 0.0, 0.0), order="xyz", inplace=False): r"""Rotate surface by angles provided or by applying matrix directly. .. versionadded:: 0.12 @@ -424,13 +427,21 @@ def to_xml_element(self): element.set("name", str(self._name)) element.set("type", self._type) - if self.boundary_type != 'transmission': + if self.boundary_type != "transmission": element.set("boundary", self.boundary_type) - if (self.boundary_type in _ALBEDO_BOUNDARIES and - not math.isclose(self.albedo, 1.0)): + if self.boundary_type in _ALBEDO_BOUNDARIES and not math.isclose( + self.albedo, 1.0 + ): element.set("albedo", str(self.albedo)) - element.set("coeffs", ' '.join([str(self._coefficients.setdefault(key, 0.0)) - for key in self._coeff_keys])) + element.set( + "coeffs", + " ".join( + [ + str(self._coefficients.setdefault(key, 0.0)) + for key in self._coeff_keys + ] + ), + ) return element @@ -451,17 +462,17 @@ def from_xml_element(elem): """ # Determine appropriate class - surf_type = elem.get('type') + surf_type = elem.get("type") cls = _SURFACE_CLASSES[surf_type] # Determine ID, boundary type, boundary albedo, coefficients kwargs = {} - kwargs['surface_id'] = int(elem.get('id')) - kwargs['boundary_type'] = elem.get('boundary', 'transmission') - if kwargs['boundary_type'] in _ALBEDO_BOUNDARIES: - kwargs['albedo'] = float(elem.get('albedo', 1.0)) - kwargs['name'] = elem.get('name') - coeffs = [float(x) for x in elem.get('coeffs').split()] + kwargs["surface_id"] = int(elem.get("id")) + kwargs["boundary_type"] = elem.get("boundary", "transmission") + if kwargs["boundary_type"] in _ALBEDO_BOUNDARIES: + kwargs["albedo"] = float(elem.get("albedo", 1.0)) + kwargs["name"] = elem.get("name") + coeffs = [float(x) for x in elem.get("coeffs").split()] kwargs.update(dict(zip(cls._coeff_keys, coeffs))) return cls(**kwargs) @@ -483,23 +494,27 @@ def from_hdf5(group): """ # If this is a DAGMC surface, do nothing for now - geom_type = group.get('geom_type') - if geom_type and geom_type[()].decode() == 'dagmc': + geom_type = group.get("geom_type") + if geom_type and geom_type[()].decode() == "dagmc": return - surface_id = int(group.name.split('/')[-1].lstrip('surface ')) - name = group['name'][()].decode() if 'name' in group else '' + surface_id = int(group.name.split("/")[-1].lstrip("surface ")) + name = group["name"][()].decode() if "name" in group else "" - bc = group['boundary_type'][()].decode() - if 'albedo' in group: - bc_alb = float(group['albedo'][()].decode()) + bc = group["boundary_type"][()].decode() + if "albedo" in group: + bc_alb = float(group["albedo"][()].decode()) else: bc_alb = 1.0 - coeffs = group['coefficients'][...] - kwargs = {'boundary_type': bc, 'albedo': bc_alb, 'name': name, - 'surface_id': surface_id} + coeffs = group["coefficients"][...] + kwargs = { + "boundary_type": bc, + "albedo": bc_alb, + "name": name, + "surface_id": surface_id, + } - surf_type = group['type'][()].decode() + surf_type = group["type"][()].decode() cls = _SURFACE_CLASSES[surf_type] return cls(*coeffs, **kwargs) @@ -507,6 +522,7 @@ def from_hdf5(group): class PlaneMixin: """A Plane mixin class for all operations on order 1 surfaces""" + def __init__(self, **kwargs): super().__init__(**kwargs) self._periodic_surface = None @@ -517,7 +533,7 @@ def periodic_surface(self): @periodic_surface.setter def periodic_surface(self, periodic_surface): - check_type('periodic surface', periodic_surface, Plane) + check_type("periodic surface", periodic_surface, Plane) self._periodic_surface = periodic_surface periodic_surface._periodic_surface = self @@ -526,7 +542,7 @@ def _get_base_coeffs(self): def _get_normal(self): a, b, c = self._get_base_coeffs()[:3] - return np.array((a, b, c)) / math.sqrt(a*a + b*b + c*c) + return np.array((a, b, c)) / math.sqrt(a * a + b * b + c * c) def bounding_box(self, side): """Determine an axis-aligned bounding box. @@ -556,17 +572,23 @@ def bounding_box(self, side): ll = np.array([-np.inf, -np.inf, -np.inf]) ur = np.array([np.inf, np.inf, np.inf]) # If the plane is axis aligned, find the proper bounding box - if np.any(np.isclose(np.abs(nhat), 1., rtol=0., atol=self._atol)): + if np.any(np.isclose(np.abs(nhat), 1.0, rtol=0.0, atol=self._atol)): sign = nhat.sum() a, b, c, d = self._get_base_coeffs() - vals = [d/val if not np.isclose(val, 0., rtol=0., atol=self._atol) - else np.nan for val in (a, b, c)] - if side == '-': + vals = [ + ( + d / val + if not np.isclose(val, 0.0, rtol=0.0, atol=self._atol) + else np.nan + ) + for val in (a, b, c) + ] + if side == "-": if sign > 0: ur = np.array([v if not np.isnan(v) else np.inf for v in vals]) else: ll = np.array([v if not np.isnan(v) else -np.inf for v in vals]) - elif side == '+': + elif side == "+": if sign > 0: ll = np.array([v if not np.isnan(v) else -np.inf for v in vals]) else: @@ -592,7 +614,7 @@ def evaluate(self, point): x, y, z = point a, b, c, d = self._get_base_coeffs() - return a*x + b*y + c*z - d + return a * x + b * y + c * z - d def translate(self, vector, inplace=False): """Translate surface in given direction @@ -611,7 +633,7 @@ def translate(self, vector, inplace=False): Translated surface """ - if np.allclose(vector, 0., rtol=0., atol=self._atol): + if np.allclose(vector, 0.0, rtol=0.0, atol=self._atol): return self a, b, c, d = self._get_base_coeffs() @@ -623,13 +645,13 @@ def translate(self, vector, inplace=False): return surf - def rotate(self, rotation, pivot=(0., 0., 0.), order='xyz', inplace=False): + def rotate(self, rotation, pivot=(0.0, 0.0, 0.0), order="xyz", inplace=False): pivot = np.asarray(pivot) rotation = np.asarray(rotation, dtype=float) # Allow rotation matrix to be passed in directly, otherwise build it if rotation.ndim == 2: - check_length('surface rotation', rotation.ravel(), 9) + check_length("surface rotation", rotation.ravel(), 9) Rmat = rotation else: Rmat = get_rotation_matrix(rotation, order=order) @@ -641,11 +663,13 @@ def rotate(self, rotation, pivot=(0., 0., 0.), order='xyz', inplace=False): # Compute new rotated coefficients a, b, c a, b, c = Rmat @ [a, b, c] - kwargs = {'boundary_type': surf.boundary_type, - 'albedo': surf.albedo, - 'name': surf.name} + kwargs = { + "boundary_type": surf.boundary_type, + "albedo": surf.albedo, + "name": surf.name, + } if inplace: - kwargs['surface_id'] = surf.id + kwargs["surface_id"] = surf.id surf = Plane(a=a, b=b, c=c, d=d, **kwargs) @@ -663,10 +687,9 @@ def to_xml_element(self): element = super().to_xml_element() # Add periodic surface pair information - if self.boundary_type == 'periodic': + if self.boundary_type == "periodic": if self.periodic_surface is not None: - element.set("periodic_surface_id", - str(self.periodic_surface.id)) + element.set("periodic_surface_id", str(self.periodic_surface.id)) return element @@ -726,21 +749,20 @@ class Plane(PlaneMixin, Surface): """ - _type = 'plane' - _coeff_keys = ('a', 'b', 'c', 'd') + _type = "plane" + _coeff_keys = ("a", "b", "c", "d") - def __init__(self, a=1., b=0., c=0., d=0., *args, **kwargs): + def __init__(self, a=1.0, b=0.0, c=0.0, d=0.0, *args, **kwargs): # *args should ultimately be limited to a, b, c, d as specified in # __init__, but to preserve the API it is allowed to accept Surface # parameters for now, but will raise warnings if this is done. kwargs = _future_kwargs_warning_helper(type(self), *args, **kwargs) # Warn if capital letter arguments are passed capdict = {} - for k in 'ABCD': + for k in "ABCD": val = kwargs.pop(k, None) if val is not None: - warn(_WARNING_UPPER.format(type(self), k.lower(), k), - FutureWarning) + warn(_WARNING_UPPER.format(type(self), k.lower(), k), FutureWarning) capdict[k.lower()] = val super().__init__(**kwargs) @@ -757,10 +779,10 @@ def __subclasshook__(cls, c): return True return NotImplemented - a = SurfaceCoefficient('a') - b = SurfaceCoefficient('b') - c = SurfaceCoefficient('c') - d = SurfaceCoefficient('d') + a = SurfaceCoefficient("a") + b = SurfaceCoefficient("b") + c = SurfaceCoefficient("c") + d = SurfaceCoefficient("d") @classmethod def from_points(cls, p1, p2, p3, **kwargs): @@ -794,7 +816,7 @@ def from_points(cls, p1, p2, p3, **kwargs): n = np.cross(p2 - p1, p3 - p1) # Check for points along a line - if np.allclose(n, 0.): + if np.allclose(n, 0.0): raise ValueError("All three points appear to lie along a line.") # The equation of the plane will by nĀ·( - p1) = 0. Determine @@ -856,20 +878,20 @@ class XPlane(PlaneMixin, Surface): """ - _type = 'x-plane' - _coeff_keys = ('x0',) + _type = "x-plane" + _coeff_keys = ("x0",) - def __init__(self, x0=0., *args, **kwargs): + def __init__(self, x0=0.0, *args, **kwargs): # work around for accepting Surface kwargs as positional parameters # until they are deprecated kwargs = _future_kwargs_warning_helper(type(self), *args, **kwargs) super().__init__(**kwargs) self.x0 = x0 - x0 = SurfaceCoefficient('x0') - a = SurfaceCoefficient(1.) - b = SurfaceCoefficient(0.) - c = SurfaceCoefficient(0.) + x0 = SurfaceCoefficient("x0") + a = SurfaceCoefficient(1.0) + b = SurfaceCoefficient(0.0) + c = SurfaceCoefficient(0.0) d = x0 def evaluate(self, point): @@ -921,20 +943,20 @@ class YPlane(PlaneMixin, Surface): """ - _type = 'y-plane' - _coeff_keys = ('y0',) + _type = "y-plane" + _coeff_keys = ("y0",) - def __init__(self, y0=0., *args, **kwargs): + def __init__(self, y0=0.0, *args, **kwargs): # work around for accepting Surface kwargs as positional parameters # until they are deprecated kwargs = _future_kwargs_warning_helper(type(self), *args, **kwargs) super().__init__(**kwargs) self.y0 = y0 - y0 = SurfaceCoefficient('y0') - a = SurfaceCoefficient(0.) - b = SurfaceCoefficient(1.) - c = SurfaceCoefficient(0.) + y0 = SurfaceCoefficient("y0") + a = SurfaceCoefficient(0.0) + b = SurfaceCoefficient(1.0) + c = SurfaceCoefficient(0.0) d = y0 def evaluate(self, point): @@ -986,20 +1008,20 @@ class ZPlane(PlaneMixin, Surface): """ - _type = 'z-plane' - _coeff_keys = ('z0',) + _type = "z-plane" + _coeff_keys = ("z0",) - def __init__(self, z0=0., *args, **kwargs): + def __init__(self, z0=0.0, *args, **kwargs): # work around for accepting Surface kwargs as positional parameters # until they are deprecated kwargs = _future_kwargs_warning_helper(type(self), *args, **kwargs) super().__init__(**kwargs) self.z0 = z0 - z0 = SurfaceCoefficient('z0') - a = SurfaceCoefficient(0.) - b = SurfaceCoefficient(0.) - c = SurfaceCoefficient(1.) + z0 = SurfaceCoefficient("z0") + a = SurfaceCoefficient(0.0) + b = SurfaceCoefficient(0.0) + c = SurfaceCoefficient(1.0) d = z0 def evaluate(self, point): @@ -1033,7 +1055,7 @@ def get_Abc(self, coeffs=None): else: a, b, c, d, e, f, g, h, j, k = coeffs - A = np.array([[a, d/2, f/2], [d/2, b, e/2], [f/2, e/2, c]]) + A = np.array([[a, d / 2, f / 2], [d / 2, b, e / 2], [f / 2, e / 2, c]]) bvec = np.array([g, h, j]) return A, bvec, k @@ -1097,13 +1119,13 @@ def translate(self, vector, inplace=False): """ vector = np.asarray(vector) - if np.allclose(vector, 0., rtol=0., atol=self._atol): + if np.allclose(vector, 0.0, rtol=0.0, atol=self._atol): return self surf = self if inplace else self.clone() - if hasattr(self, 'x0'): - for vi, xi in zip(vector, ('x0', 'y0', 'z0')): + if hasattr(self, "x0"): + for vi, xi in zip(vector, ("x0", "y0", "z0")): val = getattr(surf, xi) try: setattr(surf, xi, val + vi) @@ -1114,22 +1136,22 @@ def translate(self, vector, inplace=False): else: A, bvec, cnst = self.get_Abc() - g, h, j = bvec - 2*vector.T @ A + g, h, j = bvec - 2 * vector.T @ A k = cnst + vector.T @ A @ vector - bvec.T @ vector - for key, val in zip(('g', 'h', 'j', 'k'), (g, h, j, k)): + for key, val in zip(("g", "h", "j", "k"), (g, h, j, k)): setattr(surf, key, val) return surf - def rotate(self, rotation, pivot=(0., 0., 0.), order='xyz', inplace=False): + def rotate(self, rotation, pivot=(0.0, 0.0, 0.0), order="xyz", inplace=False): # Get pivot and rotation matrix pivot = np.asarray(pivot) rotation = np.asarray(rotation, dtype=float) # Allow rotation matrix to be passed in directly, otherwise build it if rotation.ndim == 2: - check_length('surface rotation', rotation.ravel(), 9) + check_length("surface rotation", rotation.ravel(), 9) Rmat = rotation else: Rmat = get_rotation_matrix(rotation, order=order) @@ -1143,27 +1165,30 @@ def rotate(self, rotation, pivot=(0., 0., 0.), order='xyz', inplace=False): else: base_cls = type(tsurf)._virtual_base # Copy necessary surface attributes to new kwargs dictionary - kwargs = {'boundary_type': tsurf.boundary_type, - 'albedo': tsurf.albedo, 'name': tsurf.name} + kwargs = { + "boundary_type": tsurf.boundary_type, + "albedo": tsurf.albedo, + "name": tsurf.name, + } if inplace: - kwargs['surface_id'] = tsurf.id + kwargs["surface_id"] = tsurf.id kwargs.update({k: getattr(tsurf, k) for k in base_cls._coeff_keys}) # Create new instance of the virtual base class surf = base_cls(**kwargs) # Perform rotations on axis, origin, or quadric coefficients - if hasattr(surf, 'dx'): - for key, val in zip(('dx', 'dy', 'dz'), Rmat @ tsurf._axis): + if hasattr(surf, "dx"): + for key, val in zip(("dx", "dy", "dz"), Rmat @ tsurf._axis): setattr(surf, key, val) - if hasattr(surf, 'x0'): - for key, val in zip(('x0', 'y0', 'z0'), Rmat @ tsurf._origin): + if hasattr(surf, "x0"): + for key, val in zip(("x0", "y0", "z0"), Rmat @ tsurf._origin): setattr(surf, key, val) else: A, bvec, k = surf.get_Abc() Arot = Rmat @ A @ Rmat.T a, b, c = np.diagonal(Arot) - d, e, f = 2*Arot[0, 1], 2*Arot[1, 2], 2*Arot[0, 2] + d, e, f = 2 * Arot[0, 1], 2 * Arot[1, 2], 2 * Arot[0, 2] g, h, j = Rmat @ bvec for key, val in zip(surf._coeff_keys, (a, b, c, d, e, f, g, h, j, k)): @@ -1243,11 +1268,13 @@ class Cylinder(QuadricMixin, Surface): Type of the surface """ - _type = 'cylinder' - _coeff_keys = ('x0', 'y0', 'z0', 'r', 'dx', 'dy', 'dz') - def __init__(self, x0=0., y0=0., z0=0., r=1., dx=0., dy=0., dz=1., *args, - **kwargs): + _type = "cylinder" + _coeff_keys = ("x0", "y0", "z0", "r", "dx", "dy", "dz") + + def __init__( + self, x0=0.0, y0=0.0, z0=0.0, r=1.0, dx=0.0, dy=0.0, dz=1.0, *args, **kwargs + ): kwargs = _future_kwargs_warning_helper(type(self), *args, **kwargs) super().__init__(**kwargs) @@ -1260,23 +1287,27 @@ def __subclasshook__(cls, c): return True return NotImplemented - x0 = SurfaceCoefficient('x0') - y0 = SurfaceCoefficient('y0') - z0 = SurfaceCoefficient('z0') - r = SurfaceCoefficient('r') - dx = SurfaceCoefficient('dx') - dy = SurfaceCoefficient('dy') - dz = SurfaceCoefficient('dz') + x0 = SurfaceCoefficient("x0") + y0 = SurfaceCoefficient("y0") + z0 = SurfaceCoefficient("z0") + r = SurfaceCoefficient("r") + dx = SurfaceCoefficient("dx") + dy = SurfaceCoefficient("dy") + dz = SurfaceCoefficient("dz") def bounding_box(self, side): - if side == '-': + if side == "-": r = self.r - ll = [xi - r if np.isclose(dxi, 0., rtol=0., atol=self._atol) - else -np.inf for xi, dxi in zip(self._origin, self._axis)] - ur = [xi + r if np.isclose(dxi, 0., rtol=0., atol=self._atol) - else np.inf for xi, dxi in zip(self._origin, self._axis)] + ll = [ + xi - r if np.isclose(dxi, 0.0, rtol=0.0, atol=self._atol) else -np.inf + for xi, dxi in zip(self._origin, self._axis) + ] + ur = [ + xi + r if np.isclose(dxi, 0.0, rtol=0.0, atol=self._atol) else np.inf + for xi, dxi in zip(self._origin, self._axis) + ] return BoundingBox(np.array(ll), np.array(ur)) - elif side == '+': + elif side == "+": return BoundingBox.infinite() def _get_base_coeffs(self): @@ -1289,30 +1320,30 @@ def _get_base_coeffs(self): dx = x2 - x1 dy = y2 - y1 dz = z2 - z1 - cx = y1*z2 - y2*z1 - cy = x2*z1 - x1*z2 - cz = x1*y2 - x2*y1 + cx = y1 * z2 - y2 * z1 + cy = x2 * z1 - x1 * z2 + cz = x1 * y2 - x2 * y1 # Given p=(x,y,z), p1=(x1, y1, z1), p2=(x2, y2, z2), the equation # for the cylinder can be derived as # r = |(p - p1) ⨯ (p - p2)| / |p2 - p1|. # Expanding out all terms and grouping according to what Quadric # expects gives the following coefficients. - a = dy*dy + dz*dz - b = dx*dx + dz*dz - c = dx*dx + dy*dy - d = -2*dx*dy - e = -2*dy*dz - f = -2*dx*dz - g = 2*(cy*dz - cz*dy) - h = 2*(cz*dx - cx*dz) - j = 2*(cx*dy - cy*dx) - k = cx*cx + cy*cy + cz*cz - (dx*dx + dy*dy + dz*dz)*r*r + a = dy * dy + dz * dz + b = dx * dx + dz * dz + c = dx * dx + dy * dy + d = -2 * dx * dy + e = -2 * dy * dz + f = -2 * dx * dz + g = 2 * (cy * dz - cz * dy) + h = 2 * (cz * dx - cx * dz) + j = 2 * (cx * dy - cy * dx) + k = cx * cx + cy * cy + cz * cz - (dx * dx + dy * dy + dz * dz) * r * r return (a, b, c, d, e, f, g, h, j, k) @classmethod - def from_points(cls, p1, p2, r=1., **kwargs): + def from_points(cls, p1, p2, r=1.0, **kwargs): """Return a cylinder given points that define the axis and a radius. .. versionadded:: 0.12 @@ -1353,9 +1384,13 @@ def to_xml_element(self): # This method overrides Surface.to_xml_element to generate a Quadric # since the C++ layer doesn't support Cylinders right now with catch_warnings(): - simplefilter('ignore', IDWarning) - kwargs = {'boundary_type': self.boundary_type, 'albedo': self.albedo, - 'name': self.name, 'surface_id': self.id} + simplefilter("ignore", IDWarning) + kwargs = { + "boundary_type": self.boundary_type, + "albedo": self.albedo, + "name": self.name, + "surface_id": self.id, + } quad_rep = Quadric(*self._get_base_coeffs(), **kwargs) return quad_rep.to_xml_element() @@ -1411,14 +1446,13 @@ class XCylinder(QuadricMixin, Surface): """ - _type = 'x-cylinder' - _coeff_keys = ('y0', 'z0', 'r') + _type = "x-cylinder" + _coeff_keys = ("y0", "z0", "r") - def __init__(self, y0=0., z0=0., r=1., *args, **kwargs): - R = kwargs.pop('R', None) + def __init__(self, y0=0.0, z0=0.0, r=1.0, *args, **kwargs): + R = kwargs.pop("R", None) if R is not None: - warn(_WARNING_UPPER.format(type(self).__name__, 'r', 'R'), - FutureWarning) + warn(_WARNING_UPPER.format(type(self).__name__, "r", "R"), FutureWarning) r = R kwargs = _future_kwargs_warning_helper(type(self), *args, **kwargs) super().__init__(**kwargs) @@ -1426,36 +1460,36 @@ def __init__(self, y0=0., z0=0., r=1., *args, **kwargs): for key, val in zip(self._coeff_keys, (y0, z0, r)): setattr(self, key, val) - x0 = SurfaceCoefficient(0.) - y0 = SurfaceCoefficient('y0') - z0 = SurfaceCoefficient('z0') - r = SurfaceCoefficient('r') - dx = SurfaceCoefficient(1.) - dy = SurfaceCoefficient(0.) - dz = SurfaceCoefficient(0.) + x0 = SurfaceCoefficient(0.0) + y0 = SurfaceCoefficient("y0") + z0 = SurfaceCoefficient("z0") + r = SurfaceCoefficient("r") + dx = SurfaceCoefficient(1.0) + dy = SurfaceCoefficient(0.0) + dz = SurfaceCoefficient(0.0) def _get_base_coeffs(self): y0, z0, r = self.y0, self.z0, self.r - a = d = e = f = g = 0. - b = c = 1. - h, j, k = -2*y0, -2*z0, y0*y0 + z0*z0 - r*r + a = d = e = f = g = 0.0 + b = c = 1.0 + h, j, k = -2 * y0, -2 * z0, y0 * y0 + z0 * z0 - r * r return (a, b, c, d, e, f, g, h, j, k) def bounding_box(self, side): - if side == '-': + if side == "-": return BoundingBox( np.array([-np.inf, self.y0 - self.r, self.z0 - self.r]), - np.array([np.inf, self.y0 + self.r, self.z0 + self.r]) + np.array([np.inf, self.y0 + self.r, self.z0 + self.r]), ) - elif side == '+': + elif side == "+": return BoundingBox.infinite() def evaluate(self, point): y = point[1] - self.y0 z = point[2] - self.z0 - return y*y + z*z - self.r**2 + return y * y + z * z - self.r**2 class YCylinder(QuadricMixin, Surface): @@ -1509,14 +1543,13 @@ class YCylinder(QuadricMixin, Surface): """ - _type = 'y-cylinder' - _coeff_keys = ('x0', 'z0', 'r') + _type = "y-cylinder" + _coeff_keys = ("x0", "z0", "r") - def __init__(self, x0=0., z0=0., r=1., *args, **kwargs): - R = kwargs.pop('R', None) + def __init__(self, x0=0.0, z0=0.0, r=1.0, *args, **kwargs): + R = kwargs.pop("R", None) if R is not None: - warn(_WARNING_UPPER.format(type(self).__name__, 'r', 'R'), - FutureWarning) + warn(_WARNING_UPPER.format(type(self).__name__, "r", "R"), FutureWarning) r = R kwargs = _future_kwargs_warning_helper(type(self), *args, **kwargs) super().__init__(**kwargs) @@ -1524,36 +1557,36 @@ def __init__(self, x0=0., z0=0., r=1., *args, **kwargs): for key, val in zip(self._coeff_keys, (x0, z0, r)): setattr(self, key, val) - x0 = SurfaceCoefficient('x0') - y0 = SurfaceCoefficient(0.) - z0 = SurfaceCoefficient('z0') - r = SurfaceCoefficient('r') - dx = SurfaceCoefficient(0.) - dy = SurfaceCoefficient(1.) - dz = SurfaceCoefficient(0.) + x0 = SurfaceCoefficient("x0") + y0 = SurfaceCoefficient(0.0) + z0 = SurfaceCoefficient("z0") + r = SurfaceCoefficient("r") + dx = SurfaceCoefficient(0.0) + dy = SurfaceCoefficient(1.0) + dz = SurfaceCoefficient(0.0) def _get_base_coeffs(self): x0, z0, r = self.x0, self.z0, self.r - b = d = e = f = h = 0. - a = c = 1. - g, j, k = -2*x0, -2*z0, x0*x0 + z0*z0 - r*r + b = d = e = f = h = 0.0 + a = c = 1.0 + g, j, k = -2 * x0, -2 * z0, x0 * x0 + z0 * z0 - r * r return (a, b, c, d, e, f, g, h, j, k) def bounding_box(self, side): - if side == '-': + if side == "-": return BoundingBox( np.array([self.x0 - self.r, -np.inf, self.z0 - self.r]), - np.array([self.x0 + self.r, np.inf, self.z0 + self.r]) + np.array([self.x0 + self.r, np.inf, self.z0 + self.r]), ) - elif side == '+': + elif side == "+": return BoundingBox.infinite() def evaluate(self, point): x = point[0] - self.x0 z = point[2] - self.z0 - return x*x + z*z - self.r**2 + return x * x + z * z - self.r**2 class ZCylinder(QuadricMixin, Surface): @@ -1607,14 +1640,13 @@ class ZCylinder(QuadricMixin, Surface): """ - _type = 'z-cylinder' - _coeff_keys = ('x0', 'y0', 'r') + _type = "z-cylinder" + _coeff_keys = ("x0", "y0", "r") - def __init__(self, x0=0., y0=0., r=1., *args, **kwargs): - R = kwargs.pop('R', None) + def __init__(self, x0=0.0, y0=0.0, r=1.0, *args, **kwargs): + R = kwargs.pop("R", None) if R is not None: - warn(_WARNING_UPPER.format(type(self).__name__, 'r', 'R'), - FutureWarning) + warn(_WARNING_UPPER.format(type(self).__name__, "r", "R"), FutureWarning) r = R kwargs = _future_kwargs_warning_helper(type(self), *args, **kwargs) super().__init__(**kwargs) @@ -1622,36 +1654,36 @@ def __init__(self, x0=0., y0=0., r=1., *args, **kwargs): for key, val in zip(self._coeff_keys, (x0, y0, r)): setattr(self, key, val) - x0 = SurfaceCoefficient('x0') - y0 = SurfaceCoefficient('y0') - z0 = SurfaceCoefficient(0.) - r = SurfaceCoefficient('r') - dx = SurfaceCoefficient(0.) - dy = SurfaceCoefficient(0.) - dz = SurfaceCoefficient(1.) + x0 = SurfaceCoefficient("x0") + y0 = SurfaceCoefficient("y0") + z0 = SurfaceCoefficient(0.0) + r = SurfaceCoefficient("r") + dx = SurfaceCoefficient(0.0) + dy = SurfaceCoefficient(0.0) + dz = SurfaceCoefficient(1.0) def _get_base_coeffs(self): x0, y0, r = self.x0, self.y0, self.r - c = d = e = f = j = 0. - a = b = 1. - g, h, k = -2*x0, -2*y0, x0*x0 + y0*y0 - r*r + c = d = e = f = j = 0.0 + a = b = 1.0 + g, h, k = -2 * x0, -2 * y0, x0 * x0 + y0 * y0 - r * r return (a, b, c, d, e, f, g, h, j, k) def bounding_box(self, side): - if side == '-': + if side == "-": return BoundingBox( np.array([self.x0 - self.r, self.y0 - self.r, -np.inf]), - np.array([self.x0 + self.r, self.y0 + self.r, np.inf]) + np.array([self.x0 + self.r, self.y0 + self.r, np.inf]), ) - elif side == '+': + elif side == "+": return BoundingBox.infinite() def evaluate(self, point): x = point[0] - self.x0 y = point[1] - self.y0 - return x*x + y*y - self.r**2 + return x * x + y * y - self.r**2 class Sphere(QuadricMixin, Surface): @@ -1707,14 +1739,13 @@ class Sphere(QuadricMixin, Surface): """ - _type = 'sphere' - _coeff_keys = ('x0', 'y0', 'z0', 'r') + _type = "sphere" + _coeff_keys = ("x0", "y0", "z0", "r") - def __init__(self, x0=0., y0=0., z0=0., r=1., *args, **kwargs): - R = kwargs.pop('R', None) + def __init__(self, x0=0.0, y0=0.0, z0=0.0, r=1.0, *args, **kwargs): + R = kwargs.pop("R", None) if R is not None: - warn(_WARNING_UPPER.format(type(self).__name__, 'r', 'R'), - FutureWarning) + warn(_WARNING_UPPER.format(type(self).__name__, "r", "R"), FutureWarning) r = R kwargs = _future_kwargs_warning_helper(type(self), *args, **kwargs) super().__init__(**kwargs) @@ -1722,34 +1753,34 @@ def __init__(self, x0=0., y0=0., z0=0., r=1., *args, **kwargs): for key, val in zip(self._coeff_keys, (x0, y0, z0, r)): setattr(self, key, val) - x0 = SurfaceCoefficient('x0') - y0 = SurfaceCoefficient('y0') - z0 = SurfaceCoefficient('z0') - r = SurfaceCoefficient('r') + x0 = SurfaceCoefficient("x0") + y0 = SurfaceCoefficient("y0") + z0 = SurfaceCoefficient("z0") + r = SurfaceCoefficient("r") def _get_base_coeffs(self): x0, y0, z0, r = self.x0, self.y0, self.z0, self.r - a = b = c = 1. - d = e = f = 0. - g, h, j = -2*x0, -2*y0, -2*z0 - k = x0*x0 + y0*y0 + z0*z0 - r*r + a = b = c = 1.0 + d = e = f = 0.0 + g, h, j = -2 * x0, -2 * y0, -2 * z0 + k = x0 * x0 + y0 * y0 + z0 * z0 - r * r return (a, b, c, d, e, f, g, h, j, k) def bounding_box(self, side): - if side == '-': + if side == "-": return BoundingBox( np.array([self.x0 - self.r, self.y0 - self.r, self.z0 - self.r]), - np.array([self.x0 + self.r, self.y0 + self.r, self.z0 + self.r]) + np.array([self.x0 + self.r, self.y0 + self.r, self.z0 + self.r]), ) - elif side == '+': + elif side == "+": return BoundingBox.infinite() def evaluate(self, point): x = point[0] - self.x0 y = point[1] - self.y0 z = point[2] - self.z0 - return x*x + y*y + z*z - self.r**2 + return x * x + y * y + z * z - self.r**2 class Cone(QuadricMixin, Surface): @@ -1828,15 +1859,15 @@ class Cone(QuadricMixin, Surface): """ - _type = 'cone' - _coeff_keys = ('x0', 'y0', 'z0', 'r2', 'dx', 'dy', 'dz') + _type = "cone" + _coeff_keys = ("x0", "y0", "z0", "r2", "dx", "dy", "dz") - def __init__(self, x0=0., y0=0., z0=0., r2=1., dx=0., dy=0., dz=1., *args, - **kwargs): - R2 = kwargs.pop('R2', None) + def __init__( + self, x0=0.0, y0=0.0, z0=0.0, r2=1.0, dx=0.0, dy=0.0, dz=1.0, *args, **kwargs + ): + R2 = kwargs.pop("R2", None) if R2 is not None: - warn(_WARNING_UPPER.format(type(self).__name__, 'r2', 'R2'), - FutureWarning) + warn(_WARNING_UPPER.format(type(self).__name__, "r2", "R2"), FutureWarning) r2 = R2 kwargs = _future_kwargs_warning_helper(type(self), *args, **kwargs) super().__init__(**kwargs) @@ -1850,13 +1881,13 @@ def __subclasshook__(cls, c): return True return NotImplemented - x0 = SurfaceCoefficient('x0') - y0 = SurfaceCoefficient('y0') - z0 = SurfaceCoefficient('z0') - r2 = SurfaceCoefficient('r2') - dx = SurfaceCoefficient('dx') - dy = SurfaceCoefficient('dy') - dz = SurfaceCoefficient('dz') + x0 = SurfaceCoefficient("x0") + y0 = SurfaceCoefficient("y0") + z0 = SurfaceCoefficient("z0") + r2 = SurfaceCoefficient("r2") + dx = SurfaceCoefficient("dx") + dy = SurfaceCoefficient("dy") + dz = SurfaceCoefficient("dz") def _get_base_coeffs(self): # The equation for a general cone with vertex at point p = (x0, y0, z0) @@ -1875,17 +1906,21 @@ def _get_base_coeffs(self): dx, dy, dz = self._axis cos2 = 1 / (1 + self.r2) - a = cos2 - dx*dx - b = cos2 - dy*dy - c = cos2 - dz*dz - d = -2*dx*dy - e = -2*dy*dz - f = -2*dx*dz - g = 2*(dx*(dy*y0 + dz*z0) - a*x0) - h = 2*(dy*(dx*x0 + dz*z0) - b*y0) - j = 2*(dz*(dx*x0 + dy*y0) - c*z0) - k = a*x0*x0 + b*y0*y0 + c*z0*z0 - 2*(dx*dy*x0*y0 + dy*dz*y0*z0 + - dx*dz*x0*z0) + a = cos2 - dx * dx + b = cos2 - dy * dy + c = cos2 - dz * dz + d = -2 * dx * dy + e = -2 * dy * dz + f = -2 * dx * dz + g = 2 * (dx * (dy * y0 + dz * z0) - a * x0) + h = 2 * (dy * (dx * x0 + dz * z0) - b * y0) + j = 2 * (dz * (dx * x0 + dy * y0) - c * z0) + k = ( + a * x0 * x0 + + b * y0 * y0 + + c * z0 * z0 + - 2 * (dx * dy * x0 * y0 + dy * dz * y0 * z0 + dx * dz * x0 * z0) + ) return (a, b, c, d, e, f, g, h, j, k) @@ -1901,11 +1936,13 @@ def to_xml_element(self): # This method overrides Surface.to_xml_element to generate a Quadric # since the C++ layer doesn't support Cones right now with catch_warnings(): - simplefilter('ignore', IDWarning) - kwargs = {'boundary_type': self.boundary_type, - 'albedo': self.albedo, - 'name': self.name, - 'surface_id': self.id} + simplefilter("ignore", IDWarning) + kwargs = { + "boundary_type": self.boundary_type, + "albedo": self.albedo, + "name": self.name, + "surface_id": self.id, + } quad_rep = Quadric(*self._get_base_coeffs(), **kwargs) return quad_rep.to_xml_element() @@ -1970,14 +2007,13 @@ class XCone(QuadricMixin, Surface): """ - _type = 'x-cone' - _coeff_keys = ('x0', 'y0', 'z0', 'r2') + _type = "x-cone" + _coeff_keys = ("x0", "y0", "z0", "r2") - def __init__(self, x0=0., y0=0., z0=0., r2=1., *args, **kwargs): - R2 = kwargs.pop('R2', None) + def __init__(self, x0=0.0, y0=0.0, z0=0.0, r2=1.0, *args, **kwargs): + R2 = kwargs.pop("R2", None) if R2 is not None: - warn(_WARNING_UPPER.format(type(self).__name__, 'r2', 'R2'), - FutureWarning) + warn(_WARNING_UPPER.format(type(self).__name__, "r2", "R2"), FutureWarning) r2 = R2 kwargs = _future_kwargs_warning_helper(type(self), *args, **kwargs) super().__init__(**kwargs) @@ -1985,22 +2021,22 @@ def __init__(self, x0=0., y0=0., z0=0., r2=1., *args, **kwargs): for key, val in zip(self._coeff_keys, (x0, y0, z0, r2)): setattr(self, key, val) - x0 = SurfaceCoefficient('x0') - y0 = SurfaceCoefficient('y0') - z0 = SurfaceCoefficient('z0') - r2 = SurfaceCoefficient('r2') - dx = SurfaceCoefficient(1.) - dy = SurfaceCoefficient(0.) - dz = SurfaceCoefficient(0.) + x0 = SurfaceCoefficient("x0") + y0 = SurfaceCoefficient("y0") + z0 = SurfaceCoefficient("z0") + r2 = SurfaceCoefficient("r2") + dx = SurfaceCoefficient(1.0) + dy = SurfaceCoefficient(0.0) + dz = SurfaceCoefficient(0.0) def _get_base_coeffs(self): x0, y0, z0, r2 = self.x0, self.y0, self.z0, self.r2 a = -r2 - b = c = 1. - d = e = f = 0. - g, h, j = 2*x0*r2, -2*y0, -2*z0 - k = y0*y0 + z0*z0 - r2*x0*x0 + b = c = 1.0 + d = e = f = 0.0 + g, h, j = 2 * x0 * r2, -2 * y0, -2 * z0 + k = y0 * y0 + z0 * z0 - r2 * x0 * x0 return (a, b, c, d, e, f, g, h, j, k) @@ -2008,7 +2044,7 @@ def evaluate(self, point): x = point[0] - self.x0 y = point[1] - self.y0 z = point[2] - self.z0 - return y*y + z*z - self.r2*x*x + return y * y + z * z - self.r2 * x * x class YCone(QuadricMixin, Surface): @@ -2071,14 +2107,13 @@ class YCone(QuadricMixin, Surface): """ - _type = 'y-cone' - _coeff_keys = ('x0', 'y0', 'z0', 'r2') + _type = "y-cone" + _coeff_keys = ("x0", "y0", "z0", "r2") - def __init__(self, x0=0., y0=0., z0=0., r2=1., *args, **kwargs): - R2 = kwargs.pop('R2', None) + def __init__(self, x0=0.0, y0=0.0, z0=0.0, r2=1.0, *args, **kwargs): + R2 = kwargs.pop("R2", None) if R2 is not None: - warn(_WARNING_UPPER.format(type(self).__name__, 'r2', 'R2'), - FutureWarning) + warn(_WARNING_UPPER.format(type(self).__name__, "r2", "R2"), FutureWarning) r2 = R2 kwargs = _future_kwargs_warning_helper(type(self), *args, **kwargs) super().__init__(**kwargs) @@ -2086,22 +2121,22 @@ def __init__(self, x0=0., y0=0., z0=0., r2=1., *args, **kwargs): for key, val in zip(self._coeff_keys, (x0, y0, z0, r2)): setattr(self, key, val) - x0 = SurfaceCoefficient('x0') - y0 = SurfaceCoefficient('y0') - z0 = SurfaceCoefficient('z0') - r2 = SurfaceCoefficient('r2') - dx = SurfaceCoefficient(0.) - dy = SurfaceCoefficient(1.) - dz = SurfaceCoefficient(0.) + x0 = SurfaceCoefficient("x0") + y0 = SurfaceCoefficient("y0") + z0 = SurfaceCoefficient("z0") + r2 = SurfaceCoefficient("r2") + dx = SurfaceCoefficient(0.0) + dy = SurfaceCoefficient(1.0) + dz = SurfaceCoefficient(0.0) def _get_base_coeffs(self): x0, y0, z0, r2 = self.x0, self.y0, self.z0, self.r2 b = -r2 - a = c = 1. - d = e = f = 0. - g, h, j = -2*x0, 2*y0*r2, -2*z0 - k = x0*x0 + z0*z0 - r2*y0*y0 + a = c = 1.0 + d = e = f = 0.0 + g, h, j = -2 * x0, 2 * y0 * r2, -2 * z0 + k = x0 * x0 + z0 * z0 - r2 * y0 * y0 return (a, b, c, d, e, f, g, h, j, k) @@ -2109,7 +2144,7 @@ def evaluate(self, point): x = point[0] - self.x0 y = point[1] - self.y0 z = point[2] - self.z0 - return x*x + z*z - self.r2*y*y + return x * x + z * z - self.r2 * y * y class ZCone(QuadricMixin, Surface): @@ -2172,14 +2207,13 @@ class ZCone(QuadricMixin, Surface): """ - _type = 'z-cone' - _coeff_keys = ('x0', 'y0', 'z0', 'r2') + _type = "z-cone" + _coeff_keys = ("x0", "y0", "z0", "r2") - def __init__(self, x0=0., y0=0., z0=0., r2=1., *args, **kwargs): - R2 = kwargs.pop('R2', None) + def __init__(self, x0=0.0, y0=0.0, z0=0.0, r2=1.0, *args, **kwargs): + R2 = kwargs.pop("R2", None) if R2 is not None: - warn(_WARNING_UPPER.format(type(self).__name__, 'r2', 'R2'), - FutureWarning) + warn(_WARNING_UPPER.format(type(self).__name__, "r2", "R2"), FutureWarning) r2 = R2 kwargs = _future_kwargs_warning_helper(type(self), *args, **kwargs) super().__init__(**kwargs) @@ -2187,22 +2221,22 @@ def __init__(self, x0=0., y0=0., z0=0., r2=1., *args, **kwargs): for key, val in zip(self._coeff_keys, (x0, y0, z0, r2)): setattr(self, key, val) - x0 = SurfaceCoefficient('x0') - y0 = SurfaceCoefficient('y0') - z0 = SurfaceCoefficient('z0') - r2 = SurfaceCoefficient('r2') - dx = SurfaceCoefficient(0.) - dy = SurfaceCoefficient(0.) - dz = SurfaceCoefficient(1.) + x0 = SurfaceCoefficient("x0") + y0 = SurfaceCoefficient("y0") + z0 = SurfaceCoefficient("z0") + r2 = SurfaceCoefficient("r2") + dx = SurfaceCoefficient(0.0) + dy = SurfaceCoefficient(0.0) + dz = SurfaceCoefficient(1.0) def _get_base_coeffs(self): x0, y0, z0, r2 = self.x0, self.y0, self.z0, self.r2 c = -r2 - a = b = 1. - d = e = f = 0. - g, h, j = -2*x0, -2*y0, 2*z0*r2 - k = x0*x0 + y0*y0 - r2*z0*z0 + a = b = 1.0 + d = e = f = 0.0 + g, h, j = -2 * x0, -2 * y0, 2 * z0 * r2 + k = x0 * x0 + y0 * y0 - r2 * z0 * z0 return (a, b, c, d, e, f, g, h, j, k) @@ -2210,7 +2244,7 @@ def evaluate(self, point): x = point[0] - self.x0 y = point[1] - self.y0 z = point[2] - self.z0 - return x*x + y*y - self.r2*z*z + return x * x + y * y - self.r2 * z * z class Quadric(QuadricMixin, Surface): @@ -2255,27 +2289,40 @@ class Quadric(QuadricMixin, Surface): """ - _type = 'quadric' - _coeff_keys = ('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k') - - def __init__(self, a=0., b=0., c=0., d=0., e=0., f=0., g=0., h=0., j=0., - k=0., *args, **kwargs): + _type = "quadric" + _coeff_keys = ("a", "b", "c", "d", "e", "f", "g", "h", "j", "k") + + def __init__( + self, + a=0.0, + b=0.0, + c=0.0, + d=0.0, + e=0.0, + f=0.0, + g=0.0, + h=0.0, + j=0.0, + k=0.0, + *args, + **kwargs, + ): kwargs = _future_kwargs_warning_helper(type(self), *args, **kwargs) super().__init__(**kwargs) for key, val in zip(self._coeff_keys, (a, b, c, d, e, f, g, h, j, k)): setattr(self, key, val) - a = SurfaceCoefficient('a') - b = SurfaceCoefficient('b') - c = SurfaceCoefficient('c') - d = SurfaceCoefficient('d') - e = SurfaceCoefficient('e') - f = SurfaceCoefficient('f') - g = SurfaceCoefficient('g') - h = SurfaceCoefficient('h') - j = SurfaceCoefficient('j') - k = SurfaceCoefficient('k') + a = SurfaceCoefficient("a") + b = SurfaceCoefficient("b") + c = SurfaceCoefficient("c") + d = SurfaceCoefficient("d") + e = SurfaceCoefficient("e") + f = SurfaceCoefficient("f") + g = SurfaceCoefficient("g") + h = SurfaceCoefficient("h") + j = SurfaceCoefficient("j") + k = SurfaceCoefficient("k") def _get_base_coeffs(self): return tuple(getattr(self, c) for c in self._coeff_keys) @@ -2283,19 +2330,20 @@ def _get_base_coeffs(self): class TorusMixin: """A Mixin class implementing common functionality for torus surfaces""" - _coeff_keys = ('x0', 'y0', 'z0', 'a', 'b', 'c') - def __init__(self, x0=0., y0=0., z0=0., a=0., b=0., c=0., **kwargs): + _coeff_keys = ("x0", "y0", "z0", "a", "b", "c") + + def __init__(self, x0=0.0, y0=0.0, z0=0.0, a=0.0, b=0.0, c=0.0, **kwargs): super().__init__(**kwargs) for key, val in zip(self._coeff_keys, (x0, y0, z0, a, b, c)): setattr(self, key, val) - x0 = SurfaceCoefficient('x0') - y0 = SurfaceCoefficient('y0') - z0 = SurfaceCoefficient('z0') - a = SurfaceCoefficient('a') - b = SurfaceCoefficient('b') - c = SurfaceCoefficient('c') + x0 = SurfaceCoefficient("x0") + y0 = SurfaceCoefficient("y0") + z0 = SurfaceCoefficient("z0") + a = SurfaceCoefficient("a") + b = SurfaceCoefficient("b") + c = SurfaceCoefficient("c") def translate(self, vector, inplace=False): surf = self if inplace else self.clone() @@ -2304,13 +2352,13 @@ def translate(self, vector, inplace=False): surf.z0 += vector[2] return surf - def rotate(self, rotation, pivot=(0., 0., 0.), order='xyz', inplace=False): + def rotate(self, rotation, pivot=(0.0, 0.0, 0.0), order="xyz", inplace=False): pivot = np.asarray(pivot) rotation = np.asarray(rotation, dtype=float) # Allow rotation matrix to be passed in directly, otherwise build it if rotation.ndim == 2: - check_length('surface rotation', rotation.ravel(), 9) + check_length("surface rotation", rotation.ravel(), 9) Rmat = rotation else: Rmat = get_rotation_matrix(rotation, order=order) @@ -2318,7 +2366,7 @@ def rotate(self, rotation, pivot=(0., 0., 0.), order='xyz', inplace=False): # Only can handle trivial rotation matrices close = np.isclose if not np.all(close(Rmat, -1.0) | close(Rmat, 0.0) | close(Rmat, 1.0)): - raise NotImplementedError('Torus surfaces cannot handle generic rotations') + raise NotImplementedError("Torus surfaces cannot handle generic rotations") # Translate surface to pivot surf = self.translate(-pivot, inplace=inplace) @@ -2326,7 +2374,7 @@ def rotate(self, rotation, pivot=(0., 0., 0.), order='xyz', inplace=False): # Determine "center" of torus and a point above it (along main axis) center = [surf.x0, surf.y0, surf.z0] above_center = center.copy() - index = ['x-torus', 'y-torus', 'z-torus'].index(surf._type) + index = ["x-torus", "y-torus", "z-torus"].index(surf._type) above_center[index] += 1 # Compute new rotated torus center @@ -2339,13 +2387,15 @@ def rotate(self, rotation, pivot=(0., 0., 0.), order='xyz', inplace=False): # Create rotated torus kwargs = { - 'boundary_type': surf.boundary_type, - 'albedo': surf.albedo, - 'name': surf.name, - 'a': surf.a, 'b': surf.b, 'c': surf.c + "boundary_type": surf.boundary_type, + "albedo": surf.albedo, + "name": surf.name, + "a": surf.a, + "b": surf.b, + "c": surf.c, } if inplace: - kwargs['surface_id'] = surf.id + kwargs["surface_id"] = surf.id surf = cls(x0=center[0], y0=center[1], z0=center[2], **kwargs) return surf.translate(pivot, inplace=inplace) @@ -2406,7 +2456,8 @@ class XTorus(TorusMixin, Surface): Type of the surface """ - _type = 'x-torus' + + _type = "x-torus" def evaluate(self, point): x = point[0] - self.x0 @@ -2415,17 +2466,17 @@ def evaluate(self, point): a = self.a b = self.b c = self.c - return (x*x)/(b*b) + (math.sqrt(y*y + z*z) - a)**2/(c*c) - 1 + return (x * x) / (b * b) + (math.sqrt(y * y + z * z) - a) ** 2 / (c * c) - 1 def bounding_box(self, side): x0, y0, z0 = self.x0, self.y0, self.z0 a, b, c = self.a, self.b, self.c - if side == '-': + if side == "-": return BoundingBox( np.array([x0 - b, y0 - a - c, z0 - a - c]), - np.array([x0 + b, y0 + a + c, z0 + a + c]) + np.array([x0 + b, y0 + a + c, z0 + a + c]), ) - elif side == '+': + elif side == "+": return BoundingBox.infinite() @@ -2481,7 +2532,8 @@ class YTorus(TorusMixin, Surface): Type of the surface """ - _type = 'y-torus' + + _type = "y-torus" def evaluate(self, point): x = point[0] - self.x0 @@ -2490,17 +2542,17 @@ def evaluate(self, point): a = self.a b = self.b c = self.c - return (y*y)/(b*b) + (math.sqrt(x*x + z*z) - a)**2/(c*c) - 1 + return (y * y) / (b * b) + (math.sqrt(x * x + z * z) - a) ** 2 / (c * c) - 1 def bounding_box(self, side): x0, y0, z0 = self.x0, self.y0, self.z0 a, b, c = self.a, self.b, self.c - if side == '-': + if side == "-": return BoundingBox( np.array([x0 - a - c, y0 - b, z0 - a - c]), - np.array([x0 + a + c, y0 + b, z0 + a + c]) + np.array([x0 + a + c, y0 + b, z0 + a + c]), ) - elif side == '+': + elif side == "+": return BoundingBox.infinite() @@ -2556,7 +2608,7 @@ class ZTorus(TorusMixin, Surface): Type of the surface """ - _type = 'z-torus' + _type = "z-torus" def evaluate(self, point): x = point[0] - self.x0 @@ -2565,17 +2617,17 @@ def evaluate(self, point): a = self.a b = self.b c = self.c - return (z*z)/(b*b) + (math.sqrt(x*x + y*y) - a)**2/(c*c) - 1 + return (z * z) / (b * b) + (math.sqrt(x * x + y * y) - a) ** 2 / (c * c) - 1 def bounding_box(self, side): x0, y0, z0 = self.x0, self.y0, self.z0 a, b, c = self.a, self.b, self.c - if side == '-': + if side == "-": return BoundingBox( np.array([x0 - a - c, y0 - a - c, z0 - b]), - np.array([x0 + a + c, y0 + a + c, z0 + b]) + np.array([x0 + a + c, y0 + a + c, z0 + b]), ) - elif side == '+': + elif side == "+": return BoundingBox.infinite() @@ -2633,7 +2685,7 @@ def __or__(self, other): return Union((self, other)) def __invert__(self) -> Halfspace: - return -self.surface if self.side == '+' else +self.surface + return -self.surface if self.side == "+" else +self.surface def __contains__(self, point): """Check whether a point is contained in the half-space. @@ -2651,7 +2703,7 @@ def __contains__(self, point): """ val = self.surface.evaluate(point) - return val >= 0. if self.side == '+' else val < 0. + return val >= 0.0 if self.side == "+" else val < 0.0 @property def surface(self): @@ -2659,7 +2711,7 @@ def surface(self): @surface.setter def surface(self, surface): - check_type('surface', surface, Surface) + check_type("surface", surface, Surface) self._surface = surface @property @@ -2668,7 +2720,7 @@ def side(self): @side.setter def side(self, side): - check_value('side', side, ('+', '-')) + check_value("side", side, ("+", "-")) self._side = side @property @@ -2676,8 +2728,7 @@ def bounding_box(self): return self.surface.bounding_box(self.side) def __str__(self): - return '-' + str(self.surface.id) if self.side == '-' \ - else str(self.surface.id) + return "-" + str(self.surface.id) if self.side == "-" else str(self.surface.id) def get_surfaces(self, surfaces=None): """ @@ -2766,8 +2817,9 @@ def translate(self, vector, inplace=False, memo=None): # Return translated half-space return type(self)(memo[key], self.side) - def rotate(self, rotation, pivot=(0., 0., 0.), order='xyz', inplace=False, - memo=None): + def rotate( + self, rotation, pivot=(0.0, 0.0, 0.0), order="xyz", inplace=False, memo=None + ): r"""Rotate surface by angles provided or by applying matrix directly. .. versionadded:: 0.12 @@ -2811,8 +2863,9 @@ def rotate(self, rotation, pivot=(0., 0., 0.), order='xyz', inplace=False, # If rotated surface not in memo, add it key = (self.surface, tuple(np.ravel(rotation)), tuple(pivot), order, inplace) if key not in memo: - memo[key] = self.surface.rotate(rotation, pivot=pivot, order=order, - inplace=inplace) + memo[key] = self.surface.rotate( + rotation, pivot=pivot, order=order, inplace=inplace + ) # Return rotated half-space return type(self)(memo[key], self.side) diff --git a/openmc/tallies.py b/openmc/tallies.py index f39ffa0789f..4dc80c228ff 100644 --- a/openmc/tallies.py +++ b/openmc/tallies.py @@ -24,7 +24,7 @@ # (filters, nuclides, or scores). The entrywise product performs the arithmetic # operation entrywise across the entries in two tallies with respect to a # specified axis. -_PRODUCT_TYPES = ['tensor', 'entrywise'] +_PRODUCT_TYPES = ["tensor", "entrywise"] # The following indicate acceptable types when setting Tally.scores, # Tally.nuclides, and Tally.filters @@ -33,7 +33,7 @@ _FILTER_CLASSES = (openmc.Filter, openmc.CrossFilter, openmc.AggregateFilter) # Valid types of estimators -ESTIMATOR_TYPES = ['tracklength', 'collision', 'analog'] +ESTIMATOR_TYPES = ["tracklength", "collision", "analog"] class Tally(IDManagerMixin): @@ -105,15 +105,15 @@ class Tally(IDManagerMixin): next_id = 1 used_ids = set() - def __init__(self, tally_id=None, name=''): + def __init__(self, tally_id=None, name=""): # Initialize Tally class attributes self.id = tally_id self.name = name - self._filters = cv.CheckedList(_FILTER_CLASSES, 'tally filters') - self._nuclides = cv.CheckedList(_NUCLIDE_CLASSES, 'tally nuclides') - self._scores = cv.CheckedList(_SCORE_CLASSES, 'tally scores') + self._filters = cv.CheckedList(_FILTER_CLASSES, "tally filters") + self._nuclides = cv.CheckedList(_NUCLIDE_CLASSES, "tally nuclides") + self._scores = cv.CheckedList(_SCORE_CLASSES, "tally scores") self._estimator = None - self._triggers = cv.CheckedList(openmc.Trigger, 'tally triggers') + self._triggers = cv.CheckedList(openmc.Trigger, "tally triggers") self._derivative = None self._multiply_density = True @@ -132,19 +132,19 @@ def __init__(self, tally_id=None, name=''): self._results_read = False def __repr__(self): - parts = ['Tally'] - parts.append('{: <15}=\t{}'.format('ID', self.id)) - parts.append('{: <15}=\t{}'.format('Name', self.name)) + parts = ["Tally"] + parts.append("{: <15}=\t{}".format("ID", self.id)) + parts.append("{: <15}=\t{}".format("Name", self.name)) if self.derivative is not None: - parts.append('{: <15}=\t{}'.format('Derivative ID', self.derivative.id)) - filters = ', '.join(type(f).__name__ for f in self.filters) - parts.append('{: <15}=\t{}'.format('Filters', filters)) - nuclides = ' '.join(str(nuclide) for nuclide in self.nuclides) - parts.append('{: <15}=\t{}'.format('Nuclides', nuclides)) - parts.append('{: <15}=\t{}'.format('Scores', self.scores)) - parts.append('{: <15}=\t{}'.format('Estimator', self.estimator)) - parts.append('{: <15}=\t{}'.format('Multiply dens.', self.multiply_density)) - return '\n\t'.join(parts) + parts.append("{: <15}=\t{}".format("Derivative ID", self.derivative.id)) + filters = ", ".join(type(f).__name__ for f in self.filters) + parts.append("{: <15}=\t{}".format("Filters", filters)) + nuclides = " ".join(str(nuclide) for nuclide in self.nuclides) + parts.append("{: <15}=\t{}".format("Nuclides", nuclides)) + parts.append("{: <15}=\t{}".format("Scores", self.scores)) + parts.append("{: <15}=\t{}".format("Estimator", self.estimator)) + parts.append("{: <15}=\t{}".format("Multiply dens.", self.multiply_density)) + return "\n\t".join(parts) @property def name(self): @@ -152,7 +152,7 @@ def name(self): @name.setter def name(self, name): - cv.check_type('tally name', name, str, none_ok=True) + cv.check_type("tally name", name, str, none_ok=True) self._name = name @property @@ -161,7 +161,7 @@ def multiply_density(self): @multiply_density.setter def multiply_density(self, value): - cv.check_type('multiply density', value, bool) + cv.check_type("multiply density", value, bool) self._multiply_density = value @property @@ -170,19 +170,21 @@ def filters(self): @filters.setter def filters(self, filters): - cv.check_type('tally filters', filters, MutableSequence) + cv.check_type("tally filters", filters, MutableSequence) # If the filter is already in the Tally, raise an error visited_filters = set() for f in filters: if f in visited_filters: - msg = (f'Unable to add a duplicate filter "{f}" to Tally ' - f'ID="{self.id}" since duplicate filters are not ' - 'supported in the OpenMC Python API') + msg = ( + f'Unable to add a duplicate filter "{f}" to Tally ' + f'ID="{self.id}" since duplicate filters are not ' + "supported in the OpenMC Python API" + ) raise ValueError(msg) visited_filters.add(f) - self._filters = cv.CheckedList(_FILTER_CLASSES, 'tally filters', filters) + self._filters = cv.CheckedList(_FILTER_CLASSES, "tally filters", filters) @property def nuclides(self): @@ -190,20 +192,21 @@ def nuclides(self): @nuclides.setter def nuclides(self, nuclides): - cv.check_type('tally nuclides', nuclides, MutableSequence) + cv.check_type("tally nuclides", nuclides, MutableSequence) # If the nuclide is already in the Tally, raise an error visited_nuclides = set() for nuc in nuclides: if nuc in visited_nuclides: - msg = (f'Unable to add a duplicate nuclide "{nuc}" to Tally ID=' - f'"{self.id}" since duplicate nuclides are not supported ' - 'in the OpenMC Python API') + msg = ( + f'Unable to add a duplicate nuclide "{nuc}" to Tally ID=' + f'"{self.id}" since duplicate nuclides are not supported ' + "in the OpenMC Python API" + ) raise ValueError(msg) visited_nuclides.add(nuc) - self._nuclides = cv.CheckedList(_NUCLIDE_CLASSES, 'tally nuclides', - nuclides) + self._nuclides = cv.CheckedList(_NUCLIDE_CLASSES, "tally nuclides", nuclides) @property def num_nuclides(self): @@ -215,30 +218,39 @@ def scores(self): @scores.setter def scores(self, scores): - cv.check_type('tally scores', scores, MutableSequence) + cv.check_type("tally scores", scores, MutableSequence) visited_scores = set() for i, score in enumerate(scores): # If the score is already in the Tally, raise an error if score in visited_scores: - msg = (f'Unable to add a duplicate score "{score}" to Tally ' - f'ID="{self.id}" since duplicate scores are not ' - 'supported in the OpenMC Python API') + msg = ( + f'Unable to add a duplicate score "{score}" to Tally ' + f'ID="{self.id}" since duplicate scores are not ' + "supported in the OpenMC Python API" + ) raise ValueError(msg) visited_scores.add(score) # If score is a string, strip whitespace if isinstance(score, str): # Check to see if scores are deprecated before storing - for deprecated in ['scatter-', 'nu-scatter-', 'scatter-p', - 'nu-scatter-p', 'scatter-y', 'nu-scatter-y', - 'flux-y', 'total-y']: + for deprecated in [ + "scatter-", + "nu-scatter-", + "scatter-p", + "nu-scatter-p", + "scatter-y", + "nu-scatter-y", + "flux-y", + "total-y", + ]: if score.strip().startswith(deprecated): - msg = score.strip() + ' is no longer supported.' + msg = score.strip() + " is no longer supported." raise ValueError(msg) scores[i] = score.strip() - self._scores = cv.CheckedList(_SCORE_CLASSES, 'tally scores', scores) + self._scores = cv.CheckedList(_SCORE_CLASSES, "tally scores", scores) @property def num_scores(self): @@ -266,7 +278,7 @@ def estimator(self): @estimator.setter def estimator(self, estimator): - cv.check_value('estimator', estimator, ESTIMATOR_TYPES) + cv.check_value("estimator", estimator, ESTIMATOR_TYPES) self._estimator = estimator @property @@ -275,9 +287,8 @@ def triggers(self): @triggers.setter def triggers(self, triggers): - cv.check_type('tally triggers', triggers, MutableSequence) - self._triggers = cv.CheckedList(openmc.Trigger, 'tally triggers', - triggers) + cv.check_type("tally triggers", triggers, MutableSequence) + self._triggers = cv.CheckedList(openmc.Trigger, "tally triggers", triggers) @property def num_realizations(self): @@ -285,8 +296,8 @@ def num_realizations(self): @num_realizations.setter def num_realizations(self, num_realizations): - cv.check_type('number of realizations', num_realizations, Integral) - cv.check_greater_than('number of realizations', num_realizations, 0, True) + cv.check_type("number of realizations", num_realizations, Integral) + cv.check_greater_than("number of realizations", num_realizations, 0, True) self._num_realizations = num_realizations @property @@ -295,7 +306,7 @@ def with_summary(self): @with_summary.setter def with_summary(self, with_summary): - cv.check_type('with_summary', with_summary, bool) + cv.check_type("with_summary", with_summary, bool) self._with_summary = with_summary def _read_results(self): @@ -303,9 +314,9 @@ def _read_results(self): return # Open the HDF5 statepoint file - with h5py.File(self._sp_filename, 'r') as f: + with h5py.File(self._sp_filename, "r") as f: # Extract Tally data from the file - data = f[f'tallies/tally {self.id}/results'] + data = f[f"tallies/tally {self.id}/results"] sum_ = data[:, :, 0] sum_sq = data[:, :, 1] @@ -320,7 +331,9 @@ def _read_results(self): # Convert NumPy arrays to SciPy sparse LIL matrices if self.sparse: self._sum = sps.lil_matrix(self._sum.flatten(), self._sum.shape) - self._sum_sq = sps.lil_matrix(self._sum_sq.flatten(), self._sum_sq.shape) + self._sum_sq = sps.lil_matrix( + self._sum_sq.flatten(), self._sum_sq.shape + ) # Indicate that Tally results have been read self._results_read = True @@ -340,7 +353,7 @@ def sum(self): @sum.setter def sum(self, sum): - cv.check_type('sum', sum, Iterable) + cv.check_type("sum", sum, Iterable) self._sum = sum @property @@ -358,7 +371,7 @@ def sum_sq(self): @sum_sq.setter def sum_sq(self, sum_sq): - cv.check_type('sum_sq', sum_sq, Iterable) + cv.check_type("sum_sq", sum_sq, Iterable) self._sum_sq = sum_sq @property @@ -371,8 +384,7 @@ def mean(self): # Convert NumPy array to SciPy sparse LIL matrix if self.sparse: - self._mean = sps.lil_matrix(self._mean.flatten(), - self._mean.shape) + self._mean = sps.lil_matrix(self._mean.flatten(), self._mean.shape) if self.sparse: return np.reshape(self._mean.toarray(), self.shape) @@ -388,13 +400,15 @@ def std_dev(self): n = self.num_realizations nonzero = np.abs(self.mean) > 0 self._std_dev = np.zeros_like(self.mean) - self._std_dev[nonzero] = np.sqrt((self.sum_sq[nonzero]/n - - self.mean[nonzero]**2)/(n - 1)) + self._std_dev[nonzero] = np.sqrt( + (self.sum_sq[nonzero] / n - self.mean[nonzero] ** 2) / (n - 1) + ) # Convert NumPy array to SciPy sparse LIL matrix if self.sparse: - self._std_dev = sps.lil_matrix(self._std_dev.flatten(), - self._std_dev.shape) + self._std_dev = sps.lil_matrix( + self._std_dev.flatten(), self._std_dev.shape + ) self.with_batch_statistics = True @@ -409,7 +423,7 @@ def with_batch_statistics(self): @with_batch_statistics.setter def with_batch_statistics(self, with_batch_statistics): - cv.check_type('with_batch_statistics', with_batch_statistics, bool) + cv.check_type("with_batch_statistics", with_batch_statistics, bool) self._with_batch_statistics = with_batch_statistics @property @@ -422,8 +436,7 @@ def derivative(self): @derivative.setter def derivative(self, deriv): - cv.check_type('tally derivative', deriv, openmc.TallyDerivative, - none_ok=True) + cv.check_type("tally derivative", deriv, openmc.TallyDerivative, none_ok=True) self._derivative = deriv @property @@ -442,21 +455,22 @@ def sparse(self, sparse): """ - cv.check_type('sparse', sparse, bool) + cv.check_type("sparse", sparse, bool) # Convert NumPy arrays to SciPy sparse LIL matrices if sparse and not self.sparse: if self._sum is not None: self._sum = sps.lil_matrix(self._sum.flatten(), self._sum.shape) if self._sum_sq is not None: - self._sum_sq = sps.lil_matrix(self._sum_sq.flatten(), - self._sum_sq.shape) + self._sum_sq = sps.lil_matrix( + self._sum_sq.flatten(), self._sum_sq.shape + ) if self._mean is not None: - self._mean = sps.lil_matrix(self._mean.flatten(), - self._mean.shape) + self._mean = sps.lil_matrix(self._mean.flatten(), self._mean.shape) if self._std_dev is not None: - self._std_dev = sps.lil_matrix(self._std_dev.flatten(), - self._std_dev.shape) + self._std_dev = sps.lil_matrix( + self._std_dev.flatten(), self._std_dev.shape + ) self._sparse = True @@ -483,8 +497,10 @@ def remove_score(self, score): """ if score not in self.scores: - msg = f'Unable to remove score "{score}" from Tally ' \ - f'ID="{self.id}" since the Tally does not contain this score' + msg = ( + f'Unable to remove score "{score}" from Tally ' + f'ID="{self.id}" since the Tally does not contain this score' + ) raise ValueError(msg) self._scores.remove(score) @@ -500,8 +516,10 @@ def remove_filter(self, old_filter): """ if old_filter not in self.filters: - msg = f'Unable to remove filter "{old_filter}" from Tally ' \ - f'ID="{self.id}" since the Tally does not contain this filter' + msg = ( + f'Unable to remove filter "{old_filter}" from Tally ' + f'ID="{self.id}" since the Tally does not contain this filter' + ) raise ValueError(msg) self._filters.remove(old_filter) @@ -517,8 +535,10 @@ def remove_nuclide(self, nuclide): """ if nuclide not in self.nuclides: - msg = f'Unable to remove nuclide "{nuclide}" from Tally ' \ - f'ID="{self.id}" since the Tally does not contain this nuclide' + msg = ( + f'Unable to remove nuclide "{nuclide}" from Tally ' + f'ID="{self.id}" since the Tally does not contain this nuclide' + ) raise ValueError(msg) self._nuclides.remove(nuclide) @@ -627,11 +647,11 @@ def _can_merge_scores(self, other): else: no_scores_match = False - if score == 'current' and score not in self.scores: + if score == "current" and score not in self.scores: return False # Nuclides cannot be specified on 'flux' scores - if 'flux' in self.scores or 'flux' in other.scores: + if "flux" in self.scores or "flux" in other.scores: if self.nuclides != other.nuclides: return False @@ -766,57 +786,57 @@ def merge(self, other): # Concatenate sum arrays if present in both tallies if self.sum is not None and other_copy.sum is not None: - self_sum = self.get_reshaped_data(value='sum') - other_sum = other_copy.get_reshaped_data(value='sum') + self_sum = self.get_reshaped_data(value="sum") + other_sum = other_copy.get_reshaped_data(value="sum") if join_right: - merged_sum = np.concatenate((self_sum, other_sum), - axis=merge_axis) + merged_sum = np.concatenate((self_sum, other_sum), axis=merge_axis) else: - merged_sum = np.concatenate((other_sum, self_sum), - axis=merge_axis) + merged_sum = np.concatenate((other_sum, self_sum), axis=merge_axis) merged_tally._sum = np.reshape(merged_sum, merged_tally.shape) # Concatenate sum_sq arrays if present in both tallies if self.sum_sq is not None and other.sum_sq is not None: - self_sum_sq = self.get_reshaped_data(value='sum_sq') - other_sum_sq = other_copy.get_reshaped_data(value='sum_sq') + self_sum_sq = self.get_reshaped_data(value="sum_sq") + other_sum_sq = other_copy.get_reshaped_data(value="sum_sq") if join_right: - merged_sum_sq = np.concatenate((self_sum_sq, other_sum_sq), - axis=merge_axis) + merged_sum_sq = np.concatenate( + (self_sum_sq, other_sum_sq), axis=merge_axis + ) else: - merged_sum_sq = np.concatenate((other_sum_sq, self_sum_sq), - axis=merge_axis) + merged_sum_sq = np.concatenate( + (other_sum_sq, self_sum_sq), axis=merge_axis + ) merged_tally._sum_sq = np.reshape(merged_sum_sq, merged_tally.shape) # Concatenate mean arrays if present in both tallies if self.mean is not None and other.mean is not None: - self_mean = self.get_reshaped_data(value='mean') - other_mean = other_copy.get_reshaped_data(value='mean') + self_mean = self.get_reshaped_data(value="mean") + other_mean = other_copy.get_reshaped_data(value="mean") if join_right: - merged_mean = np.concatenate((self_mean, other_mean), - axis=merge_axis) + merged_mean = np.concatenate((self_mean, other_mean), axis=merge_axis) else: - merged_mean = np.concatenate((other_mean, self_mean), - axis=merge_axis) + merged_mean = np.concatenate((other_mean, self_mean), axis=merge_axis) merged_tally._mean = np.reshape(merged_mean, merged_tally.shape) # Concatenate std. dev. arrays if present in both tallies if self.std_dev is not None and other.std_dev is not None: - self_std_dev = self.get_reshaped_data(value='std_dev') - other_std_dev = other_copy.get_reshaped_data(value='std_dev') + self_std_dev = self.get_reshaped_data(value="std_dev") + other_std_dev = other_copy.get_reshaped_data(value="std_dev") if join_right: - merged_std_dev = np.concatenate((self_std_dev, other_std_dev), - axis=merge_axis) + merged_std_dev = np.concatenate( + (self_std_dev, other_std_dev), axis=merge_axis + ) else: - merged_std_dev = np.concatenate((other_std_dev, self_std_dev), - axis=merge_axis) + merged_std_dev = np.concatenate( + (other_std_dev, self_std_dev), axis=merge_axis + ) merged_tally._std_dev = np.reshape(merged_std_dev, merged_tally.shape) @@ -841,7 +861,7 @@ def to_xml_element(self): element.set("id", str(self.id)) # Optional Tally name - if self.name != '': + if self.name != "": element.set("name", self.name) # Multiply by density @@ -851,21 +871,23 @@ def to_xml_element(self): # Optional Tally filters if len(self.filters) > 0: subelement = ET.SubElement(element, "filters") - subelement.text = ' '.join(str(f.id) for f in self.filters) + subelement.text = " ".join(str(f.id) for f in self.filters) # Optional Nuclides if self.nuclides: subelement = ET.SubElement(element, "nuclides") - subelement.text = ' '.join(str(n) for n in self.nuclides) + subelement.text = " ".join(str(n) for n in self.nuclides) # Scores if len(self.scores) == 0: - msg = f'Unable to get XML for Tally ID="{self.id}" since it does ' \ - 'not contain any scores' + msg = ( + f'Unable to get XML for Tally ID="{self.id}" since it does ' + "not contain any scores" + ) raise ValueError(msg) subelement = ET.SubElement(element, "scores") - subelement.text = ' '.join(str(x) for x in self.scores) + subelement.text = " ".join(str(x) for x in self.scores) # Tally estimator type if self.estimator is not None: @@ -900,46 +922,46 @@ def from_xml_element(cls, elem, **kwargs): Tally object """ - tally_id = int(elem.get('id')) - name = elem.get('name', '') + tally_id = int(elem.get("id")) + name = elem.get("name", "") tally = cls(tally_id=tally_id, name=name) - text = get_text(elem, 'multiply_density') + text = get_text(elem, "multiply_density") if text is not None: - tally.multiply_density = text in ('true', '1') + tally.multiply_density = text in ("true", "1") # Read filters - filters_elem = elem.find('filters') + filters_elem = elem.find("filters") if filters_elem is not None: filter_ids = [int(x) for x in filters_elem.text.split()] - tally.filters = [kwargs['filters'][uid] for uid in filter_ids] + tally.filters = [kwargs["filters"][uid] for uid in filter_ids] # Read nuclides - nuclides_elem = elem.find('nuclides') + nuclides_elem = elem.find("nuclides") if nuclides_elem is not None: tally.nuclides = nuclides_elem.text.split() # Read scores - scores_elem = elem.find('scores') + scores_elem = elem.find("scores") if scores_elem is not None: tally.scores = scores_elem.text.split() # Set estimator - estimator_elem = elem.find('estimator') + estimator_elem = elem.find("estimator") if estimator_elem is not None: tally.estimator = estimator_elem.text # Read triggers tally.triggers = [ openmc.Trigger.from_xml_element(trigger_elem) - for trigger_elem in elem.findall('trigger') + for trigger_elem in elem.findall("trigger") ] # Read tally derivative - deriv_elem = elem.find('derivative') + deriv_elem = elem.find("derivative") if deriv_elem is not None: deriv_id = int(deriv_elem.text) - tally.derivative = kwargs['derivatives'][deriv_id] + tally.derivative = kwargs["derivatives"][deriv_id] return tally @@ -996,8 +1018,7 @@ def find_filter(self, filter_type): return test_filter # If we did not find the Filter, throw an Exception - msg = f'Unable to find filter type "{filter_type}" in Tally ' \ - f'ID="{self.id}"' + msg = f'Unable to find filter type "{filter_type}" in Tally ' f'ID="{self.id}"' raise ValueError(msg) def get_nuclide_index(self, nuclide): @@ -1032,8 +1053,10 @@ def get_nuclide_index(self, nuclide): if test_nuclide == nuclide: return i - msg = (f'Unable to get the nuclide index for Tally since "{nuclide}" ' - 'is not one of the nuclides') + msg = ( + f'Unable to get the nuclide index for Tally since "{nuclide}" ' + "is not one of the nuclides" + ) raise KeyError(msg) def get_score_index(self, score): @@ -1061,8 +1084,10 @@ def get_score_index(self, score): score_index = self.scores.index(score) except ValueError: - msg = f'Unable to get the score index for Tally since "{score}" ' \ - 'is not one of the scores' + msg = ( + f'Unable to get the score index for Tally since "{score}" ' + "is not one of the scores" + ) raise ValueError(msg) return score_index @@ -1100,8 +1125,8 @@ def get_filter_indices(self, filters=[], filter_bins=[]): """ - cv.check_type('filters', filters, Iterable, openmc.FilterMeta) - cv.check_type('filter_bins', filter_bins, Iterable, tuple) + cv.check_type("filters", filters, Iterable, openmc.FilterMeta) + cv.check_type("filter_bins", filter_bins, Iterable, tuple) # If user did not specify any specific Filters, use them all if not filters: @@ -1150,7 +1175,7 @@ def get_nuclide_indices(self, nuclides): """ - cv.check_iterable_type('nuclides', nuclides, str) + cv.check_iterable_type("nuclides", nuclides, str) # If user did not specify any specific Nuclides, use them all if not nuclides: @@ -1184,9 +1209,11 @@ def get_score_indices(self, scores): for score in scores: if not isinstance(score, (str, openmc.CrossScore)): - msg = f'Unable to get score indices for score "{score}" in ' \ - f'ID="{self.id}" since it is not a string or CrossScore ' \ - 'Tally' + msg = ( + f'Unable to get score indices for score "{score}" in ' + f'ID="{self.id}" since it is not a string or CrossScore ' + "Tally" + ) raise ValueError(msg) # Determine the score indices from any of the requested scores @@ -1201,8 +1228,9 @@ def get_score_indices(self, scores): return score_indices - def get_values(self, scores=[], filters=[], filter_bins=[], - nuclides=[], value='mean'): + def get_values( + self, scores=[], filters=[], filter_bins=[], nuclides=[], value="mean" + ): """Returns one or more tallied values given a list of scores, filters, filter bins and nuclides. @@ -1253,11 +1281,13 @@ def get_values(self, scores=[], filters=[], filter_bins=[], """ # Ensure that the tally has data - if (value == 'mean' and self.mean is None) or \ - (value == 'std_dev' and self.std_dev is None) or \ - (value == 'rel_err' and self.mean is None) or \ - (value == 'sum' and self.sum is None) or \ - (value == 'sum_sq' and self.sum_sq is None): + if ( + (value == "mean" and self.mean is None) + or (value == "std_dev" and self.std_dev is None) + or (value == "rel_err" and self.mean is None) + or (value == "sum" and self.sum is None) + or (value == "sum_sq" and self.sum_sq is None) + ): msg = f'The Tally ID="{self.id}" has no data to return' raise ValueError(msg) @@ -1270,26 +1300,35 @@ def get_values(self, scores=[], filters=[], filter_bins=[], indices = np.ix_(filter_indices, nuclide_indices, score_indices) # Return the desired result from Tally - if value == 'mean': + if value == "mean": data = self.mean[indices] - elif value == 'std_dev': + elif value == "std_dev": data = self.std_dev[indices] - elif value == 'rel_err': + elif value == "rel_err": data = self.std_dev[indices] / self.mean[indices] - elif value == 'sum': + elif value == "sum": data = self.sum[indices] - elif value == 'sum_sq': + elif value == "sum_sq": data = self.sum_sq[indices] else: - msg = f'Unable to return results from Tally ID="{value}" since ' \ - f'the requested value "{self.id}" is not \'mean\', ' \ - '\'std_dev\', \'rel_err\', \'sum\', or \'sum_sq\'' + msg = ( + f'Unable to return results from Tally ID="{value}" since ' + f"the requested value \"{self.id}\" is not 'mean', " + "'std_dev', 'rel_err', 'sum', or 'sum_sq'" + ) raise LookupError(msg) return data - def get_pandas_dataframe(self, filters=True, nuclides=True, scores=True, - derivative=True, paths=True, float_format='{:.2e}'): + def get_pandas_dataframe( + self, + filters=True, + nuclides=True, + scores=True, + derivative=True, + paths=True, + float_format="{:.2e}", + ): """Build a Pandas DataFrame for the Tally data. This method constructs a Pandas DataFrame object for the Tally data @@ -1347,21 +1386,20 @@ def get_pandas_dataframe(self, filters=True, nuclides=True, scores=True, if filters: # Append each Filter's DataFrame to the overall DataFrame for f, stride in zip(self.filters, self.filter_strides): - filter_df = f.get_pandas_dataframe( - data_size, stride, paths=paths) + filter_df = f.get_pandas_dataframe(data_size, stride, paths=paths) df = pd.concat([df, filter_df], axis=1) # Include DataFrame column for nuclides if user requested it if nuclides: nuclides = [] - column_name = 'nuclide' + column_name = "nuclide" for nuclide in self.nuclides: if isinstance(nuclide, openmc.Nuclide): nuclides.append(nuclide.name) elif isinstance(nuclide, openmc.AggregateNuclide): nuclides.append(nuclide.name) - column_name = f'{nuclide.aggregate_op}(nuclide)' + column_name = f"{nuclide.aggregate_op}(nuclide)" else: nuclides.append(nuclide) @@ -1373,34 +1411,34 @@ def get_pandas_dataframe(self, filters=True, nuclides=True, scores=True, # Include column for scores if user requested it if scores: scores = [] - column_name = 'score' + column_name = "score" for score in self.scores: if isinstance(score, (str, openmc.CrossScore)): scores.append(str(score)) elif isinstance(score, openmc.AggregateScore): scores.append(score.name) - column_name = f'{score.aggregate_op}(score)' + column_name = f"{score.aggregate_op}(score)" tile_factor = data_size / len(self.scores) df[column_name] = np.tile(scores, int(tile_factor)) # Include columns for derivatives if user requested it if derivative and (self.derivative is not None): - df['d_variable'] = self.derivative.variable + df["d_variable"] = self.derivative.variable if self.derivative.material is not None: - df['d_material'] = self.derivative.material + df["d_material"] = self.derivative.material if self.derivative.nuclide is not None: - df['d_nuclide'] = self.derivative.nuclide + df["d_nuclide"] = self.derivative.nuclide # Append columns with mean, std. dev. for each tally bin - df['mean'] = self.mean.ravel() - df['std. dev.'] = self.std_dev.ravel() + df["mean"] = self.mean.ravel() + df["std. dev."] = self.std_dev.ravel() df = df.dropna(axis=1) # Expand the columns into Pandas MultiIndices for readability - if pd.__version__ >= '0.16': + if pd.__version__ >= "0.16": columns = copy.deepcopy(df.columns.values) # Convert all elements in columns list to tuples @@ -1414,7 +1452,7 @@ def get_pandas_dataframe(self, filters=True, nuclides=True, scores=True, delta_len = max_len_column - len(column) if delta_len > 0: new_column = list(column) - new_column.extend(['']*delta_len) + new_column.extend([""] * delta_len) columns[i] = tuple(new_column) # Create and set a MultiIndex for the DataFrame's columns, but only @@ -1428,7 +1466,7 @@ def get_pandas_dataframe(self, filters=True, nuclides=True, scores=True, return df - def get_reshaped_data(self, value='mean', expand_dims=False): + def get_reshaped_data(self, value="mean", expand_dims=False): """Returns an array of tally data with one dimension per filter. The tally data in OpenMC is stored as a 3D array with the dimensions @@ -1499,8 +1537,14 @@ def get_reshaped_data(self, value='mean', expand_dims=False): return data - def hybrid_product(self, other, binary_op, filter_product=None, - nuclide_product=None, score_product=None): + def hybrid_product( + self, + other, + binary_op, + filter_product=None, + nuclide_product=None, + score_product=None, + ): """Combines filters, scores and nuclides with another tally. This is a helper method for the tally arithmetic operator overloaded @@ -1551,35 +1595,39 @@ def hybrid_product(self, other, binary_op, filter_product=None, # Set default value for filter product if it was not set if filter_product is None: - filter_product = 'entrywise' - elif filter_product == 'tensor': - msg = 'Unable to perform Tally arithmetic with a tensor product' \ - 'for the filter data as this is not currently supported.' + filter_product = "entrywise" + elif filter_product == "tensor": + msg = ( + "Unable to perform Tally arithmetic with a tensor product" + "for the filter data as this is not currently supported." + ) raise ValueError(msg) # Set default value for nuclide product if it was not set if nuclide_product is None: if self.nuclides == other.nuclides: - nuclide_product = 'entrywise' + nuclide_product = "entrywise" else: - nuclide_product = 'tensor' + nuclide_product = "tensor" # Set default value for score product if it was not set if score_product is None: if self.scores == other.scores: - score_product = 'entrywise' + score_product = "entrywise" else: - score_product = 'tensor' + score_product = "tensor" # Check product types - cv.check_value('filter product', filter_product, _PRODUCT_TYPES) - cv.check_value('nuclide product', nuclide_product, _PRODUCT_TYPES) - cv.check_value('score product', score_product, _PRODUCT_TYPES) + cv.check_value("filter product", filter_product, _PRODUCT_TYPES) + cv.check_value("nuclide product", nuclide_product, _PRODUCT_TYPES) + cv.check_value("score product", score_product, _PRODUCT_TYPES) # Check that results have been read if not other.derived and other.sum is None: - msg = f'Unable to use tally arithmetic with Tally ' \ - f'ID="{other.id}" since it does not contain any results.' + msg = ( + f"Unable to use tally arithmetic with Tally " + f'ID="{other.id}" since it does not contain any results.' + ) raise ValueError(msg) new_tally = Tally() @@ -1591,8 +1639,8 @@ def hybrid_product(self, other, binary_op, filter_product=None, new_tally._sp_filename = self._sp_filename # Construct a combined derived name from the two tally operands - if self.name != '' and other.name != '': - new_name = f'({self.name} {binary_op} {other.name})' + if self.name != "" and other.name != "": + new_name = f"({self.name} {binary_op} {other.name})" new_tally.name = new_name # Query the mean and std dev so the tally data is read in from file @@ -1608,41 +1656,46 @@ def hybrid_product(self, other, binary_op, filter_product=None, other_copy.sparse = False # Align the tally data based on desired hybrid product - data = self_copy._align_tally_data(other_copy, filter_product, - nuclide_product, score_product) + data = self_copy._align_tally_data( + other_copy, filter_product, nuclide_product, score_product + ) # Perform tally arithmetic operation - if binary_op == '+': - new_tally._mean = data['self']['mean'] + data['other']['mean'] - new_tally._std_dev = np.sqrt(data['self']['std. dev.']**2 + - data['other']['std. dev.']**2) - elif binary_op == '-': - new_tally._mean = data['self']['mean'] - data['other']['mean'] - new_tally._std_dev = np.sqrt(data['self']['std. dev.']**2 + - data['other']['std. dev.']**2) - elif binary_op == '*': - with np.errstate(divide='ignore', invalid='ignore'): - self_rel_err = data['self']['std. dev.'] / data['self']['mean'] - other_rel_err = data['other']['std. dev.'] / data['other']['mean'] - new_tally._mean = data['self']['mean'] * data['other']['mean'] - new_tally._std_dev = np.abs(new_tally.mean) * \ - np.sqrt(self_rel_err**2 + other_rel_err**2) - elif binary_op == '/': - with np.errstate(divide='ignore', invalid='ignore'): - self_rel_err = data['self']['std. dev.'] / data['self']['mean'] - other_rel_err = data['other']['std. dev.'] / data['other']['mean'] - new_tally._mean = data['self']['mean'] / data['other']['mean'] - new_tally._std_dev = np.abs(new_tally.mean) * \ - np.sqrt(self_rel_err**2 + other_rel_err**2) - elif binary_op == '^': - with np.errstate(divide='ignore', invalid='ignore'): - mean_ratio = data['other']['mean'] / data['self']['mean'] - first_term = mean_ratio * data['self']['std. dev.'] - second_term = \ - np.log(data['self']['mean']) * data['other']['std. dev.'] - new_tally._mean = data['self']['mean'] ** data['other']['mean'] - new_tally._std_dev = np.abs(new_tally.mean) * \ - np.sqrt(first_term**2 + second_term**2) + if binary_op == "+": + new_tally._mean = data["self"]["mean"] + data["other"]["mean"] + new_tally._std_dev = np.sqrt( + data["self"]["std. dev."] ** 2 + data["other"]["std. dev."] ** 2 + ) + elif binary_op == "-": + new_tally._mean = data["self"]["mean"] - data["other"]["mean"] + new_tally._std_dev = np.sqrt( + data["self"]["std. dev."] ** 2 + data["other"]["std. dev."] ** 2 + ) + elif binary_op == "*": + with np.errstate(divide="ignore", invalid="ignore"): + self_rel_err = data["self"]["std. dev."] / data["self"]["mean"] + other_rel_err = data["other"]["std. dev."] / data["other"]["mean"] + new_tally._mean = data["self"]["mean"] * data["other"]["mean"] + new_tally._std_dev = np.abs(new_tally.mean) * np.sqrt( + self_rel_err**2 + other_rel_err**2 + ) + elif binary_op == "/": + with np.errstate(divide="ignore", invalid="ignore"): + self_rel_err = data["self"]["std. dev."] / data["self"]["mean"] + other_rel_err = data["other"]["std. dev."] / data["other"]["mean"] + new_tally._mean = data["self"]["mean"] / data["other"]["mean"] + new_tally._std_dev = np.abs(new_tally.mean) * np.sqrt( + self_rel_err**2 + other_rel_err**2 + ) + elif binary_op == "^": + with np.errstate(divide="ignore", invalid="ignore"): + mean_ratio = data["other"]["mean"] / data["self"]["mean"] + first_term = mean_ratio * data["self"]["std. dev."] + second_term = np.log(data["self"]["mean"]) * data["other"]["std. dev."] + new_tally._mean = data["self"]["mean"] ** data["other"]["mean"] + new_tally._std_dev = np.abs(new_tally.mean) * np.sqrt( + first_term**2 + second_term**2 + ) # Convert any infs and nans to zero new_tally._mean[np.isinf(new_tally._mean)] = 0 @@ -1659,31 +1712,31 @@ def hybrid_product(self, other, binary_op, filter_product=None, new_tally.num_realizations = self_copy.num_realizations # Add filters to the new tally - if filter_product == 'entrywise': + if filter_product == "entrywise": for self_filter in self_copy.filters: new_tally.filters.append(self_filter) else: all_filters = [self_copy.filters, other_copy.filters] for self_filter, other_filter in product(*all_filters): - new_filter = openmc.CrossFilter(self_filter, other_filter, - binary_op) + new_filter = openmc.CrossFilter(self_filter, other_filter, binary_op) new_tally.filters.append(new_filter) # Add nuclides to the new tally - if nuclide_product == 'entrywise': + if nuclide_product == "entrywise": for self_nuclide in self_copy.nuclides: new_tally.nuclides.append(self_nuclide) else: all_nuclides = [self_copy.nuclides, other_copy.nuclides] for self_nuclide, other_nuclide in product(*all_nuclides): - new_nuclide = openmc.CrossNuclide(self_nuclide, other_nuclide, - binary_op) + new_nuclide = openmc.CrossNuclide( + self_nuclide, other_nuclide, binary_op + ) new_tally.nuclides.append(new_nuclide) # Define helper function that handles score units appropriately # depending on the binary operator def cross_score(score1, score2, binary_op): - if binary_op == '+' or binary_op == '-': + if binary_op == "+" or binary_op == "-": if score1 == score2: return score1 else: @@ -1692,7 +1745,7 @@ def cross_score(score1, score2, binary_op): return openmc.CrossScore(score1, score2, binary_op) # Add scores to the new tally - if score_product == 'entrywise': + if score_product == "entrywise": for self_score in self_copy.scores: new_score = cross_score(self_score, self_score, binary_op) new_tally.scores.append(new_score) @@ -1713,8 +1766,7 @@ def filter_strides(self): stride *= self_filter.num_bins return all_strides[::-1] - def _align_tally_data(self, other, filter_product, nuclide_product, - score_product): + def _align_tally_data(self, other, filter_product, nuclide_product, score_product): """Aligns data from two tallies for tally arithmetic. This is a helper method to construct a dict of dicts of the "aligned" @@ -1776,7 +1828,7 @@ def _align_tally_data(self, other, filter_product, nuclide_product, # Repeat and tile the data by nuclide in preparation for performing # the tensor product across nuclides. - if nuclide_product == 'tensor': + if nuclide_product == "tensor": self._mean = np.repeat(self.mean, other.num_nuclides, axis=1) self._std_dev = np.repeat(self.std_dev, other.num_nuclides, axis=1) other._mean = np.tile(other.mean, (1, self.num_nuclides, 1)) @@ -1794,15 +1846,13 @@ def _align_tally_data(self, other, filter_product, nuclide_product, # Add nuclides present in self but not in other to other for nuclide in other_missing_nuclides: other._mean = np.insert(other.mean, other.num_nuclides, 0, axis=1) - other._std_dev = np.insert(other.std_dev, other.num_nuclides, 0, - axis=1) + other._std_dev = np.insert(other.std_dev, other.num_nuclides, 0, axis=1) other.nuclides.append(nuclide) # Add nuclides present in other but not in self to self for nuclide in self_missing_nuclides: self._mean = np.insert(self.mean, self.num_nuclides, 0, axis=1) - self._std_dev = np.insert(self.std_dev, self.num_nuclides, 0, - axis=1) + self._std_dev = np.insert(self.std_dev, self.num_nuclides, 0, axis=1) self.nuclides.append(nuclide) # Align other nuclides with self nuclides @@ -1815,7 +1865,7 @@ def _align_tally_data(self, other, filter_product, nuclide_product, # Repeat and tile the data by score in preparation for performing # the tensor product across scores. - if score_product == 'tensor': + if score_product == "tensor": self._mean = np.repeat(self.mean, other.num_scores, axis=2) self._std_dev = np.repeat(self.std_dev, other.num_scores, axis=2) other._mean = np.tile(other.mean, (1, 1, self.num_scores)) @@ -1851,12 +1901,12 @@ def _align_tally_data(self, other, filter_product, nuclide_product, other._swap_scores(score, other.scores[i]) data = {} - data['self'] = {} - data['other'] = {} - data['self']['mean'] = self.mean - data['other']['mean'] = other.mean - data['self']['std. dev.'] = self.std_dev - data['other']['std. dev.'] = other.std_dev + data["self"] = {} + data["other"] = {} + data["self"]["mean"] = self.mean + data["other"]["mean"] = other.mean + data["self"]["std. dev."] = self.std_dev + data["other"]["std. dev."] = other.std_dev return data def _swap_filters(self, filter1, filter2): @@ -1881,19 +1931,23 @@ def _swap_filters(self, filter1, filter2): """ - cv.check_type('filter1', filter1, _FILTER_CLASSES) - cv.check_type('filter2', filter2, _FILTER_CLASSES) + cv.check_type("filter1", filter1, _FILTER_CLASSES) + cv.check_type("filter2", filter2, _FILTER_CLASSES) # Check that the filters exist in the tally and are not the same if filter1 == filter2: return elif filter1 not in self.filters: - msg = f'Unable to swap "{filter1.type}" filter1 in Tally ' \ - f'ID="{self.id}" since it does not contain such a filter' + msg = ( + f'Unable to swap "{filter1.type}" filter1 in Tally ' + f'ID="{self.id}" since it does not contain such a filter' + ) raise ValueError(msg) elif filter2 not in self.filters: - msg = f'Unable to swap "{filter2.type}" filter2 in Tally ' \ - f'ID="{self.id}" since it does not contain such a filter' + msg = ( + f'Unable to swap "{filter2.type}" filter2 in Tally ' + f'ID="{self.id}" since it does not contain such a filter' + ) raise ValueError(msg) # Construct lists of tuples for the bins in each of the two filters @@ -1922,11 +1976,13 @@ def _swap_filters(self, filter1, filter2): if self.mean is not None: mean[i] = self.get_values( - filters=filters, filter_bins=filter_bins, value='mean') + filters=filters, filter_bins=filter_bins, value="mean" + ) if self.std_dev is not None: std_dev[i] = self.get_values( - filters=filters, filter_bins=filter_bins, value='std_dev') + filters=filters, filter_bins=filter_bins, value="std_dev" + ) # Swap the filters in the copied version of this Tally filter1_index = self.filters.index(filter1) @@ -1970,24 +2026,30 @@ def _swap_nuclides(self, nuclide1, nuclide2): # Check that results have been read if not self.derived and self.sum is None: - msg = f'Unable to use tally arithmetic with Tally ID="{self.id}" ' \ - 'since it does not contain any results.' + msg = ( + f'Unable to use tally arithmetic with Tally ID="{self.id}" ' + "since it does not contain any results." + ) raise ValueError(msg) - cv.check_type('nuclide1', nuclide1, _NUCLIDE_CLASSES) - cv.check_type('nuclide2', nuclide2, _NUCLIDE_CLASSES) + cv.check_type("nuclide1", nuclide1, _NUCLIDE_CLASSES) + cv.check_type("nuclide2", nuclide2, _NUCLIDE_CLASSES) # Check that the nuclides exist in the tally and are not the same if nuclide1 == nuclide2: - msg = 'Unable to swap a nuclide with itself' + msg = "Unable to swap a nuclide with itself" raise ValueError(msg) elif nuclide1 not in self.nuclides: - msg = f'Unable to swap nuclide1 "{nuclide1.name}" in Tally ' \ - f'ID="{self.id}" since it does not contain such a nuclide' + msg = ( + f'Unable to swap nuclide1 "{nuclide1.name}" in Tally ' + f'ID="{self.id}" since it does not contain such a nuclide' + ) raise ValueError(msg) elif nuclide2 not in self.nuclides: - msg = f'Unable to swap "{nuclide2.name}" nuclide2 in Tally ' \ - f'ID="{self.id}" since it does not contain such a nuclide' + msg = ( + f'Unable to swap "{nuclide2.name}" nuclide2 in Tally ' + f'ID="{self.id}" since it does not contain such a nuclide' + ) raise ValueError(msg) # Swap the nuclides in the Tally @@ -2035,31 +2097,41 @@ def _swap_scores(self, score1, score2): # Check that results have been read if not self.derived and self.sum is None: - msg = 'Unable to use tally arithmetic with Tally ID="{}" ' \ - 'since it does not contain any results.'.format(self.id) + msg = ( + 'Unable to use tally arithmetic with Tally ID="{}" ' + "since it does not contain any results.".format(self.id) + ) raise ValueError(msg) # Check that the scores are valid if not isinstance(score1, (str, openmc.CrossScore)): - msg = 'Unable to swap score1 "{}" in Tally ID="{}" since it is ' \ - 'not a string or CrossScore'.format(score1, self.id) + msg = ( + 'Unable to swap score1 "{}" in Tally ID="{}" since it is ' + "not a string or CrossScore".format(score1, self.id) + ) raise ValueError(msg) elif not isinstance(score2, (str, openmc.CrossScore)): - msg = 'Unable to swap score2 "{}" in Tally ID="{}" since it is ' \ - 'not a string or CrossScore'.format(score2, self.id) + msg = ( + 'Unable to swap score2 "{}" in Tally ID="{}" since it is ' + "not a string or CrossScore".format(score2, self.id) + ) raise ValueError(msg) # Check that the scores exist in the tally and are not the same if score1 == score2: - msg = 'Unable to swap a score with itself' + msg = "Unable to swap a score with itself" raise ValueError(msg) elif score1 not in self.scores: - msg = 'Unable to swap score1 "{}" in Tally ID="{}" since it ' \ - 'does not contain such a score'.format(score1, self.id) + msg = ( + 'Unable to swap score1 "{}" in Tally ID="{}" since it ' + "does not contain such a score".format(score1, self.id) + ) raise ValueError(msg) elif score2 not in self.scores: - msg = 'Unable to swap score2 "{}" in Tally ID="{}" since it ' \ - 'does not contain such a score'.format(score2, self.id) + msg = ( + 'Unable to swap score2 "{}" in Tally ID="{}" since it ' + "does not contain such a score".format(score2, self.id) + ) raise ValueError(msg) # Swap the scores in the Tally @@ -2119,19 +2191,21 @@ def __add__(self, other): # Check that results have been read if not self.derived and self.sum is None: - msg = 'Unable to use tally arithmetic with Tally ID="{}" ' \ - 'since it does not contain any results.'.format(self.id) + msg = ( + 'Unable to use tally arithmetic with Tally ID="{}" ' + "since it does not contain any results.".format(self.id) + ) raise ValueError(msg) if isinstance(other, Tally): - new_tally = self.hybrid_product(other, binary_op='+') + new_tally = self.hybrid_product(other, binary_op="+") # If both tally operands were sparse, sparsify the new tally if self.sparse and other.sparse: new_tally.sparse = True elif isinstance(other, Real): - new_tally = Tally(name='derived') + new_tally = Tally(name="derived") new_tally._derived = True new_tally.with_batch_statistics = True new_tally.name = self.name @@ -2191,19 +2265,21 @@ def __sub__(self, other): # Check that results have been read if not self.derived and self.sum is None: - msg = 'Unable to use tally arithmetic with Tally ID="{}" ' \ - 'since it does not contain any results.'.format(self.id) + msg = ( + 'Unable to use tally arithmetic with Tally ID="{}" ' + "since it does not contain any results.".format(self.id) + ) raise ValueError(msg) if isinstance(other, Tally): - new_tally = self.hybrid_product(other, binary_op='-') + new_tally = self.hybrid_product(other, binary_op="-") # If both tally operands were sparse, sparsify the new tally if self.sparse and other.sparse: new_tally.sparse = True elif isinstance(other, Real): - new_tally = Tally(name='derived') + new_tally = Tally(name="derived") new_tally._derived = True new_tally.name = self.name new_tally._mean = self.mean - other @@ -2262,19 +2338,21 @@ def __mul__(self, other): # Check that results have been read if not self.derived and self.sum is None: - msg = 'Unable to use tally arithmetic with Tally ID="{}" ' \ - 'since it does not contain any results.'.format(self.id) + msg = ( + 'Unable to use tally arithmetic with Tally ID="{}" ' + "since it does not contain any results.".format(self.id) + ) raise ValueError(msg) if isinstance(other, Tally): - new_tally = self.hybrid_product(other, binary_op='*') + new_tally = self.hybrid_product(other, binary_op="*") # If original tally operands were sparse, sparsify the new tally if self.sparse and other.sparse: new_tally.sparse = True elif isinstance(other, Real): - new_tally = Tally(name='derived') + new_tally = Tally(name="derived") new_tally._derived = True new_tally.name = self.name new_tally._mean = self.mean * other @@ -2333,23 +2411,25 @@ def __truediv__(self, other): # Check that results have been read if not self.derived and self.sum is None: - msg = 'Unable to use tally arithmetic with Tally ID="{}" ' \ - 'since it does not contain any results.'.format(self.id) + msg = ( + 'Unable to use tally arithmetic with Tally ID="{}" ' + "since it does not contain any results.".format(self.id) + ) raise ValueError(msg) if isinstance(other, Tally): - new_tally = self.hybrid_product(other, binary_op='/') + new_tally = self.hybrid_product(other, binary_op="/") # If original tally operands were sparse, sparsify the new tally if self.sparse and other.sparse: new_tally.sparse = True elif isinstance(other, Real): - new_tally = Tally(name='derived') + new_tally = Tally(name="derived") new_tally._derived = True new_tally.name = self.name new_tally._mean = self.mean / other - new_tally._std_dev = self.std_dev * np.abs(1. / other) + new_tally._std_dev = self.std_dev * np.abs(1.0 / other) new_tally.estimator = self.estimator new_tally.with_summary = self.with_summary new_tally.num_realizations = self.num_realizations @@ -2407,22 +2487,24 @@ def __pow__(self, power): # Check that results have been read if not self.derived and self.sum is None: - msg = 'Unable to use tally arithmetic with Tally ID="{}" ' \ - 'since it does not contain any results.'.format(self.id) + msg = ( + 'Unable to use tally arithmetic with Tally ID="{}" ' + "since it does not contain any results.".format(self.id) + ) raise ValueError(msg) if isinstance(power, Tally): - new_tally = self.hybrid_product(power, binary_op='^') + new_tally = self.hybrid_product(power, binary_op="^") # If original tally operand was sparse, sparsify the new tally if self.sparse: new_tally.sparse = True elif isinstance(power, Real): - new_tally = Tally(name='derived') + new_tally = Tally(name="derived") new_tally._derived = True new_tally.name = self.name - new_tally._mean = self._mean ** power + new_tally._mean = self._mean**power self_rel_err = self.std_dev / self.mean new_tally._std_dev = np.abs(new_tally._mean * power * self_rel_err) new_tally.estimator = self.estimator @@ -2478,7 +2560,7 @@ def __rsub__(self, other): """ - return -1. * self + other + return -1.0 * self + other def __rmul__(self, other): """Right multiplication with a scalar value. @@ -2545,8 +2627,9 @@ def __neg__(self): new_tally = self * -1 return new_tally - def get_slice(self, scores=[], filters=[], filter_bins=[], nuclides=[], - squeeze=False): + def get_slice( + self, scores=[], filters=[], filter_bins=[], nuclides=[], squeeze=False + ): """Build a sliced tally for the specified filters, scores and nuclides. This method constructs a new tally to encapsulate a subset of the data @@ -2593,8 +2676,10 @@ def get_slice(self, scores=[], filters=[], filter_bins=[], nuclides=[], # Ensure that the tally has data if not self.derived and self.sum is None: - msg = 'Unable to use tally arithmetic with Tally ID="{}" ' \ - 'since it does not contain any results.'.format(self.id) + msg = ( + 'Unable to use tally arithmetic with Tally ID="{}" ' + "since it does not contain any results.".format(self.id) + ) raise ValueError(msg) # Create deep copy of tally to return as sliced tally @@ -2607,20 +2692,20 @@ def get_slice(self, scores=[], filters=[], filter_bins=[], nuclides=[], new_tally.sparse = False if not self.derived and self.sum is not None: - new_sum = self.get_values(scores, filters, filter_bins, - nuclides, 'sum') + new_sum = self.get_values(scores, filters, filter_bins, nuclides, "sum") new_tally.sum = new_sum if not self.derived and self.sum_sq is not None: - new_sum_sq = self.get_values(scores, filters, filter_bins, - nuclides, 'sum_sq') + new_sum_sq = self.get_values( + scores, filters, filter_bins, nuclides, "sum_sq" + ) new_tally.sum_sq = new_sum_sq if self.mean is not None: - new_mean = self.get_values(scores, filters, filter_bins, - nuclides, 'mean') + new_mean = self.get_values(scores, filters, filter_bins, nuclides, "mean") new_tally._mean = new_mean if self.std_dev is not None: - new_std_dev = self.get_values(scores, filters, filter_bins, - nuclides, 'std_dev') + new_std_dev = self.get_values( + scores, filters, filter_bins, nuclides, "std_dev" + ) new_tally._std_dev = new_std_dev # SCORES @@ -2664,12 +2749,12 @@ def get_slice(self, scores=[], filters=[], filter_bins=[], nuclides=[], new_tally.filters.remove(f) continue else: - raise RuntimeError('Cannot remove sliced filter with ' - 'more than one bin.') + raise RuntimeError( + "Cannot remove sliced filter with " "more than one bin." + ) # Remove and/or reorder filter bins to user specifications - bin_indices = [f.get_bin_index(b) - for b in filter_bins[i]] + bin_indices = [f.get_bin_index(b) for b in filter_bins[i]] bin_indices = np.unique(bin_indices) # Set bins for sliced filter @@ -2689,8 +2774,14 @@ def get_slice(self, scores=[], filters=[], filter_bins=[], nuclides=[], new_tally.sparse = self.sparse return new_tally - def summation(self, scores=[], filter_type=None, - filter_bins=[], nuclides=[], remove_filter=False): + def summation( + self, + scores=[], + filter_type=None, + filter_bins=[], + nuclides=[], + remove_filter=False, + ): """Vectorized sum of tally data across scores, filter bins and/or nuclides using tally aggregation. @@ -2739,8 +2830,8 @@ def summation(self, scores=[], filter_type=None, tally_sum._results_read = self._results_read # Get tally data arrays reshaped with one dimension per filter - mean = self.get_reshaped_data(value='mean') - std_dev = self.get_reshaped_data(value='std_dev') + mean = self.get_reshaped_data(value="mean") + std_dev = self.get_reshaped_data(value="std_dev") # Sum across any filter bins specified by the user if isinstance(filter_type, openmc.FilterMeta): @@ -2759,8 +2850,7 @@ def summation(self, scores=[], filter_type=None, # Only sum across bins specified by the user else: - bin_indices = \ - [find_filter.get_bin_index(bin) for bin in filter_bins] + bin_indices = [find_filter.get_bin_index(bin) for bin in filter_bins] # Sum across the bins in the user-specified filter for i, self_filter in enumerate(self.filters): @@ -2781,8 +2871,9 @@ def summation(self, scores=[], filter_type=None, # Add AggregateFilter to the tally sum if not remove_filter: - filter_sum = openmc.AggregateFilter(self_filter, - [tuple(filter_bins)], 'sum') + filter_sum = openmc.AggregateFilter( + self_filter, [tuple(filter_bins)], "sum" + ) tally_sum.filters.append(filter_sum) # Add a copy of each filter not summed across to the tally sum @@ -2804,7 +2895,7 @@ def summation(self, scores=[], filter_type=None, std_dev = np.sqrt(std_dev) # Add AggregateNuclide to the tally sum - nuclide_sum = openmc.AggregateNuclide(nuclides, 'sum') + nuclide_sum = openmc.AggregateNuclide(nuclides, "sum") tally_sum.nuclides.append(nuclide_sum) # Add a copy of this tally's nuclides to the tally sum @@ -2822,7 +2913,7 @@ def summation(self, scores=[], filter_type=None, std_dev = np.sqrt(std_dev) # Add AggregateScore to the tally sum - score_sum = openmc.AggregateScore(scores, 'sum') + score_sum = openmc.AggregateScore(scores, "sum") tally_sum.scores.append(score_sum) # Add a copy of this tally's scores to the tally sum @@ -2841,8 +2932,14 @@ def summation(self, scores=[], filter_type=None, tally_sum.sparse = self.sparse return tally_sum - def average(self, scores=[], filter_type=None, - filter_bins=[], nuclides=[], remove_filter=False): + def average( + self, + scores=[], + filter_type=None, + filter_bins=[], + nuclides=[], + remove_filter=False, + ): """Vectorized average of tally data across scores, filter bins and/or nuclides using tally aggregation. @@ -2891,8 +2988,8 @@ def average(self, scores=[], filter_type=None, tally_avg._results_read = self._results_read # Get tally data arrays reshaped with one dimension per filter - mean = self.get_reshaped_data(value='mean') - std_dev = self.get_reshaped_data(value='std_dev') + mean = self.get_reshaped_data(value="mean") + std_dev = self.get_reshaped_data(value="std_dev") # Average across any filter bins specified by the user if isinstance(filter_type, openmc.FilterMeta): @@ -2911,8 +3008,7 @@ def average(self, scores=[], filter_type=None, # Only average across bins specified by the user else: - bin_indices = \ - [find_filter.get_bin_index(bin) for bin in filter_bins] + bin_indices = [find_filter.get_bin_index(bin) for bin in filter_bins] # Average across the bins in the user-specified filter for i, self_filter in enumerate(self.filters): @@ -2934,8 +3030,9 @@ def average(self, scores=[], filter_type=None, # Add AggregateFilter to the tally avg if not remove_filter: - filter_sum = openmc.AggregateFilter(self_filter, - [tuple(filter_bins)], 'avg') + filter_sum = openmc.AggregateFilter( + self_filter, [tuple(filter_bins)], "avg" + ) tally_avg.filters.append(filter_sum) # Add a copy of each filter not averaged across to the tally avg @@ -2958,7 +3055,7 @@ def average(self, scores=[], filter_type=None, std_dev = np.sqrt(std_dev) # Add AggregateNuclide to the tally avg - nuclide_avg = openmc.AggregateNuclide(nuclides, 'avg') + nuclide_avg = openmc.AggregateNuclide(nuclides, "avg") tally_avg.nuclides.append(nuclide_avg) # Add a copy of this tally's nuclides to the tally avg @@ -2977,7 +3074,7 @@ def average(self, scores=[], filter_type=None, std_dev = np.sqrt(std_dev) # Add AggregateScore to the tally avg - score_sum = openmc.AggregateScore(scores, 'avg') + score_sum = openmc.AggregateScore(scores, "avg") tally_avg.scores.append(score_sum) # Add a copy of this tally's scores to the tally avg @@ -3022,12 +3119,14 @@ def diagonalize_filter(self, new_filter, filter_position=-1): """ - cv.check_type('new_filter', new_filter, _FILTER_CLASSES) - cv.check_type('filter_position', filter_position, Integral) + cv.check_type("new_filter", new_filter, _FILTER_CLASSES) + cv.check_type("filter_position", filter_position, Integral) if new_filter in self.filters: - msg = 'Unable to diagonalize Tally ID="{}" which already ' \ - 'contains a "{}" filter'.format(self.id, type(new_filter)) + msg = ( + 'Unable to diagonalize Tally ID="{}" which already ' + 'contains a "{}" filter'.format(self.id, type(new_filter)) + ) raise ValueError(msg) # Add the new filter to a copy of this Tally @@ -3037,14 +3136,14 @@ def diagonalize_filter(self, new_filter, filter_position=-1): # Determine "base" indices along the new "diagonal", and the factor # by which the "base" indices should be repeated to account for all # other filter bins in the diagonalized tally - indices = np.arange(0, new_filter.num_bins**2, new_filter.num_bins+1) + indices = np.arange(0, new_filter.num_bins**2, new_filter.num_bins + 1) diag_factor = self.num_filter_bins // new_filter.num_bins diag_indices = np.zeros(self.num_filter_bins, dtype=int) # Determine the filter indices along the new "diagonal" for i in range(diag_factor): start = i * new_filter.num_bins - end = (i+1) * new_filter.num_bins + end = (i + 1) * new_filter.num_bins diag_indices[start:end] = indices + (i * new_filter.num_bins**2) # Inject this Tally's data along the diagonal of the diagonalized Tally @@ -3088,7 +3187,7 @@ class Tallies(cv.CheckedList): """ def __init__(self, tallies=None): - super().__init__(Tally, 'tallies collection') + super().__init__(Tally, "tallies collection") if tallies is not None: self += tallies @@ -3207,8 +3306,7 @@ def _create_derivative_subelements(self, root_element): root_element.append(d.to_xml_element()) def to_xml_element(self, memo=None): - """Creates a 'tallies' element to be written to an XML file. - """ + """Creates a 'tallies' element to be written to an XML file.""" memo = memo if memo is not None else set() element = ET.Element("tallies") self._create_mesh_subelements(element, memo) @@ -3222,7 +3320,7 @@ def to_xml_element(self, memo=None): return element - def export_to_xml(self, path='tallies.xml'): + def export_to_xml(self, path="tallies.xml"): """Create a tallies.xml file that can be used for a simulation. Parameters @@ -3236,11 +3334,11 @@ def export_to_xml(self, path='tallies.xml'): # Check if path is a directory p = Path(path) if p.is_dir(): - p /= 'tallies.xml' + p /= "tallies.xml" # Write the XML Tree to the tallies.xml file tree = ET.ElementTree(root_element) - tree.write(str(p), xml_declaration=True, encoding='utf-8') + tree.write(str(p), xml_declaration=True, encoding="utf-8") @classmethod def from_xml_element(cls, elem, meshes=None): @@ -3263,25 +3361,25 @@ def from_xml_element(cls, elem, meshes=None): """ # Read mesh elements meshes = {} if meshes is None else meshes - for e in elem.findall('mesh'): + for e in elem.findall("mesh"): mesh = MeshBase.from_xml_element(e) meshes[mesh.id] = mesh # Read filter elements filters = {} - for e in elem.findall('filter'): + for e in elem.findall("filter"): filter = openmc.Filter.from_xml_element(e, meshes=meshes) filters[filter.id] = filter # Read derivative elements derivatives = {} - for e in elem.findall('derivative'): + for e in elem.findall("derivative"): deriv = openmc.TallyDerivative.from_xml_element(e) derivatives[deriv.id] = deriv # Read tally elements tallies = [] - for e in elem.findall('tally'): + for e in elem.findall("tally"): tally = openmc.Tally.from_xml_element( e, filters=filters, derivatives=derivatives ) @@ -3290,7 +3388,7 @@ def from_xml_element(cls, elem, meshes=None): return cls(tallies) @classmethod - def from_xml(cls, path='tallies.xml'): + def from_xml(cls, path="tallies.xml"): """Generate tallies from XML file Parameters diff --git a/openmc/tally_derivative.py b/openmc/tally_derivative.py index ff918c1b3c6..5aba6efb93d 100644 --- a/openmc/tally_derivative.py +++ b/openmc/tally_derivative.py @@ -39,8 +39,7 @@ class TallyDerivative(EqualityMixin, IDManagerMixin): next_id = 1 used_ids = set() - def __init__(self, derivative_id=None, variable=None, material=None, - nuclide=None): + def __init__(self, derivative_id=None, variable=None, material=None, nuclide=None): # Initialize Tally class attributes self.id = derivative_id self.variable = variable @@ -48,12 +47,12 @@ def __init__(self, derivative_id=None, variable=None, material=None, self.nuclide = nuclide def __repr__(self): - string = 'Tally Derivative\n' - string += '{: <16}=\t{}\n'.format('\tID', self.id) - string += '{: <16}=\t{}\n'.format('\tVariable', self.variable) - string += '{: <16}=\t{}\n'.format('\tMaterial', self.material) - if self.variable == 'nuclide_density': - string += '{: <16}=\t{}\n'.format('\tNuclide', self.nuclide) + string = "Tally Derivative\n" + string += "{: <16}=\t{}\n".format("\tID", self.id) + string += "{: <16}=\t{}\n".format("\tVariable", self.variable) + string += "{: <16}=\t{}\n".format("\tMaterial", self.material) + if self.variable == "nuclide_density": + string += "{: <16}=\t{}\n".format("\tNuclide", self.nuclide) return string @@ -64,9 +63,12 @@ def variable(self): @variable.setter def variable(self, var): if var is not None: - cv.check_type('derivative variable', var, str) - cv.check_value('derivative variable', var, - ('density', 'nuclide_density', 'temperature')) + cv.check_type("derivative variable", var, str) + cv.check_value( + "derivative variable", + var, + ("density", "nuclide_density", "temperature"), + ) self._variable = var @property @@ -76,7 +78,7 @@ def material(self): @material.setter def material(self, mat): if mat is not None: - cv.check_type('derivative material', mat, Integral) + cv.check_type("derivative material", mat, Integral) self._material = mat @property @@ -86,7 +88,7 @@ def nuclide(self): @nuclide.setter def nuclide(self, nuc): if nuc is not None: - cv.check_type('derivative nuclide', nuc, str) + cv.check_type("derivative nuclide", nuc, str) self._nuclide = nuc def to_xml_element(self): @@ -103,7 +105,7 @@ def to_xml_element(self): element.set("id", str(self.id)) element.set("variable", self.variable) element.set("material", str(self.material)) - if self.variable == 'nuclide_density': + if self.variable == "nuclide_density": element.set("nuclide", self.nuclide) return element diff --git a/openmc/tracks.py b/openmc/tracks.py index 61e5a72442e..768ea82430e 100644 --- a/openmc/tracks.py +++ b/openmc/tracks.py @@ -8,7 +8,7 @@ from pathlib import Path -ParticleTrack = namedtuple('ParticleTrack', ['particle', 'states']) +ParticleTrack = namedtuple("ParticleTrack", ["particle", "states"]) ParticleTrack.__doc__ = """\ Particle track information @@ -24,8 +24,12 @@ ``material_id`` (material ID). """ + + def _particle_track_repr(self): return f"" + + ParticleTrack.__repr__ = _particle_track_repr @@ -34,7 +38,7 @@ def _particle_track_repr(self): def _identifier(dset_name): """Return (batch, gen, particle) tuple given dataset name""" - _, batch, gen, particle = dset_name.split('_') + _, batch, gen, particle = dset_name.split("_") return (int(batch), int(gen), int(particle)) @@ -67,8 +71,8 @@ class Track(Sequence): def __init__(self, dset): tracks = dset[()] - offsets = dset.attrs['offsets'] - particles = dset.attrs['particles'] + offsets = dset.attrs["offsets"] + particles = dset.attrs["particles"] self.identifier = _identifier(dset.name) # Construct list of track histories @@ -79,7 +83,7 @@ def __init__(self, dset): self.particle_tracks = tracks_list def __repr__(self): - return f'' + return f"" def __getitem__(self, index): return self.particle_tracks[index] @@ -166,17 +170,17 @@ def plot(self, axes=None): # Setup axes is one wasn't passed if axes is None: fig = plt.figure() - ax = plt.axes(projection='3d') - ax.set_xlabel('x [cm]') - ax.set_ylabel('y [cm]') - ax.set_zlabel('z [cm]') + ax = plt.axes(projection="3d") + ax.set_xlabel("x [cm]") + ax.set_ylabel("y [cm]") + ax.set_zlabel("z [cm]") else: ax = axes # Plot each particle track for _, states in self: - r = states['r'] - ax.plot3D(r['x'], r['y'], r['z']) + r = states["r"] + ax.plot3D(r["x"], r["y"], r["z"]) return ax @@ -188,9 +192,12 @@ def sources(self): state = particle_track.states[0] sources.append( SourceParticle( - r=state['r'], u=state['u'], E=state['E'], - time=state['time'], wgt=state['wgt'], - particle=particle_type + r=state["r"], + u=state["u"], + E=state["E"], + time=state["time"], + wgt=state["wgt"], + particle=particle_type, ) ) return sources @@ -211,11 +218,11 @@ class Tracks(list): """ - def __init__(self, filepath='tracks.h5'): + def __init__(self, filepath="tracks.h5"): # Read data from track file - with h5py.File(filepath, 'r') as fh: + with h5py.File(filepath, "r") as fh: # Check filetype and version - check_filetype_version(fh, 'track', _VERSION_TRACK) + check_filetype_version(fh, "track", _VERSION_TRACK) for dset_name in sorted(fh, key=_identifier): dset = fh[dset_name] @@ -261,16 +268,17 @@ def plot(self): """ import matplotlib.pyplot as plt + fig = plt.figure() - ax = plt.axes(projection='3d') - ax.set_xlabel('x [cm]') - ax.set_ylabel('y [cm]') - ax.set_zlabel('z [cm]') + ax = plt.axes(projection="3d") + ax.set_xlabel("x [cm]") + ax.set_ylabel("y [cm]") + ax.set_zlabel("z [cm]") for track in self: track.plot(ax) return ax - def write_to_vtk(self, filename=Path('tracks.vtp')): + def write_to_vtk(self, filename=Path("tracks.vtp")): """Creates a VTP file of the tracks Parameters @@ -294,7 +302,7 @@ def write_to_vtk(self, filename=Path('tracks.vtp')): for particle in self: for pt in particle.particle_tracks: for state in pt.states: - points.InsertNextPoint(state['r']) + points.InsertNextPoint(state["r"]) # Create VTK line and assign points to line. n = pt.states.size @@ -322,7 +330,7 @@ def write_to_vtk(self, filename=Path('tracks.vtp')): return data @staticmethod - def combine(track_files, path='tracks.h5'): + def combine(track_files, path="tracks.h5"): """Combine multiple track files into a single track file Parameters @@ -333,13 +341,13 @@ def combine(track_files, path='tracks.h5'): Path of combined track file to create """ - with h5py.File(path, 'w') as h5_out: + with h5py.File(path, "w") as h5_out: for i, fname in enumerate(track_files): - with h5py.File(fname, 'r') as h5_in: + with h5py.File(fname, "r") as h5_in: # Copy file attributes for first file if i == 0: - h5_out.attrs['filetype'] = h5_in.attrs['filetype'] - h5_out.attrs['version'] = h5_in.attrs['version'] + h5_out.attrs["filetype"] = h5_in.attrs["filetype"] + h5_out.attrs["version"] = h5_in.attrs["version"] # Copy each 'track_*' dataset from input file for dset in h5_in: diff --git a/openmc/trigger.py b/openmc/trigger.py index be2537e3ac8..dfa11891ecb 100644 --- a/openmc/trigger.py +++ b/openmc/trigger.py @@ -45,11 +45,11 @@ def __init__(self, trigger_type: str, threshold: float, ignore_zeros: bool = Fal self._scores = [] def __repr__(self): - string = 'Trigger\n' - string += '{: <16}=\t{}\n'.format('\tType', self._trigger_type) - string += '{: <16}=\t{}\n'.format('\tThreshold', self._threshold) - string += '{: <16}=\t{}\n'.format('\tIgnore Zeros', self._ignore_zeros) - string += '{: <16}=\t{}\n'.format('\tScores', self._scores) + string = "Trigger\n" + string += "{: <16}=\t{}\n".format("\tType", self._trigger_type) + string += "{: <16}=\t{}\n".format("\tThreshold", self._threshold) + string += "{: <16}=\t{}\n".format("\tIgnore Zeros", self._ignore_zeros) + string += "{: <16}=\t{}\n".format("\tScores", self._scores) return string @property @@ -58,8 +58,9 @@ def trigger_type(self): @trigger_type.setter def trigger_type(self, trigger_type): - cv.check_value('tally trigger type', trigger_type, - ['variance', 'std_dev', 'rel_err']) + cv.check_value( + "tally trigger type", trigger_type, ["variance", "std_dev", "rel_err"] + ) self._trigger_type = trigger_type @property @@ -68,7 +69,7 @@ def threshold(self): @threshold.setter def threshold(self, threshold): - cv.check_type('tally trigger threshold', threshold, Real) + cv.check_type("tally trigger threshold", threshold, Real) self._threshold = threshold @property @@ -77,7 +78,7 @@ def ignore_zeros(self): @ignore_zeros.setter def ignore_zeros(self, ignore_zeros): - cv.check_type('tally trigger ignores zeros', ignore_zeros, bool) + cv.check_type("tally trigger ignores zeros", ignore_zeros, bool) self._ignore_zeros = ignore_zeros @property @@ -86,7 +87,7 @@ def scores(self): @scores.setter def scores(self, scores): - cv.check_type('trigger scores', scores, Iterable, str) + cv.check_type("trigger scores", scores, Iterable, str) # Set scores making sure not to have duplicates self._scores = [] @@ -110,7 +111,7 @@ def to_xml_element(self): if self._ignore_zeros: element.set("ignore_zeros", "true") if len(self._scores) != 0: - element.set("scores", ' '.join(self._scores)) + element.set("scores", " ".join(self._scores)) return element @classmethod @@ -133,7 +134,7 @@ def from_xml_element(cls, elem: ET.Element): threshold = float(elem.get("threshold")) ignore_zeros = str(elem.get("ignore_zeros", "false")).lower() # Try to convert to bool. Let Trigger error out on instantiation. - ignore_zeros = ignore_zeros in ('true', '1') + ignore_zeros = ignore_zeros in ("true", "1") trigger = cls(trigger_type, threshold, ignore_zeros) # Add scores if present diff --git a/openmc/universe.py b/openmc/universe.py index 648b773df6f..381f491ccac 100644 --- a/openmc/universe.py +++ b/openmc/universe.py @@ -34,7 +34,7 @@ class UniverseBase(ABC, IDManagerMixin): next_id = 1 used_ids = set() - def __init__(self, universe_id=None, name=''): + def __init__(self, universe_id=None, name=""): # Initialize Universe class attributes self.id = universe_id self.name = name @@ -46,9 +46,9 @@ def __init__(self, universe_id=None, name=''): self._cells = {} def __repr__(self): - string = 'Universe\n' - string += '{: <16}=\t{}\n'.format('\tID', self._id) - string += '{: <16}=\t{}\n'.format('\tName', self._name) + string = "Universe\n" + string += "{: <16}=\t{}\n".format("\tID", self._id) + string += "{: <16}=\t{}\n".format("\tName", self._name) return string @property @@ -58,10 +58,10 @@ def name(self): @name.setter def name(self, name): if name is not None: - cv.check_type('universe name', name, str) + cv.check_type("universe name", name, str) self._name = name else: - self._name = '' + self._name = "" @property def volume(self): @@ -70,7 +70,7 @@ def volume(self): @volume.setter def volume(self, volume): if volume is not None: - cv.check_type('universe volume', volume, Real) + cv.check_type("universe volume", volume, Real) self._volume = volume def add_volume_information(self, volume_calc): @@ -82,15 +82,14 @@ def add_volume_information(self, volume_calc): Results from a stochastic volume calculation """ - if volume_calc.domain_type == 'universe': + if volume_calc.domain_type == "universe": if self.id in volume_calc.volumes: self._volume = volume_calc.volumes[self.id].n self._atoms = volume_calc.atoms[self.id] else: - raise ValueError( - 'No volume information found for this universe.') + raise ValueError("No volume information found for this universe.") else: - raise ValueError('No volume information found for this universe.') + raise ValueError("No volume information found for this universe.") def get_all_universes(self, memo=None): """Return all universes that are contained within this one. @@ -174,8 +173,7 @@ def clone(self, clone_materials=True, clone_regions=True, memo=None): # Clone all cells for the universe clone clone._cells = {} for cell in self._cells.values(): - clone.add_cell(cell.clone(clone_materials, clone_regions, - memo)) + clone.add_cell(cell.clone(clone_materials, clone_regions, memo)) # Memoize the clone memo[self] = clone @@ -215,7 +213,7 @@ class Universe(UniverseBase): """ - def __init__(self, universe_id=None, name='', cells=None): + def __init__(self, universe_id=None, name="", cells=None): super().__init__(universe_id, name) if cells is not None: @@ -223,8 +221,8 @@ def __init__(self, universe_id=None, name='', cells=None): def __repr__(self): string = super().__repr__() - string += '{: <16}=\t{}\n'.format('\tGeom', 'CSG') - string += '{: <16}=\t{}\n'.format('\tCells', list(self._cells.keys())) + string += "{: <16}=\t{}\n".format("\tGeom", "CSG") + string += "{: <16}=\t{}\n".format("\tCells", list(self._cells.keys())) return string @property @@ -233,8 +231,7 @@ def cells(self): @property def bounding_box(self) -> openmc.BoundingBox: - regions = [c.region for c in self.cells.values() - if c.region is not None] + regions = [c.region for c in self.cells.values() if c.region is not None] if regions: return openmc.Union(regions).bounding_box else: @@ -257,8 +254,8 @@ def from_hdf5(cls, group, cells): Universe instance """ - universe_id = int(group.name.split('/')[-1].lstrip('universe ')) - cell_ids = group['cells'][()] + universe_id = int(group.name.split("/")[-1].lstrip("universe ")) + cell_ids = group["cells"][()] # Create this Universe universe = cls(universe_id) @@ -287,9 +284,9 @@ def find(self, point): p = np.asarray(point) for cell in self._cells.values(): if p in cell: - if cell.fill_type in ('material', 'distribmat', 'void'): + if cell.fill_type in ("material", "distribmat", "void"): return [self, cell] - elif cell.fill_type == 'universe': + elif cell.fill_type == "universe": if cell.translation is not None: p -= cell.translation if cell.rotation is not None: @@ -300,14 +297,29 @@ def find(self, point): return [] # default kwargs that are passed to plt.legend in the plot method below. - _default_legend_kwargs = {'bbox_to_anchor': ( - 1.05, 1), 'loc': 2, 'borderaxespad': 0.0} - - def plot(self, origin=None, width=None, pixels=40000, - basis='xy', color_by='cell', colors=None, seed=None, - openmc_exec='openmc', axes=None, legend=False, axis_units='cm', - legend_kwargs=_default_legend_kwargs, outline=False, - **kwargs): + _default_legend_kwargs = { + "bbox_to_anchor": (1.05, 1), + "loc": 2, + "borderaxespad": 0.0, + } + + def plot( + self, + origin=None, + width=None, + pixels=40000, + basis="xy", + color_by="cell", + colors=None, + seed=None, + openmc_exec="openmc", + axes=None, + legend=False, + axis_units="cm", + legend_kwargs=_default_legend_kwargs, + outline=False, + **kwargs, + ): """Display a slice plot of the universe. Parameters @@ -380,15 +392,15 @@ def plot(self, origin=None, width=None, pixels=40000, import matplotlib.pyplot as plt # Determine extents of plot - if basis == 'xy': + if basis == "xy": x, y = 0, 1 - xlabel, ylabel = f'x [{axis_units}]', f'y [{axis_units}]' - elif basis == 'yz': + xlabel, ylabel = f"x [{axis_units}]", f"y [{axis_units}]" + elif basis == "yz": x, y = 1, 2 - xlabel, ylabel = f'y [{axis_units}]', f'z [{axis_units}]' - elif basis == 'xz': + xlabel, ylabel = f"y [{axis_units}]", f"z [{axis_units}]" + elif basis == "xz": x, y = 0, 2 - xlabel, ylabel = f'x [{axis_units}]', f'z [{axis_units}]' + xlabel, ylabel = f"x [{axis_units}]", f"z [{axis_units}]" bb = self.bounding_box # checks to see if bounding box contains -inf or inf values @@ -406,8 +418,8 @@ def plot(self, origin=None, width=None, pixels=40000, origin = np.nan_to_num(bb.center) if width is None: bb_width = bb.width - x_width = bb_width['xyz'.index(basis[0])] - y_width = bb_width['xyz'.index(basis[1])] + x_width = bb_width["xyz".index(basis[0])] + y_width = bb_width["xyz".index(basis[1])] width = (x_width, y_width) if isinstance(pixels, int): @@ -415,12 +427,12 @@ def plot(self, origin=None, width=None, pixels=40000, pixels_y = math.sqrt(pixels / aspect_ratio) pixels = (int(pixels / pixels_y), int(pixels_y)) - axis_scaling_factor = {'km': 0.00001, 'm': 0.01, 'cm': 1, 'mm': 10} + axis_scaling_factor = {"km": 0.00001, "m": 0.01, "cm": 1, "mm": 10} - x_min = (origin[x] - 0.5*width[0]) * axis_scaling_factor[axis_units] - x_max = (origin[x] + 0.5*width[0]) * axis_scaling_factor[axis_units] - y_min = (origin[y] - 0.5*width[1]) * axis_scaling_factor[axis_units] - y_max = (origin[y] + 0.5*width[1]) * axis_scaling_factor[axis_units] + x_min = (origin[x] - 0.5 * width[0]) * axis_scaling_factor[axis_units] + x_max = (origin[x] + 0.5 * width[0]) * axis_scaling_factor[axis_units] + y_min = (origin[y] - 0.5 * width[1]) * axis_scaling_factor[axis_units] + y_max = (origin[y] + 0.5 * width[1]) * axis_scaling_factor[axis_units] with TemporaryDirectory() as tmpdir: model = openmc.Model() @@ -432,7 +444,7 @@ def plot(self, origin=None, width=None, pixels=40000, # so, set energy mode accordingly for mat in self.get_all_materials().values(): if mat._macroscopic is not None: - model.settings.energy_mode = 'multi-group' + model.settings.energy_mode = "multi-group" break # Create plot object matching passed arguments @@ -450,28 +462,27 @@ def plot(self, origin=None, width=None, pixels=40000, model.plot_geometry(False, cwd=tmpdir, openmc_exec=openmc_exec) # Read image from file - img_path = Path(tmpdir) / f'plot_{plot.id}.png' + img_path = Path(tmpdir) / f"plot_{plot.id}.png" if not img_path.is_file(): - img_path = img_path.with_suffix('.ppm') + img_path = img_path.with_suffix(".ppm") img = mpimg.imread(str(img_path)) # Create a figure sized such that the size of the axes within # exactly matches the number of pixels specified if axes is None: - px = 1/plt.rcParams['figure.dpi'] + px = 1 / plt.rcParams["figure.dpi"] fig, axes = plt.subplots() axes.set_xlabel(xlabel) axes.set_ylabel(ylabel) params = fig.subplotpars - width = pixels[0]*px/(params.right - params.left) - height = pixels[1]*px/(params.top - params.bottom) + width = pixels[0] * px / (params.right - params.left) + height = pixels[1] * px / (params.top - params.bottom) fig.set_size_inches(width, height) if outline: # Combine R, G, B values into a single int rgb = (img * 256).astype(int) - image_value = (rgb[..., 0] << 16) + \ - (rgb[..., 1] << 8) + (rgb[..., 2]) + image_value = (rgb[..., 0] << 16) + (rgb[..., 1] << 8) + (rgb[..., 2]) axes.contour( image_value, @@ -487,8 +498,10 @@ def plot(self, origin=None, width=None, pixels=40000, # or cell if that was requested if legend: if plot.colors == {}: - raise ValueError("Must pass 'colors' dictionary if you " - "are adding a legend via legend=True.") + raise ValueError( + "Must pass 'colors' dictionary if you " + "are adding a legend via legend=True." + ) if color_by == "cell": expected_key_type = openmc.Cell @@ -500,13 +513,13 @@ def plot(self, origin=None, width=None, pixels=40000, if isinstance(key, int): raise TypeError( - "Cannot use IDs in colors dict for auto legend.") + "Cannot use IDs in colors dict for auto legend." + ) elif not isinstance(key, expected_key_type): - raise TypeError( - "Color dict key type does not match color_by") + raise TypeError("Color dict key type does not match color_by") # this works whether we're doing cells or materials - label = key.name if key.name != '' else key.id + label = key.name if key.name != "" else key.id # matplotlib takes RGB on 0-1 scale rather than 0-255. at # this point PlotBase has already checked that 3-tuple @@ -514,8 +527,7 @@ def plot(self, origin=None, width=None, pixels=40000, # then we know it just needs to be converted to the 0-1 # format. if len(color) == 3 and not isinstance(color, str): - scaled_color = ( - color[0]/255, color[1]/255, color[2]/255) + scaled_color = (color[0] / 255, color[1] / 255, color[2] / 255) else: scaled_color = color @@ -539,8 +551,10 @@ def add_cell(self, cell): """ if not isinstance(cell, openmc.Cell): - msg = f'Unable to add a Cell to Universe ID="{self._id}" since ' \ - f'"{cell}" is not a Cell' + msg = ( + f'Unable to add a Cell to Universe ID="{self._id}" since ' + f'"{cell}" is not a Cell' + ) raise TypeError(msg) cell_id = cell.id @@ -559,8 +573,10 @@ def add_cells(self, cells): """ if not isinstance(cells, Iterable): - msg = f'Unable to add Cells to Universe ID="{self._id}" since ' \ - f'"{cells}" is not iterable' + msg = ( + f'Unable to add Cells to Universe ID="{self._id}" since ' + f'"{cells}" is not iterable' + ) raise TypeError(msg) for cell in cells: @@ -577,8 +593,10 @@ def remove_cell(self, cell): """ if not isinstance(cell, openmc.Cell): - msg = f'Unable to remove a Cell from Universe ID="{self._id}" ' \ - f'since "{cell}" is not a Cell' + msg = ( + f'Unable to remove a Cell from Universe ID="{self._id}" ' + f'since "{cell}" is not a Cell' + ) raise TypeError(msg) # If the Cell is in the Universe's list of Cells, delete it @@ -625,14 +643,15 @@ def get_nuclide_densities(self): volume = self.volume for name, atoms in self._atoms.items(): nuclide = openmc.Nuclide(name) - density = 1.0e-24 * atoms.n/volume # density in atoms/b-cm + density = 1.0e-24 * atoms.n / volume # density in atoms/b-cm nuclides[name] = (nuclide, density) else: raise RuntimeError( - 'Volume information is needed to calculate microscopic cross ' - f'sections for universe {self.id}. This can be done by running ' - 'a stochastic volume calculation via the ' - 'openmc.VolumeCalculation object') + "Volume information is needed to calculate microscopic cross " + f"sections for universe {self.id}. This can be done by running " + "a stochastic volume calculation via the " + "openmc.VolumeCalculation object" + ) return nuclides @@ -706,36 +725,37 @@ def create_xml_subelement(self, xml_element, memo=None): cell_element.set("universe", str(self._id)) xml_element.append(cell_element) - def _determine_paths(self, path='', instances_only=False): + def _determine_paths(self, path="", instances_only=False): """Count the number of instances for each cell in the universe, and record the count in the :attr:`Cell.num_instances` properties.""" - univ_path = path + f'u{self.id}' + univ_path = path + f"u{self.id}" for cell in self.cells.values(): - cell_path = f'{univ_path}->c{cell.id}' + cell_path = f"{univ_path}->c{cell.id}" fill = cell._fill fill_type = cell.fill_type # If universe-filled, recursively count cells in filling universe - if fill_type == 'universe': - fill._determine_paths(cell_path + '->', instances_only) + if fill_type == "universe": + fill._determine_paths(cell_path + "->", instances_only) # If lattice-filled, recursively call for all universes in lattice - elif fill_type == 'lattice': + elif fill_type == "lattice": latt = fill # Count instances in each universe in the lattice for index in latt._natural_indices: - latt_path = '{}->l{}({})->'.format( - cell_path, latt.id, ",".join(str(x) for x in index)) + latt_path = "{}->l{}({})->".format( + cell_path, latt.id, ",".join(str(x) for x in index) + ) univ = latt.get_universe(index) univ._determine_paths(latt_path, instances_only) else: - if fill_type == 'material': + if fill_type == "material": mat = fill - elif fill_type == 'distribmat': + elif fill_type == "distribmat": mat = fill[cell._num_instances] else: mat = None @@ -743,7 +763,7 @@ def _determine_paths(self, path='', instances_only=False): if mat is not None: mat._num_instances += 1 if not instances_only: - mat._paths.append(f'{cell_path}->m{mat.id}') + mat._paths.append(f"{cell_path}->m{mat.id}") # Append current path cell._num_instances += 1 @@ -820,12 +840,14 @@ class DAGMCUniverse(UniverseBase): """ - def __init__(self, - filename: cv.PathLike, - universe_id=None, - name='', - auto_geom_ids=False, - auto_mat_ids=False): + def __init__( + self, + filename: cv.PathLike, + universe_id=None, + name="", + auto_geom_ids=False, + auto_mat_ids=False, + ): super().__init__(universe_id, name) # Initialize class attributes self.filename = filename @@ -834,14 +856,14 @@ def __init__(self, def __repr__(self): string = super().__repr__() - string += '{: <16}=\t{}\n'.format('\tGeom', 'DAGMC') - string += '{: <16}=\t{}\n'.format('\tFile', self.filename) + string += "{: <16}=\t{}\n".format("\tGeom", "DAGMC") + string += "{: <16}=\t{}\n".format("\tFile", self.filename) return string @property def bounding_box(self): with h5py.File(self.filename) as dagmc_file: - coords = dagmc_file['tstt']['nodes']['coordinates'][()] + coords = dagmc_file["tstt"]["nodes"]["coordinates"][()] lower_left_corner = coords.min(axis=0) upper_right_corner = coords.max(axis=0) return openmc.BoundingBox(lower_left_corner, upper_right_corner) @@ -852,7 +874,7 @@ def filename(self): @filename.setter def filename(self, val: cv.PathLike): - cv.check_type('DAGMC filename', val, cv.PathLike) + cv.check_type("DAGMC filename", val, cv.PathLike) self._filename = input_path(val) @property @@ -861,7 +883,7 @@ def auto_geom_ids(self): @auto_geom_ids.setter def auto_geom_ids(self, val): - cv.check_type('DAGMC automatic geometry ids', val, bool) + cv.check_type("DAGMC automatic geometry ids", val, bool) self._auto_geom_ids = val @property @@ -870,19 +892,18 @@ def auto_mat_ids(self): @auto_mat_ids.setter def auto_mat_ids(self, val): - cv.check_type('DAGMC automatic material ids', val, bool) + cv.check_type("DAGMC automatic material ids", val, bool) self._auto_mat_ids = val @property def material_names(self): dagmc_file_contents = h5py.File(self.filename) - material_tags_hex = dagmc_file_contents['/tstt/tags/NAME'].get( - 'values') + material_tags_hex = dagmc_file_contents["/tstt/tags/NAME"].get("values") material_tags_ascii = [] for tag in material_tags_hex: - candidate_tag = tag.tobytes().decode().replace('\x00', '') + candidate_tag = tag.tobytes().decode().replace("\x00", "") # tags might be for temperature or reflective surfaces - if candidate_tag.startswith('mat:'): + if candidate_tag.startswith("mat:"): # removes first 4 characters as openmc.Material name should be # set without the 'mat:' part of the tag material_tags_ascii.append(candidate_tag[4:]) @@ -911,32 +932,32 @@ def _n_geom_elements(self, geom_type): int Number of geometry elements of the specified type """ - cv.check_value('geometry type', geom_type, ('volume', 'surface')) + cv.check_value("geometry type", geom_type, ("volume", "surface")) def decode_str_tag(tag_val): - return tag_val.tobytes().decode().replace('\x00', '') + return tag_val.tobytes().decode().replace("\x00", "") with h5py.File(self.filename) as dagmc_file: - category_data = dagmc_file['tstt/tags/CATEGORY/values'] + category_data = dagmc_file["tstt/tags/CATEGORY/values"] category_strs = map(decode_str_tag, category_data) n = sum([v == geom_type.capitalize() for v in category_strs]) # check for presence of an implicit complement in the file and # increment the number of cells if it doesn't exist - if geom_type == 'volume': - name_data = dagmc_file['tstt/tags/NAME/values'] + if geom_type == "volume": + name_data = dagmc_file["tstt/tags/NAME/values"] name_strs = map(decode_str_tag, name_data) - if not sum(['impl_complement' in n for n in name_strs]): + if not sum(["impl_complement" in n for n in name_strs]): n += 1 return n @property def n_cells(self): - return self._n_geom_elements('volume') + return self._n_geom_elements("volume") @property def n_surfaces(self): - return self._n_geom_elements('surface') + return self._n_geom_elements("surface") def create_xml_subelement(self, xml_element, memo=None): if memo is None: @@ -948,23 +969,23 @@ def create_xml_subelement(self, xml_element, memo=None): memo.add(self) # Set xml element values - dagmc_element = ET.Element('dagmc_universe') - dagmc_element.set('id', str(self.id)) + dagmc_element = ET.Element("dagmc_universe") + dagmc_element.set("id", str(self.id)) if self.auto_geom_ids: - dagmc_element.set('auto_geom_ids', 'true') + dagmc_element.set("auto_geom_ids", "true") if self.auto_mat_ids: - dagmc_element.set('auto_mat_ids', 'true') - dagmc_element.set('filename', str(self.filename)) + dagmc_element.set("auto_mat_ids", "true") + dagmc_element.set("filename", str(self.filename)) xml_element.append(dagmc_element) def bounding_region( - self, - bounded_type: str = 'box', - boundary_type: str = 'vacuum', - starting_id: int = 10000, - padding_distance: float = 0. - ): + self, + bounded_type: str = "box", + boundary_type: str = "vacuum", + starting_id: int = 10000, + padding_distance: float = 0.0, + ): """Creates a either a spherical or box shaped bounding region around the DAGMC geometry. @@ -995,15 +1016,15 @@ def bounding_region( Region instance """ - check_type('boundary type', boundary_type, str) - check_value('boundary type', boundary_type, _BOUNDARY_TYPES) - check_type('starting surface id', starting_id, Integral) - check_type('bounded type', bounded_type, str) - check_value('bounded type', bounded_type, ('box', 'sphere')) + check_type("boundary type", boundary_type, str) + check_value("boundary type", boundary_type, _BOUNDARY_TYPES) + check_type("starting surface id", starting_id, Integral) + check_type("bounded type", bounded_type, str) + check_value("bounded type", bounded_type, ("box", "sphere")) bbox = self.bounding_box.expand(padding_distance, True) - if bounded_type == 'sphere': + if bounded_type == "sphere": radius = np.linalg.norm(bbox.upper_right - bbox.center) bounding_surface = openmc.Sphere( surface_id=starting_id, @@ -1016,14 +1037,14 @@ def bounding_region( return -bounding_surface - if bounded_type == 'box': + if bounded_type == "box": # defines plane surfaces for all six faces of the bounding box lower_x = openmc.XPlane(bbox[0][0], surface_id=starting_id) - upper_x = openmc.XPlane(bbox[1][0], surface_id=starting_id+1) - lower_y = openmc.YPlane(bbox[0][1], surface_id=starting_id+2) - upper_y = openmc.YPlane(bbox[1][1], surface_id=starting_id+3) - lower_z = openmc.ZPlane(bbox[0][2], surface_id=starting_id+4) - upper_z = openmc.ZPlane(bbox[1][2], surface_id=starting_id+5) + upper_x = openmc.XPlane(bbox[1][0], surface_id=starting_id + 1) + lower_y = openmc.YPlane(bbox[0][1], surface_id=starting_id + 2) + upper_y = openmc.YPlane(bbox[1][1], surface_id=starting_id + 3) + lower_z = openmc.ZPlane(bbox[0][2], surface_id=starting_id + 4) + upper_z = openmc.ZPlane(bbox[1][2], surface_id=starting_id + 5) region = +lower_x & -upper_x & +lower_y & -upper_y & +lower_z & -upper_z @@ -1050,7 +1071,8 @@ def bounded_universe(self, bounding_cell_id=10000, **kwargs): Universe instance """ bounding_cell = openmc.Cell( - fill=self, cell_id=bounding_cell_id, region=self.bounding_region(**kwargs)) + fill=self, cell_id=bounding_cell_id, region=self.bounding_region(**kwargs) + ) return openmc.Universe(cells=[bounding_cell]) @classmethod @@ -1068,14 +1090,14 @@ def from_hdf5(cls, group): DAGMCUniverse instance """ - id = int(group.name.split('/')[-1].lstrip('universe ')) - fname = group['filename'][()].decode() - name = group['name'][()].decode() if 'name' in group else None + id = int(group.name.split("/")[-1].lstrip("universe ")) + fname = group["filename"][()].decode() + name = group["name"][()].decode() if "name" in group else None out = cls(fname, universe_id=id, name=name) - out.auto_geom_ids = bool(group.attrs['auto_geom_ids']) - out.auto_mat_ids = bool(group.attrs['auto_mat_ids']) + out.auto_geom_ids = bool(group.attrs["auto_geom_ids"]) + out.auto_mat_ids = bool(group.attrs["auto_mat_ids"]) return out @@ -1094,17 +1116,17 @@ def from_xml_element(cls, elem): DAGMCUniverse instance """ - id = int(get_text(elem, 'id')) - fname = get_text(elem, 'filename') + id = int(get_text(elem, "id")) + fname = get_text(elem, "filename") out = cls(fname, universe_id=id) - name = get_text(elem, 'name') + name = get_text(elem, "name") if name is not None: out.name = name - out.auto_geom_ids = bool(elem.get('auto_geom_ids')) - out.auto_mat_ids = bool(elem.get('auto_mat_ids')) + out.auto_geom_ids = bool(elem.get("auto_geom_ids")) + out.auto_mat_ids = bool(elem.get("auto_mat_ids")) return out diff --git a/openmc/utility_funcs.py b/openmc/utility_funcs.py index da9f73b1651..5dd4b7565eb 100644 --- a/openmc/utility_funcs.py +++ b/openmc/utility_funcs.py @@ -26,7 +26,7 @@ def change_directory(working_dir: PathLike | None = None, *, tmpdir: bool = Fals tmp = TemporaryDirectory() working_dir = tmp.name elif working_dir is None: - raise ValueError('Must pass working_dir argument or specify tmpdir=True.') + raise ValueError("Must pass working_dir argument or specify tmpdir=True.") working_dir = Path(working_dir) working_dir.mkdir(parents=True, exist_ok=True) @@ -53,7 +53,7 @@ def input_path(filename: PathLike) -> Path: Path object """ - if openmc.config['resolve_paths']: + if openmc.config["resolve_paths"]: return Path(filename).resolve() else: return Path(filename) diff --git a/openmc/volume.py b/openmc/volume.py index df19def1eac..e9c09bb947d 100644 --- a/openmc/volume.py +++ b/openmc/volume.py @@ -68,6 +68,7 @@ class VolumeCalculation: .. versionadded:: 0.12 """ + def __init__(self, domains, samples, lower_left=None, upper_right=None): self._atoms = {} self._volumes = {} @@ -75,55 +76,68 @@ def __init__(self, domains, samples, lower_left=None, upper_right=None): self._trigger_type = None self._iterations = None - cv.check_type('domains', domains, Iterable, - (openmc.Cell, openmc.Material, openmc.Universe)) + cv.check_type( + "domains", + domains, + Iterable, + (openmc.Cell, openmc.Material, openmc.Universe), + ) if isinstance(domains[0], openmc.Cell): - self._domain_type = 'cell' + self._domain_type = "cell" elif isinstance(domains[0], openmc.Material): - self._domain_type = 'material' + self._domain_type = "material" elif isinstance(domains[0], openmc.Universe): - self._domain_type = 'universe' + self._domain_type = "universe" self.ids = [d.id for d in domains] self.samples = samples if lower_left is not None: if upper_right is None: - raise ValueError('Both lower-left and upper-right coordinates ' - 'should be specified') + raise ValueError( + "Both lower-left and upper-right coordinates " "should be specified" + ) # For cell domains, try to compute bounding box and make sure # user-specified one is valid - if self.domain_type == 'cell': + if self.domain_type == "cell": for c in domains: ll, ur = c.bounding_box if np.any(np.isinf(ll)) or np.any(np.isinf(ur)): continue - if (np.any(np.asarray(lower_left) > ll) or - np.any(np.asarray(upper_right) < ur)): - msg = ('Specified bounding box is smaller than ' - f'computed bounding box for cell {c.id}. Volume ' - 'calculation may be incorrect!') + if np.any(np.asarray(lower_left) > ll) or np.any( + np.asarray(upper_right) < ur + ): + msg = ( + "Specified bounding box is smaller than " + f"computed bounding box for cell {c.id}. Volume " + "calculation may be incorrect!" + ) warnings.warn(msg) self.lower_left = lower_left self.upper_right = upper_right else: - if self.domain_type == 'cell': + if self.domain_type == "cell": ll, ur = openmc.Union(c.region for c in domains).bounding_box if np.any(np.isinf(ll)) or np.any(np.isinf(ur)): - raise ValueError('Could not automatically determine bounding ' - 'box for stochastic volume calculation.') + raise ValueError( + "Could not automatically determine bounding " + "box for stochastic volume calculation." + ) else: self.lower_left = ll self.upper_right = ur else: - raise ValueError('Could not automatically determine bounding box ' - 'for stochastic volume calculation.') + raise ValueError( + "Could not automatically determine bounding box " + "for stochastic volume calculation." + ) if np.isinf(self.lower_left).any() or np.isinf(self.upper_right).any(): - raise ValueError('Lower-left and upper-right bounding box ' - 'coordinates must be finite.') + raise ValueError( + "Lower-left and upper-right bounding box " "coordinates must be finite." + ) @property def ids(self): @@ -131,7 +145,7 @@ def ids(self): @ids.setter def ids(self, ids): - cv.check_type('domain IDs', ids, Iterable, Real) + cv.check_type("domain IDs", ids, Iterable, Real) self._ids = ids @property @@ -140,8 +154,8 @@ def samples(self): @samples.setter def samples(self, samples): - cv.check_type('number of samples', samples, Integral) - cv.check_greater_than('number of samples', samples, 0) + cv.check_type("number of samples", samples, Integral) + cv.check_greater_than("number of samples", samples, 0) self._samples = samples @property @@ -150,7 +164,7 @@ def lower_left(self): @lower_left.setter def lower_left(self, lower_left): - name = 'lower-left bounding box coordinates', + name = ("lower-left bounding box coordinates",) cv.check_type(name, lower_left, Iterable, Real) cv.check_length(name, lower_left, 3) self._lower_left = lower_left @@ -161,7 +175,7 @@ def upper_right(self): @upper_right.setter def upper_right(self, upper_right): - name = 'upper-right bounding box coordinates' + name = "upper-right bounding box coordinates" cv.check_type(name, upper_right, Iterable, Real) cv.check_length(name, upper_right, 3) self._upper_right = upper_right @@ -172,7 +186,7 @@ def threshold(self): @threshold.setter def threshold(self, threshold): - name = 'volume std. dev. threshold' + name = "volume std. dev. threshold" cv.check_type(name, threshold, Real) cv.check_greater_than(name, threshold, 0.0) self._threshold = threshold @@ -183,8 +197,9 @@ def trigger_type(self): @trigger_type.setter def trigger_type(self, trigger_type): - cv.check_value('tally trigger type', trigger_type, - ('variance', 'std_dev', 'rel_err')) + cv.check_value( + "tally trigger type", trigger_type, ("variance", "std_dev", "rel_err") + ) self._trigger_type = trigger_type @property @@ -193,7 +208,7 @@ def iterations(self): @iterations.setter def iterations(self, iterations): - name = 'volume calculation iterations' + name = "volume calculation iterations" cv.check_type(name, iterations, Integral) cv.check_greater_than(name, iterations, 0) self._iterations = iterations @@ -208,7 +223,7 @@ def atoms(self): @atoms.setter def atoms(self, atoms): - cv.check_type('atoms', atoms, Mapping) + cv.check_type("atoms", atoms, Mapping) self._atoms = atoms @property @@ -217,13 +232,13 @@ def volumes(self): @volumes.setter def volumes(self, volumes): - cv.check_type('volumes', volumes, Mapping) + cv.check_type("volumes", volumes, Mapping) self._volumes = volumes @property def atoms_dataframe(self): items = [] - columns = [self.domain_type.capitalize(), 'Nuclide', 'Atoms'] + columns = [self.domain_type.capitalize(), "Nuclide", "Atoms"] for uid, atoms_dict in self.atoms.items(): for name, atoms in atoms_dict.items(): items.append((uid, name, atoms)) @@ -260,30 +275,30 @@ def from_hdf5(cls, filename): Results of the stochastic volume calculation """ - with h5py.File(filename, 'r') as f: + with h5py.File(filename, "r") as f: cv.check_filetype_version(f, "volume", _VERSION_VOLUME) - domain_type = f.attrs['domain_type'].decode() - samples = f.attrs['samples'] - lower_left = f.attrs['lower_left'] - upper_right = f.attrs['upper_right'] + domain_type = f.attrs["domain_type"].decode() + samples = f.attrs["samples"] + lower_left = f.attrs["lower_left"] + upper_right = f.attrs["upper_right"] - threshold = f.attrs.get('threshold') - trigger_type = f.attrs.get('trigger_type') - iterations = f.attrs.get('iterations', 1) + threshold = f.attrs.get("threshold") + trigger_type = f.attrs.get("trigger_type") + iterations = f.attrs.get("iterations", 1) volumes = {} atoms = {} ids = [] for obj_name in f: - if obj_name.startswith('domain_'): + if obj_name.startswith("domain_"): domain_id = int(obj_name[7:]) ids.append(domain_id) group = f[obj_name] - volume = ufloat(*group['volume'][()]) + volume = ufloat(*group["volume"][()]) volumes[domain_id] = volume - nucnames = group['nuclides'][()] - atoms_ = group['atoms'][()] + nucnames = group["nuclides"][()] + atoms_ = group["atoms"][()] atom_dict = {} for name_i, atoms_i in zip(nucnames, atoms_): atom_dict[name_i.decode()] = ufloat(*atoms_i) @@ -292,12 +307,12 @@ def from_hdf5(cls, filename): # Instantiate some throw-away domains that are used by the constructor # to assign IDs with warnings.catch_warnings(): - warnings.simplefilter('ignore', openmc.IDWarning) - if domain_type == 'cell': + warnings.simplefilter("ignore", openmc.IDWarning) + if domain_type == "cell": domains = [openmc.Cell(uid) for uid in ids] - elif domain_type == 'material': + elif domain_type == "material": domains = [openmc.Material(uid) for uid in ids] - elif domain_type == 'universe': + elif domain_type == "universe": domains = [openmc.Universe(uid) for uid in ids] # Instantiate the class and assign results @@ -344,13 +359,13 @@ def to_xml_element(self): dt_elem = ET.SubElement(element, "domain_type") dt_elem.text = self.domain_type id_elem = ET.SubElement(element, "domain_ids") - id_elem.text = ' '.join(str(uid) for uid in self.ids) + id_elem.text = " ".join(str(uid) for uid in self.ids) samples_elem = ET.SubElement(element, "samples") samples_elem.text = str(self.samples) ll_elem = ET.SubElement(element, "lower_left") - ll_elem.text = ' '.join(str(x) for x in self.lower_left) + ll_elem.text = " ".join(str(x) for x in self.lower_left) ur_elem = ET.SubElement(element, "upper_right") - ur_elem.text = ' '.join(str(x) for x in self.upper_right) + ur_elem.text = " ".join(str(x) for x in self.upper_right) if self.threshold: trigger_elem = ET.SubElement(element, "threshold") trigger_elem.set("type", self.trigger_type) @@ -386,12 +401,12 @@ def from_xml_element(cls, elem): # Instantiate some throw-away domains that are used by the constructor # to assign IDs with warnings.catch_warnings(): - warnings.simplefilter('ignore', openmc.IDWarning) - if domain_type == 'cell': + warnings.simplefilter("ignore", openmc.IDWarning) + if domain_type == "cell": domains = [openmc.Cell(uid) for uid in ids] - elif domain_type == 'material': + elif domain_type == "material": domains = [openmc.Material(uid) for uid in ids] - elif domain_type == 'universe': + elif domain_type == "universe": domains = [openmc.Universe(uid) for uid in ids] vol = cls(domains, samples, lower_left, upper_right) diff --git a/openmc/weight_windows.py b/openmc/weight_windows.py index 5df9f71dcc7..b61fecb76f5 100644 --- a/openmc/weight_windows.py +++ b/openmc/weight_windows.py @@ -9,7 +9,13 @@ import openmc from openmc.filter import _PARTICLES -from openmc.mesh import MeshBase, RectilinearMesh, CylindricalMesh, SphericalMesh, UnstructuredMesh +from openmc.mesh import ( + MeshBase, + RectilinearMesh, + CylindricalMesh, + SphericalMesh, + UnstructuredMesh, +) import openmc.checkvalue as cv from openmc.checkvalue import PathLike from ._xml import get_text, clean_indentation @@ -103,6 +109,7 @@ class WeightWindows(IDManagerMixin): openmc.Settings """ + next_id = 1 used_ids = set() @@ -113,12 +120,12 @@ def __init__( upper_ww_bounds: Iterable[float] | None = None, upper_bound_ratio: float | None = None, energy_bounds: Iterable[Real] | None = None, - particle_type: str = 'neutron', + particle_type: str = "neutron", survival_ratio: float = 3, max_lower_bound_ratio: float | None = None, max_split: int = 10, - weight_cutoff: float = 1.e-38, - id: int | None = None + weight_cutoff: float = 1.0e-38, + id: int | None = None, ): self.mesh = mesh self.id = id @@ -129,12 +136,16 @@ def __init__( self.lower_ww_bounds = lower_ww_bounds if upper_ww_bounds is not None and upper_bound_ratio: - raise ValueError("Exactly one of upper_ww_bounds and " - "upper_bound_ratio must be present.") + raise ValueError( + "Exactly one of upper_ww_bounds and " + "upper_bound_ratio must be present." + ) if upper_ww_bounds is None and upper_bound_ratio is None: - raise ValueError("Exactly one of upper_ww_bounds and " - "upper_bound_ratio must be present.") + raise ValueError( + "Exactly one of upper_ww_bounds and " + "upper_bound_ratio must be present." + ) if upper_bound_ratio: self.upper_ww_bounds = [ @@ -145,8 +156,9 @@ def __init__( self.upper_ww_bounds = upper_ww_bounds if len(self.lower_ww_bounds) != len(self.upper_ww_bounds): - raise ValueError('Size of the lower and upper weight ' - 'window bounds do not match') + raise ValueError( + "Size of the lower and upper weight " "window bounds do not match" + ) self.survival_ratio = survival_ratio @@ -158,17 +170,19 @@ def __init__( self.weight_cutoff = weight_cutoff def __repr__(self) -> str: - string = type(self).__name__ + '\n' - string += '{: <16}=\t{}\n'.format('\tID', self._id) - string += '{: <16}=\t{}\n'.format('\tMesh', self.mesh) - string += '{: <16}=\t{}\n'.format('\tParticle Type', self._particle_type) - string += '{: <16}=\t{}\n'.format('\tEnergy Bounds', self._energy_bounds) - string += '{: <16}=\t{}\n'.format('\tMax lower bound ratio', self.max_lower_bound_ratio) - string += '{: <16}=\t{}\n'.format('\tLower WW Bounds', self._lower_ww_bounds) - string += '{: <16}=\t{}\n'.format('\tUpper WW Bounds', self._upper_ww_bounds) - string += '{: <16}=\t{}\n'.format('\tSurvival Ratio', self._survival_ratio) - string += '{: <16}=\t{}\n'.format('\tMax Split', self._max_split) - string += '{: <16}=\t{}\n'.format('\tWeight Cutoff', self._weight_cutoff) + string = type(self).__name__ + "\n" + string += "{: <16}=\t{}\n".format("\tID", self._id) + string += "{: <16}=\t{}\n".format("\tMesh", self.mesh) + string += "{: <16}=\t{}\n".format("\tParticle Type", self._particle_type) + string += "{: <16}=\t{}\n".format("\tEnergy Bounds", self._energy_bounds) + string += "{: <16}=\t{}\n".format( + "\tMax lower bound ratio", self.max_lower_bound_ratio + ) + string += "{: <16}=\t{}\n".format("\tLower WW Bounds", self._lower_ww_bounds) + string += "{: <16}=\t{}\n".format("\tUpper WW Bounds", self._upper_ww_bounds) + string += "{: <16}=\t{}\n".format("\tSurvival Ratio", self._survival_ratio) + string += "{: <16}=\t{}\n".format("\tMax Split", self._max_split) + string += "{: <16}=\t{}\n".format("\tWeight Cutoff", self._weight_cutoff) return string def __eq__(self, other: WeightWindows) -> bool: @@ -179,11 +193,13 @@ def __eq__(self, other: WeightWindows) -> bool: # TODO: add ability to check mesh equality # check several attributes directly - attrs = ('particle_type', - 'survival_ratio', - 'max_lower_bound_ratio', - 'max_split', - 'weight_cutoff') + attrs = ( + "particle_type", + "survival_ratio", + "max_lower_bound_ratio", + "max_split", + "weight_cutoff", + ) for attr in attrs: if getattr(self, attr) != getattr(other, attr): return False @@ -206,7 +222,7 @@ def mesh(self) -> MeshBase: @mesh.setter def mesh(self, mesh: MeshBase): - cv.check_type('Weight window mesh', mesh, MeshBase) + cv.check_type("Weight window mesh", mesh, MeshBase) self._mesh = mesh @property @@ -215,7 +231,7 @@ def particle_type(self) -> str: @particle_type.setter def particle_type(self, pt: str): - cv.check_value('Particle type', pt, _PARTICLES) + cv.check_value("Particle type", pt, _PARTICLES) self._particle_type = pt @property @@ -224,7 +240,7 @@ def energy_bounds(self) -> Iterable[Real]: @energy_bounds.setter def energy_bounds(self, bounds: Iterable[float]): - cv.check_type('Energy bounds', bounds, Iterable, Real) + cv.check_type("Energy bounds", bounds, Iterable, Real) self._energy_bounds = np.asarray(bounds) @property @@ -239,11 +255,9 @@ def lower_ww_bounds(self) -> np.ndarray: @lower_ww_bounds.setter def lower_ww_bounds(self, bounds: Iterable[float]): - cv.check_iterable_type('Lower WW bounds', - bounds, - Real, - min_depth=1, - max_depth=4) + cv.check_iterable_type( + "Lower WW bounds", bounds, Real, min_depth=1, max_depth=4 + ) # reshape data according to mesh and energy bins bounds = np.asarray(bounds) if isinstance(self.mesh, UnstructuredMesh): @@ -258,11 +272,9 @@ def upper_ww_bounds(self) -> np.ndarray: @upper_ww_bounds.setter def upper_ww_bounds(self, bounds: Iterable[float]): - cv.check_iterable_type('Upper WW bounds', - bounds, - Real, - min_depth=1, - max_depth=4) + cv.check_iterable_type( + "Upper WW bounds", bounds, Real, min_depth=1, max_depth=4 + ) # reshape data according to mesh and energy bins bounds = np.asarray(bounds) if isinstance(self.mesh, UnstructuredMesh): @@ -277,8 +289,8 @@ def survival_ratio(self) -> float: @survival_ratio.setter def survival_ratio(self, val: float): - cv.check_type('Survival ratio', val, Real) - cv.check_greater_than('Survival ratio', val, 1.0, True) + cv.check_type("Survival ratio", val, Real) + cv.check_greater_than("Survival ratio", val, 1.0, True) self._survival_ratio = val @property @@ -287,8 +299,8 @@ def max_lower_bound_ratio(self) -> float: @max_lower_bound_ratio.setter def max_lower_bound_ratio(self, val: float): - cv.check_type('Maximum lower bound ratio', val, Real) - cv.check_greater_than('Maximum lower bound ratio', val, 1.0, equality=True) + cv.check_type("Maximum lower bound ratio", val, Real) + cv.check_greater_than("Maximum lower bound ratio", val, 1.0, equality=True) self._max_lower_bound_ratio = val @property @@ -297,7 +309,7 @@ def max_split(self) -> int: @max_split.setter def max_split(self, val: int): - cv.check_type('Max split', val, Integral) + cv.check_type("Max split", val, Integral) self._max_split = val @property @@ -306,8 +318,8 @@ def weight_cutoff(self) -> float: @weight_cutoff.setter def weight_cutoff(self, cutoff: float): - cv.check_type('Weight cutoff', cutoff, Real) - cv.check_greater_than('Weight cutoff', cutoff, 0.0, True) + cv.check_type("Weight cutoff", cutoff, Real) + cv.check_greater_than("Weight cutoff", cutoff, 0.0, True) self._weight_cutoff = cutoff def to_xml_element(self) -> ET.Element: @@ -318,43 +330,45 @@ def to_xml_element(self) -> ET.Element: element : lxml.etree._Element XML element containing the weight window information """ - element = ET.Element('weight_windows') + element = ET.Element("weight_windows") - element.set('id', str(self._id)) + element.set("id", str(self._id)) - subelement = ET.SubElement(element, 'mesh') + subelement = ET.SubElement(element, "mesh") subelement.text = str(self.mesh.id) - subelement = ET.SubElement(element, 'particle_type') + subelement = ET.SubElement(element, "particle_type") subelement.text = self.particle_type if self.energy_bounds is not None: - subelement = ET.SubElement(element, 'energy_bounds') - subelement.text = ' '.join(str(e) for e in self.energy_bounds) + subelement = ET.SubElement(element, "energy_bounds") + subelement.text = " ".join(str(e) for e in self.energy_bounds) - subelement = ET.SubElement(element, 'lower_ww_bounds') - subelement.text = ' '.join(str(b) for b in self.lower_ww_bounds.ravel('F')) + subelement = ET.SubElement(element, "lower_ww_bounds") + subelement.text = " ".join(str(b) for b in self.lower_ww_bounds.ravel("F")) - subelement = ET.SubElement(element, 'upper_ww_bounds') - subelement.text = ' '.join(str(b) for b in self.upper_ww_bounds.ravel('F')) + subelement = ET.SubElement(element, "upper_ww_bounds") + subelement.text = " ".join(str(b) for b in self.upper_ww_bounds.ravel("F")) - subelement = ET.SubElement(element, 'survival_ratio') + subelement = ET.SubElement(element, "survival_ratio") subelement.text = str(self.survival_ratio) if self.max_lower_bound_ratio is not None: - subelement = ET.SubElement(element, 'max_lower_bound_ratio') + subelement = ET.SubElement(element, "max_lower_bound_ratio") subelement.text = str(self.max_lower_bound_ratio) - subelement = ET.SubElement(element, 'max_split') + subelement = ET.SubElement(element, "max_split") subelement.text = str(self.max_split) - subelement = ET.SubElement(element, 'weight_cutoff') + subelement = ET.SubElement(element, "weight_cutoff") subelement.text = str(self.weight_cutoff) return element @classmethod - def from_xml_element(cls, elem: ET.Element, meshes: dict[int, MeshBase]) -> WeightWindows: + def from_xml_element( + cls, elem: ET.Element, meshes: dict[int, MeshBase] + ) -> WeightWindows: """Generate weight window settings from an XML element Parameters @@ -370,29 +384,29 @@ def from_xml_element(cls, elem: ET.Element, meshes: dict[int, MeshBase]) -> Weig Weight windows object """ # Get mesh for weight windows - mesh_id = int(get_text(elem, 'mesh')) + mesh_id = int(get_text(elem, "mesh")) if mesh_id not in meshes: raise ValueError(f'Could not locate mesh with ID "{mesh_id}"') mesh = meshes[mesh_id] # Read all other parameters - lower_ww_bounds = [float(l) for l in get_text(elem, 'lower_ww_bounds').split()] - upper_ww_bounds = [float(u) for u in get_text(elem, 'upper_ww_bounds').split()] - e_bounds = [float(b) for b in get_text(elem, 'energy_bounds').split()] - particle_type = get_text(elem, 'particle_type') - survival_ratio = float(get_text(elem, 'survival_ratio')) + lower_ww_bounds = [float(l) for l in get_text(elem, "lower_ww_bounds").split()] + upper_ww_bounds = [float(u) for u in get_text(elem, "upper_ww_bounds").split()] + e_bounds = [float(b) for b in get_text(elem, "energy_bounds").split()] + particle_type = get_text(elem, "particle_type") + survival_ratio = float(get_text(elem, "survival_ratio")) ww_shape = (len(e_bounds) - 1,) + mesh.dimension[::-1] lower_ww_bounds = np.array(lower_ww_bounds).reshape(ww_shape).T upper_ww_bounds = np.array(upper_ww_bounds).reshape(ww_shape).T max_lower_bound_ratio = None - if get_text(elem, 'max_lower_bound_ratio'): - max_lower_bound_ratio = float(get_text(elem, 'max_lower_bound_ratio')) + if get_text(elem, "max_lower_bound_ratio"): + max_lower_bound_ratio = float(get_text(elem, "max_lower_bound_ratio")) - max_split = int(get_text(elem, 'max_split')) - weight_cutoff = float(get_text(elem, 'weight_cutoff')) - id = int(get_text(elem, 'id')) + max_split = int(get_text(elem, "max_split")) + weight_cutoff = float(get_text(elem, "weight_cutoff")) + id = int(get_text(elem, "id")) return cls( mesh=mesh, @@ -404,7 +418,7 @@ def from_xml_element(cls, elem: ET.Element, meshes: dict[int, MeshBase]) -> Weig max_lower_bound_ratio=max_lower_bound_ratio, max_split=max_split, weight_cutoff=weight_cutoff, - id=id + id=id, ) @classmethod @@ -424,25 +438,25 @@ def from_hdf5(cls, group: h5py.Group, meshes: dict[int, MeshBase]) -> WeightWind A weight window object """ - id = int(group.name.split('/')[-1].lstrip('weight_windows')) - mesh_id = group['mesh'][()] + id = int(group.name.split("/")[-1].lstrip("weight_windows")) + mesh_id = group["mesh"][()] mesh = meshes[mesh_id] - ptype = group['particle_type'][()].decode() - e_bounds = group['energy_bounds'][()] + ptype = group["particle_type"][()].decode() + e_bounds = group["energy_bounds"][()] # weight window bounds are stored with the shape (e, k, j, i) # in C++ and HDF5 -- the opposite of how they are stored here - shape = (e_bounds.size - 1, *mesh.dimension[::-1]) - lower_ww_bounds = group['lower_ww_bounds'][()].reshape(shape).T - upper_ww_bounds = group['upper_ww_bounds'][()].reshape(shape).T - survival_ratio = group['survival_ratio'][()] + shape = (e_bounds.size - 1, *mesh.dimension[::-1]) + lower_ww_bounds = group["lower_ww_bounds"][()].reshape(shape).T + upper_ww_bounds = group["upper_ww_bounds"][()].reshape(shape).T + survival_ratio = group["survival_ratio"][()] max_lower_bound_ratio = None - if group.get('max_lower_bound_ratio') is not None: - max_lower_bound_ratio = group['max_lower_bound_ratio'][()] + if group.get("max_lower_bound_ratio") is not None: + max_lower_bound_ratio = group["max_lower_bound_ratio"][()] - max_split = group['max_split'][()] - weight_cutoff = group['weight_cutoff'][()] + max_split = group["max_split"][()] + weight_cutoff = group["weight_cutoff"][()] return cls( mesh=mesh, @@ -454,7 +468,7 @@ def from_hdf5(cls, group: h5py.Group, meshes: dict[int, MeshBase]) -> WeightWind max_lower_bound_ratio=max_lower_bound_ratio, max_split=max_split, weight_cutoff=weight_cutoff, - id=id + id=id, ) @@ -482,40 +496,41 @@ def wwinp_to_wws(path: PathLike) -> list[WeightWindows]: # header value checks if _if != 1: - raise ValueError(f'Found incorrect file type, if: {_if}') + raise ValueError(f"Found incorrect file type, if: {_if}") if iv > 1: # read number of time bins for each particle, 'nt(1...ni)' - nt = np.fromstring(wwinp.readline(), sep=' ', dtype=int) + nt = np.fromstring(wwinp.readline(), sep=" ", dtype=int) # raise error if time bins are present for now - raise ValueError('Time-dependent weight windows ' - 'are not yet supported') + raise ValueError("Time-dependent weight windows " "are not yet supported") else: nt = ni * [1] # read number of energy bins for each particle, 'ne(1...ni)' - ne = np.fromstring(wwinp.readline(), sep=' ', dtype=int) + ne = np.fromstring(wwinp.readline(), sep=" ", dtype=int) # read coarse mesh dimensions and lower left corner - mesh_description = np.fromstring(wwinp.readline(), sep=' ') + mesh_description = np.fromstring(wwinp.readline(), sep=" ") nfx, nfy, nfz = mesh_description[:3].astype(int) xyz0 = mesh_description[3:] # read cylindrical and spherical mesh vectors if present if nr == 16: # read number of coarse bins - line_arr = np.fromstring(wwinp.readline(), sep=' ') + line_arr = np.fromstring(wwinp.readline(), sep=" ") ncx, ncy, ncz = line_arr[:3].astype(int) # read polar vector (x1, y1, z1) xyz1 = line_arr[3:] # read azimuthal vector (x2, y2, z2) - line_arr = np.fromstring(wwinp.readline(), sep=' ') + line_arr = np.fromstring(wwinp.readline(), sep=" ") xyz2 = line_arr[:3] # oriented polar and azimuthal vectors aren't yet supported if np.count_nonzero(xyz1) or np.count_nonzero(xyz2): - raise NotImplementedError('Custom sphere/cylinder orientations are not supported') + raise NotImplementedError( + "Custom sphere/cylinder orientations are not supported" + ) # read geometry type nwg = int(line_arr[-1]) @@ -523,13 +538,12 @@ def wwinp_to_wws(path: PathLike) -> list[WeightWindows]: elif nr == 10: # read rectilinear data: # number of coarse mesh bins and mesh type - ncx, ncy, ncz, nwg = \ - np.fromstring(wwinp.readline(), sep=' ').astype(int) + ncx, ncy, ncz, nwg = np.fromstring(wwinp.readline(), sep=" ").astype(int) else: - raise RuntimeError(f'Invalid mesh description (nr) found: {nr}') + raise RuntimeError(f"Invalid mesh description (nr) found: {nr}") # read BLOCK 2 and BLOCK 3 data into a single array - ww_data = np.fromstring(wwinp.read(), sep=' ') + ww_data = np.fromstring(wwinp.read(), sep=" ") # extract mesh data from the ww_data array start_idx = 0 @@ -537,26 +551,30 @@ def wwinp_to_wws(path: PathLike) -> list[WeightWindows]: # first values in the mesh definition arrays are the first # coordinate of the grid end_idx = start_idx + 1 + 3 * ncx - i0, i_vals = ww_data[start_idx], ww_data[start_idx+1:end_idx] + i0, i_vals = ww_data[start_idx], ww_data[start_idx + 1 : end_idx] start_idx = end_idx end_idx = start_idx + 1 + 3 * ncy - j0, j_vals = ww_data[start_idx], ww_data[start_idx+1:end_idx] + j0, j_vals = ww_data[start_idx], ww_data[start_idx + 1 : end_idx] start_idx = end_idx end_idx = start_idx + 1 + 3 * ncz - k0, k_vals = ww_data[start_idx], ww_data[start_idx+1:end_idx] + k0, k_vals = ww_data[start_idx], ww_data[start_idx + 1 : end_idx] start_idx = end_idx # mesh consistency checks if nr == 16 and nwg == 1 or nr == 10 and nwg != 1: - raise ValueError(f'Mesh description in header ({nr}) ' - f'does not match the mesh type ({nwg})') + raise ValueError( + f"Mesh description in header ({nr}) " + f"does not match the mesh type ({nwg})" + ) if nr == 10 and (xyz0 != (i0, j0, k0)).any(): - raise ValueError(f'Mesh origin in the header ({xyz0}) ' - f' does not match the origin in the mesh ' - f' description ({i0, j0, k0})') + raise ValueError( + f"Mesh origin in the header ({xyz0}) " + f" does not match the origin in the mesh " + f" description ({i0, j0, k0})" + ) # create openmc mesh object grids = [] @@ -564,13 +582,14 @@ def wwinp_to_wws(path: PathLike) -> list[WeightWindows]: for grid0, grid_vals, n_pnts in mesh_definition: # file spec checks for the mesh definition if (grid_vals[2::3] != 1.0).any(): - raise ValueError('One or more mesh ratio value, qx, ' - 'is not equal to one') + raise ValueError("One or more mesh ratio value, qx, " "is not equal to one") s = int(grid_vals[::3].sum()) if s != n_pnts: - raise ValueError(f'Sum of the fine bin entries, {s}, does ' - f'not match the number of fine bins, {n_pnts}') + raise ValueError( + f"Sum of the fine bin entries, {s}, does " + f"not match the number of fine bins, {n_pnts}" + ) # extend the grid based on the next coarse bin endpoint, px # and the number of fine bins in the coarse bin, sx @@ -589,19 +608,16 @@ def wwinp_to_wws(path: PathLike) -> list[WeightWindows]: r_grid=grids[0], z_grid=grids[1], phi_grid=grids[2], - origin = xyz0, + origin=xyz0, ) elif nwg == 3: mesh = SphericalMesh( - r_grid=grids[0], - theta_grid=grids[1], - phi_grid=grids[2], - origin = xyz0 + r_grid=grids[0], theta_grid=grids[1], phi_grid=grids[2], origin=xyz0 ) # extract weight window values from array wws = [] - for ne_i, nt_i, particle_type in zip(ne, nt, ('neutron', 'photon')): + for ne_i, nt_i, particle_type in zip(ne, nt, ("neutron", "photon")): # no information to read for this particle if # either the energy bins or time bins are empty if ne_i == 0 or nt_i == 0: @@ -635,12 +651,14 @@ def wwinp_to_wws(path: PathLike) -> list[WeightWindows]: start_idx = end_idx # create a weight window object - ww = WeightWindows(id=None, - mesh=mesh, - lower_ww_bounds=ww_values, - upper_bound_ratio=5.0, - energy_bounds=energy_bounds, - particle_type=particle_type) + ww = WeightWindows( + id=None, + mesh=mesh, + lower_ww_bounds=ww_values, + upper_bound_ratio=5.0, + energy_bounds=energy_bounds, + particle_type=particle_type, + ) wws.append(ww) return wws @@ -694,17 +712,17 @@ class WeightWindowGenerator: Whether or not to apply weight windows on the fly. """ - _MAGIC_PARAMS = {'value': str, 'threshold': float, 'ratio': float} + _MAGIC_PARAMS = {"value": str, "threshold": float, "ratio": float} def __init__( self, mesh: openmc.MeshBase, energy_bounds: Sequence[float] | None = None, - particle_type: str = 'neutron', - method: str = 'magic', + particle_type: str = "neutron", + method: str = "magic", max_realizations: int = 1, update_interval: int = 1, - on_the_fly: bool = True + on_the_fly: bool = True, ): self._update_parameters = None @@ -719,7 +737,7 @@ def __init__( self.on_the_fly = on_the_fly def __repr__(self): - string = type(self).__name__ + '\n' + string = type(self).__name__ + "\n" string += f'\t{"Mesh":<20}=\t{self.mesh.id}\n' string += f'\t{"Particle:":<20}=\t{self.particle_type}\n' string += f'\t{"Energy Bounds:":<20}=\t{self.energy_bounds}\n' @@ -739,7 +757,7 @@ def mesh(self) -> openmc.MeshBase: @mesh.setter def mesh(self, m: openmc.MeshBase): - cv.check_type('mesh', m, openmc.MeshBase) + cv.check_type("mesh", m, openmc.MeshBase) self._mesh = m @property @@ -748,7 +766,7 @@ def energy_bounds(self) -> Iterable[Real]: @energy_bounds.setter def energy_bounds(self, eb: Iterable[float]): - cv.check_type('energy bounds', eb, Iterable, Real) + cv.check_type("energy bounds", eb, Iterable, Real) self._energy_bounds = eb @property @@ -757,7 +775,7 @@ def particle_type(self) -> str: @particle_type.setter def particle_type(self, pt: str): - cv.check_value('particle type', pt, ('neutron', 'photon')) + cv.check_value("particle type", pt, ("neutron", "photon")) self._particle_type = pt @property @@ -766,8 +784,8 @@ def method(self) -> str: @method.setter def method(self, m: str): - cv.check_type('generation method', m, str) - cv.check_value('generation method', m, {'magic'}) + cv.check_type("generation method", m, str) + cv.check_value("generation method", m, {"magic"}) self._method = m if self._update_parameters is not None: try: @@ -781,8 +799,8 @@ def max_realizations(self) -> int: @max_realizations.setter def max_realizations(self, m: int): - cv.check_type('max tally realizations', m, Integral) - cv.check_greater_than('max tally realizations', m, 0) + cv.check_type("max tally realizations", m, Integral) + cv.check_greater_than("max tally realizations", m, 0) self._max_realizations = m @property @@ -791,8 +809,8 @@ def update_interval(self) -> int: @update_interval.setter def update_interval(self, ui: int): - cv.check_type('update interval', ui, Integral) - cv.check_greater_than('update interval', ui , 0) + cv.check_type("update interval", ui, Integral) + cv.check_greater_than("update interval", ui, 0) self._update_interval = ui @property @@ -800,14 +818,18 @@ def update_parameters(self) -> dict: return self._update_parameters def _check_update_parameters(self, params: dict): - if self.method == 'magic': + if self.method == "magic": check_params = self._MAGIC_PARAMS for key, val in params.items(): if key not in check_params: - raise ValueError(f'Invalid param "{key}" for {self.method} ' - 'weight window generation') - cv.check_type(f'weight window generation param: "{key}"', val, self._MAGIC_PARAMS[key]) + raise ValueError( + f'Invalid param "{key}" for {self.method} ' + "weight window generation" + ) + cv.check_type( + f'weight window generation param: "{key}"', val, self._MAGIC_PARAMS[key] + ) @update_parameters.setter def update_parameters(self, params: dict): @@ -820,13 +842,13 @@ def on_the_fly(self) -> bool: @on_the_fly.setter def on_the_fly(self, otf: bool): - cv.check_type('on the fly generation', otf, bool) + cv.check_type("on the fly generation", otf, bool) self._on_the_fly = otf def _update_parameters_subelement(self, element: ET.Element): if not self.update_parameters: return - params_element = ET.SubElement(element, 'update_parameters') + params_element = ET.SubElement(element, "update_parameters") for pname, value in self.update_parameters.items(): param_element = ET.SubElement(params_element, pname) param_element.text = str(value) @@ -843,7 +865,7 @@ def _sanitize_update_parameters(cls, method: str, update_parameters: dict): update_parameters : dict The update parameters as-read from the XML node (keys: str, values: str) """ - if method == 'magic': + if method == "magic": check_params = cls._MAGIC_PARAMS for param, param_type in check_params.items(): @@ -851,24 +873,23 @@ def _sanitize_update_parameters(cls, method: str, update_parameters: dict): update_parameters[param] = param_type(update_parameters[param]) def to_xml_element(self): - """Creates a 'weight_window_generator' element to be written to an XML file. - """ - element = ET.Element('weight_windows_generator') + """Creates a 'weight_window_generator' element to be written to an XML file.""" + element = ET.Element("weight_windows_generator") - mesh_elem = ET.SubElement(element, 'mesh') + mesh_elem = ET.SubElement(element, "mesh") mesh_elem.text = str(self.mesh.id) if self.energy_bounds is not None: - subelement = ET.SubElement(element, 'energy_bounds') - subelement.text = ' '.join(str(e) for e in self.energy_bounds) - particle_elem = ET.SubElement(element, 'particle_type') + subelement = ET.SubElement(element, "energy_bounds") + subelement.text = " ".join(str(e) for e in self.energy_bounds) + particle_elem = ET.SubElement(element, "particle_type") particle_elem.text = self.particle_type - realizations_elem = ET.SubElement(element, 'max_realizations') + realizations_elem = ET.SubElement(element, "max_realizations") realizations_elem.text = str(self.max_realizations) - update_interval_elem = ET.SubElement(element, 'update_interval') + update_interval_elem = ET.SubElement(element, "update_interval") update_interval_elem.text = str(self.update_interval) - otf_elem = ET.SubElement(element, 'on_the_fly') + otf_elem = ET.SubElement(element, "on_the_fly") otf_elem.text = str(self.on_the_fly).lower() - method_elem = ET.SubElement(element, 'method') + method_elem = ET.SubElement(element, "method") method_elem.text = self.method if self.update_parameters is not None: self._update_parameters_subelement(element) @@ -894,22 +915,22 @@ def from_xml_element(cls, elem: ET.Element, meshes: dict) -> WeightWindowGenerat openmc.WeightWindowGenerator """ - mesh_id = int(get_text(elem, 'mesh')) + mesh_id = int(get_text(elem, "mesh")) mesh = meshes[mesh_id] - energy_bounds = [float(x) for x in get_text(elem, 'energy_bounds').split()] - particle_type = get_text(elem, 'particle_type') + energy_bounds = [float(x) for x in get_text(elem, "energy_bounds").split()] + particle_type = get_text(elem, "particle_type") wwg = cls(mesh, energy_bounds, particle_type) - wwg.max_realizations = int(get_text(elem, 'max_realizations')) - wwg.update_interval = int(get_text(elem, 'update_interval')) - wwg.on_the_fly = bool(get_text(elem, 'on_the_fly')) - wwg.method = get_text(elem, 'method') + wwg.max_realizations = int(get_text(elem, "max_realizations")) + wwg.update_interval = int(get_text(elem, "update_interval")) + wwg.on_the_fly = bool(get_text(elem, "on_the_fly")) + wwg.method = get_text(elem, "method") - if elem.find('update_parameters') is not None: + if elem.find("update_parameters") is not None: update_parameters = {} - params_elem = elem.find('update_parameters') + params_elem = elem.find("update_parameters") for entry in params_elem: update_parameters[entry.tag] = entry.text @@ -918,7 +939,8 @@ def from_xml_element(cls, elem: ET.Element, meshes: dict) -> WeightWindowGenerat return wwg -def hdf5_to_wws(path='weight_windows.h5'): + +def hdf5_to_wws(path="weight_windows.h5"): """Create WeightWindows instances from a weight windows HDF5 file .. versionadded:: 0.14.0 @@ -936,7 +958,10 @@ def hdf5_to_wws(path='weight_windows.h5'): with h5py.File(path) as h5_file: # read in all of the meshes in the mesh node meshes = {} - for mesh_group in h5_file['meshes']: - mesh = MeshBase.from_hdf5(h5_file['meshes'][mesh_group]) + for mesh_group in h5_file["meshes"]: + mesh = MeshBase.from_hdf5(h5_file["meshes"][mesh_group]) meshes[mesh.id] = mesh - return [WeightWindows.from_hdf5(ww, meshes) for ww in h5_file['weight_windows'].values()] + return [ + WeightWindows.from_hdf5(ww, meshes) + for ww in h5_file["weight_windows"].values() + ] diff --git a/pyproject.toml b/pyproject.toml index d0419d550f4..6ffdbe3aa6b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,6 +51,8 @@ docs = [ test = ["packaging", "pytest", "pytest-cov", "colorama", "openpyxl"] ci = ["cpp-coveralls", "coveralls"] vtk = ["vtk"] +format = ["black~=24.8"] +develop = ["openmc[test,format]"] [project.urls] Homepage = "https://openmc.org" @@ -67,6 +69,10 @@ exclude = ['tests*'] "openmc.data" = ["*.txt", "*.DAT", "*.json", "*.h5"] "openmc.lib" = ["libopenmc.dylib", "libopenmc.so"] +[tool.black] +target-version = ["py311", "py312", "py313"] +line-length = 88 + [project.scripts] openmc-ace-to-hdf5 = "scripts.openmc_ace_to_hdf5:main" openmc-plot-mesh-tally = "scripts.openmc_plot_mesh_tally:main" diff --git a/tests/conftest.py b/tests/conftest.py index cd86da53900..f2e9603ae2c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -5,17 +5,17 @@ def pytest_addoption(parser): - parser.addoption('--exe') - parser.addoption('--mpi', action='store_true') - parser.addoption('--mpiexec') - parser.addoption('--mpi-np') - parser.addoption('--update', action='store_true') - parser.addoption('--build-inputs', action='store_true') - parser.addoption('--event', action='store_true') + parser.addoption("--exe") + parser.addoption("--mpi", action="store_true") + parser.addoption("--mpiexec") + parser.addoption("--mpi-np") + parser.addoption("--update", action="store_true") + parser.addoption("--build-inputs", action="store_true") + parser.addoption("--event", action="store_true") def pytest_configure(config): - opts = ['exe', 'mpi', 'mpiexec', 'mpi_np', 'update', 'build_inputs', 'event'] + opts = ["exe", "mpi", "mpiexec", "mpi_np", "update", "build_inputs", "event"] for opt in opts: if config.getoption(opt) is not None: regression_config[opt] = config.getoption(opt) @@ -30,7 +30,7 @@ def run_in_tmpdir(tmpdir): orig.chdir() -@pytest.fixture(scope='session', autouse=True) +@pytest.fixture(scope="session", autouse=True) def resolve_paths(): - with openmc.config.patch('resolve_paths', False): + with openmc.config.patch("resolve_paths", False): yield diff --git a/tests/dummy_operator.py b/tests/dummy_operator.py index 1bb00129d04..90295a904ee 100644 --- a/tests/dummy_operator.py +++ b/tests/dummy_operator.py @@ -8,8 +8,14 @@ from openmc.deplete.reaction_rates import ReactionRates from openmc.deplete.abc import TransportOperator, OperatorResult from openmc.deplete import ( - CECMIntegrator, PredictorIntegrator, CELIIntegrator, LEQIIntegrator, - EPCRK4Integrator, CF4Integrator, SICELIIntegrator, SILEQIIntegrator + CECMIntegrator, + PredictorIntegrator, + CELIIntegrator, + LEQIIntegrator, + EPCRK4Integrator, + CF4Integrator, + SICELIIntegrator, + SILEQIIntegrator, ) # Bundle for nicely passing test data to depletion unit tests @@ -18,48 +24,63 @@ # similar for atoms_2, but for type 2. This includes the first step # Solutions should be the exact solution that can be obtained using # the DummyOperator depletion matrix with two 0.75 second time steps -DepletionSolutionTuple = namedtuple( - "DepletionSolutionTuple", "solver atoms_1 atoms_2") +DepletionSolutionTuple = namedtuple("DepletionSolutionTuple", "solver atoms_1 atoms_2") predictor_solution = DepletionSolutionTuple( - PredictorIntegrator, np.array([1.0, 2.46847546272295, 4.11525874568034]), - np.array([1.0, 0.986431226850467, -0.0581692232513460])) + PredictorIntegrator, + np.array([1.0, 2.46847546272295, 4.11525874568034]), + np.array([1.0, 0.986431226850467, -0.0581692232513460]), +) cecm_solution = DepletionSolutionTuple( - CECMIntegrator, np.array([1.0, 1.86872629872102, 2.18097439443550]), - np.array([1.0, 1.395525772416039, 2.69429754646747])) + CECMIntegrator, + np.array([1.0, 1.86872629872102, 2.18097439443550]), + np.array([1.0, 1.395525772416039, 2.69429754646747]), +) cf4_solution = DepletionSolutionTuple( - CF4Integrator, np.array([1.0, 2.06101629, 2.57241318]), - np.array([1.0, 1.37783588, 2.63731630])) + CF4Integrator, + np.array([1.0, 2.06101629, 2.57241318]), + np.array([1.0, 1.37783588, 2.63731630]), +) epc_rk4_solution = DepletionSolutionTuple( - EPCRK4Integrator, np.array([1.0, 2.01978516, 2.05246421]), - np.array([1.0, 1.42038037, 3.06177191])) + EPCRK4Integrator, + np.array([1.0, 2.01978516, 2.05246421]), + np.array([1.0, 1.42038037, 3.06177191]), +) celi_solution = DepletionSolutionTuple( - CELIIntegrator, np.array([1.0, 1.82078767, 2.68441779]), - np.array([1.0, 0.97122898, 0.05125966])) + CELIIntegrator, + np.array([1.0, 1.82078767, 2.68441779]), + np.array([1.0, 0.97122898, 0.05125966]), +) si_celi_solution = DepletionSolutionTuple( - SICELIIntegrator, np.array([1.0, 2.03325094, 2.69291933]), - np.array([1.0, 1.16826254, 0.37907772])) + SICELIIntegrator, + np.array([1.0, 2.03325094, 2.69291933]), + np.array([1.0, 1.16826254, 0.37907772]), +) leqi_solution = DepletionSolutionTuple( - LEQIIntegrator, np.array([1.0, 1.82078767, 2.74526197]), - np.array([1.0, 0.97122898, 0.23339915])) + LEQIIntegrator, + np.array([1.0, 1.82078767, 2.74526197]), + np.array([1.0, 0.97122898, 0.23339915]), +) si_leqi_solution = DepletionSolutionTuple( - SILEQIIntegrator, np.array([1.0, 2.03325094, 2.92711288]), - np.array([1.0, 1.16826254, 0.53753236])) + SILEQIIntegrator, + np.array([1.0, 2.03325094, 2.92711288]), + np.array([1.0, 1.16826254, 0.53753236]), +) SCHEMES = { @@ -130,6 +151,7 @@ class DummyOperator(TransportOperator): y_2(1.5) ~ 3.1726475740397628 """ + def __init__(self, previous_results=None): self.prev_res = previous_results self.chain = TestChain() diff --git a/tests/regression_tests/__init__.py b/tests/regression_tests/__init__.py index e1cb56f1dd8..92e76ae989d 100644 --- a/tests/regression_tests/__init__.py +++ b/tests/regression_tests/__init__.py @@ -2,13 +2,13 @@ # Test configuration options for regression tests config = { - 'event' : False, - 'exe': 'openmc', - 'mpi': False, - 'mpiexec': 'mpiexec', - 'mpi_np': '2', - 'update': False, - 'build_inputs': False + "event": False, + "exe": "openmc", + "mpi": False, + "mpiexec": "mpiexec", + "mpi_np": "2", + "update": False, + "build_inputs": False, } @@ -28,9 +28,10 @@ def assert_atoms_equal(res_ref, res_test, tol=1e-5): for nuc in res_test[0].index_nuc: _, y_test = res_test.get_atoms(mat, nuc) _, y_ref = res_ref.get_atoms(mat, nuc) - assert y_test == pytest.approx(y_ref, rel=tol), \ - f'Atoms not equal for material {mat}, nuclide {nuc}\n' \ - f'y_ref={y_ref}\ny_test={y_test}' + assert y_test == pytest.approx(y_ref, rel=tol), ( + f"Atoms not equal for material {mat}, nuclide {nuc}\n" + f"y_ref={y_ref}\ny_test={y_test}" + ) def assert_reaction_rates_equal(res_ref, res_test, tol=1e-5): @@ -40,6 +41,7 @@ def assert_reaction_rates_equal(res_ref, res_test, tol=1e-5): for rx in reactions.index_rx: y_test = res_test.get_reaction_rate(mat, nuc, rx)[1] y_ref = res_ref.get_reaction_rate(mat, nuc, rx)[1] - assert y_test == pytest.approx(y_ref, rel=tol), \ - f'Reaction rate not equal for material {mat}, nuclide '\ - f'{nuc}, {rx}\ny_ref={y_ref}\ny_test={y_test}' + assert y_test == pytest.approx(y_ref, rel=tol), ( + f"Reaction rate not equal for material {mat}, nuclide " + f"{nuc}, {rx}\ny_ref={y_ref}\ny_test={y_test}" + ) diff --git a/tests/regression_tests/adj_cell_rotation/test.py b/tests/regression_tests/adj_cell_rotation/test.py index 3fe24053665..e2cf1949443 100644 --- a/tests/regression_tests/adj_cell_rotation/test.py +++ b/tests/regression_tests/adj_cell_rotation/test.py @@ -9,12 +9,12 @@ def model(): model = openmc.model.Model() fuel = openmc.Material() - fuel.set_density('g/cc', 10.0) - fuel.add_nuclide('U235', 1.0) + 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) + h1.set_density("g/cc", 0.1) + h1.add_nuclide("H1", 0.1) inner_sphere = openmc.Sphere(x0=1.0, r=5.0) @@ -24,9 +24,9 @@ def model(): # Create one cell on top of the other. Only one # has a rotation - box = openmc.model.RectangularPrism(15., 15., 'z', boundary_type='vacuum') - lower_z = openmc.ZPlane(-7.5, boundary_type='vacuum') - upper_z = openmc.ZPlane(22.5, boundary_type='vacuum') + box = openmc.model.RectangularPrism(15.0, 15.0, "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) @@ -39,12 +39,12 @@ def model(): model.settings.particles = 10000 model.settings.inactive = 5 model.settings.batches = 10 - source_box = openmc.stats.Box((-4., -4., -4.), (4., 4., 4.)) + source_box = openmc.stats.Box((-4.0, -4.0, -4.0), (4.0, 4.0, 4.0)) model.settings.source = openmc.IndependentSource(space=source_box) return model def test_rotation(model): - harness = PyAPITestHarness('statepoint.10.h5', model) + harness = PyAPITestHarness("statepoint.10.h5", model) harness.main() diff --git a/tests/regression_tests/albedo_box/test.py b/tests/regression_tests/albedo_box/test.py index 179f58e5b36..fe28aa088bb 100755 --- a/tests/regression_tests/albedo_box/test.py +++ b/tests/regression_tests/albedo_box/test.py @@ -2,5 +2,5 @@ def test_albedo_box(): - harness = TestHarness('statepoint.10.h5') + harness = TestHarness("statepoint.10.h5") harness.main() diff --git a/tests/regression_tests/asymmetric_lattice/test.py b/tests/regression_tests/asymmetric_lattice/test.py index fadf272ffd3..4f8d749d665 100644 --- a/tests/regression_tests/asymmetric_lattice/test.py +++ b/tests/regression_tests/asymmetric_lattice/test.py @@ -13,27 +13,29 @@ def __init__(self, *args, **kwargs): # Extract universes encapsulating fuel and water assemblies geometry = self._model.geometry - water = geometry.get_universes_by_name('water assembly (hot)')[0] - fuel = geometry.get_universes_by_name('fuel assembly (hot)')[0] + water = geometry.get_universes_by_name("water assembly (hot)")[0] + fuel = geometry.get_universes_by_name("fuel assembly (hot)")[0] # Construct a 3x3 lattice of fuel assemblies - core_lat = openmc.RectLattice(name='3x3 Core Lattice', lattice_id=202) + core_lat = openmc.RectLattice(name="3x3 Core Lattice", lattice_id=202) core_lat.lower_left = (-32.13, -32.13) core_lat.pitch = (21.42, 21.42) - core_lat.universes = [[fuel, water, water], - [fuel, fuel, fuel], - [water, water, water]] + core_lat.universes = [ + [fuel, water, water], + [fuel, fuel, fuel], + [water, water, water], + ] # Create bounding surfaces - min_x = openmc.XPlane(-32.13, boundary_type='reflective') - max_x = openmc.XPlane(+32.13, boundary_type='reflective') - min_y = openmc.YPlane(-32.13, boundary_type='reflective') - max_y = openmc.YPlane(+32.13, boundary_type='reflective') - min_z = openmc.ZPlane(0, boundary_type='reflective') - max_z = openmc.ZPlane(+32.13, boundary_type='reflective') + min_x = openmc.XPlane(-32.13, boundary_type="reflective") + max_x = openmc.XPlane(+32.13, boundary_type="reflective") + min_y = openmc.YPlane(-32.13, boundary_type="reflective") + max_y = openmc.YPlane(+32.13, boundary_type="reflective") + min_z = openmc.ZPlane(0, boundary_type="reflective") + max_z = openmc.ZPlane(+32.13, boundary_type="reflective") # Define root universe - root_univ = openmc.Universe(universe_id=0, name='root universe') + root_univ = openmc.Universe(universe_id=0, name="root universe") root_cell = openmc.Cell(cell_id=1) root_cell.region = +min_x & -max_x & +min_y & -max_y & +min_z & -max_z root_cell.fill = core_lat @@ -46,9 +48,9 @@ def __init__(self, *args, **kwargs): distrib_filter = openmc.DistribcellFilter(27) # Initialize the tallies - tally = openmc.Tally(name='distribcell tally', tally_id=27) + tally = openmc.Tally(name="distribcell tally", tally_id=27) tally.filters.append(distrib_filter) - tally.scores.append('nu-fission') + tally.scores.append("nu-fission") # Assign the tallies file to the input set self._model.tallies.append(tally) @@ -56,7 +58,7 @@ def __init__(self, *args, **kwargs): # Specify summary output and correct source sampling box self._model.settings.source = openmc.IndependentSource( space=openmc.stats.Box([-32, -32, 0], [32, 32, 32]), - constraints={'fissionable': True} + constraints={"fissionable": True}, ) def _get_results(self, hash_output=True): @@ -67,29 +69,29 @@ def _get_results(self, hash_output=True): sp = openmc.StatePoint(statepoint) # Extract the tally of interest - tally = sp.get_tally(name='distribcell tally') + tally = sp.get_tally(name="distribcell tally") # Create a string of all mean, std. dev. values for both tallies - outstr = '' - outstr += '\n'.join(map('{:.8e}'.format, tally.mean.flatten())) + '\n' - outstr += '\n'.join(map('{:.8e}'.format, tally.std_dev.flatten())) + '\n' + outstr = "" + outstr += "\n".join(map("{:.8e}".format, tally.mean.flatten())) + "\n" + outstr += "\n".join(map("{:.8e}".format, tally.std_dev.flatten())) + "\n" # Extract fuel assembly lattices from the summary cells = sp.summary.geometry.get_all_cells() fuel_cell = cells[27] # Append a string of lattice distribcell offsets to the string - outstr += '\n'.join(fuel_cell.paths) + '\n' + outstr += "\n".join(fuel_cell.paths) + "\n" # Hash the results if necessary if hash_output: sha512 = hashlib.sha512() - sha512.update(outstr.encode('utf-8')) + sha512.update(outstr.encode("utf-8")) outstr = sha512.hexdigest() return outstr def test_asymmetric_lattice(): - harness = AsymmetricLatticeTestHarness('statepoint.10.h5') + harness = AsymmetricLatticeTestHarness("statepoint.10.h5") harness.main() diff --git a/tests/regression_tests/cmfd_feed/test.py b/tests/regression_tests/cmfd_feed/test.py index d513dee2aa5..e8c610f8650 100644 --- a/tests/regression_tests/cmfd_feed/test.py +++ b/tests/regression_tests/cmfd_feed/test.py @@ -26,12 +26,12 @@ def test_cmfd_physical_adjoint(): cmfd_run.tally_begin = 5 cmfd_run.solver_begin = 5 cmfd_run.feedback = True - cmfd_run.gauss_seidel_tolerance = [1.e-15, 1.e-20] + cmfd_run.gauss_seidel_tolerance = [1.0e-15, 1.0e-20] cmfd_run.run_adjoint = True - cmfd_run.adjoint_type = 'physical' + cmfd_run.adjoint_type = "physical" cmfd_run.run() - assert(np.all(cmfd_run._phi == cmfd_run._adj_phi)) - assert(cmfd_run._adj_keff == cmfd_run._keff) + assert np.all(cmfd_run._phi == cmfd_run._adj_phi) + assert cmfd_run._adj_keff == cmfd_run._keff def test_cmfd_math_adjoint(): @@ -56,12 +56,12 @@ def test_cmfd_math_adjoint(): cmfd_run.tally_begin = 5 cmfd_run.solver_begin = 5 cmfd_run.feedback = True - cmfd_run.gauss_seidel_tolerance = [1.e-15, 1.e-20] + cmfd_run.gauss_seidel_tolerance = [1.0e-15, 1.0e-20] cmfd_run.run_adjoint = True - cmfd_run.adjoint_type = 'math' + cmfd_run.adjoint_type = "math" cmfd_run.run() - assert(np.all(cmfd_run._phi == cmfd_run._adj_phi)) - assert(cmfd_run._adj_keff == cmfd_run._keff) + assert np.all(cmfd_run._phi == cmfd_run._adj_phi) + assert cmfd_run._adj_keff == cmfd_run._keff def test_cmfd_write_matrices(): @@ -84,38 +84,38 @@ def test_cmfd_write_matrices(): cmfd_run.mesh = cmfd_mesh cmfd_run.tally_begin = 5 cmfd_run.solver_begin = 5 - cmfd_run.display = {'dominance': True} + cmfd_run.display = {"dominance": True} cmfd_run.feedback = True - cmfd_run.gauss_seidel_tolerance = [1.e-15, 1.e-20] + cmfd_run.gauss_seidel_tolerance = [1.0e-15, 1.0e-20] cmfd_run.write_matrices = True cmfd_run.run() # Load loss matrix from numpy output file - loss_np = scipy.sparse.load_npz('loss.npz').todense() + loss_np = scipy.sparse.load_npz("loss.npz").todense() # Load loss matrix from data file - loss_dat = np.loadtxt("loss.dat", delimiter=',') + loss_dat = np.loadtxt("loss.dat", delimiter=",") # Go through each element of loss_dat and compare to loss_np for elem in loss_dat: - assert(np.isclose(loss_np[int(elem[0]), int(elem[1])], elem[2])) + assert np.isclose(loss_np[int(elem[0]), int(elem[1])], elem[2]) # Load production matrix from numpy output file - prod_np = scipy.sparse.load_npz('prod.npz').todense() + prod_np = scipy.sparse.load_npz("prod.npz").todense() # Load production matrix from data file - prod_dat = np.loadtxt("prod.dat", delimiter=',') + prod_dat = np.loadtxt("prod.dat", delimiter=",") # Go through each element of prod_dat and compare to prod_np for elem in prod_dat: - assert(np.isclose(prod_np[int(elem[0]), int(elem[1])], elem[2])) + assert np.isclose(prod_np[int(elem[0]), int(elem[1])], elem[2]) # Load flux vector from numpy output file - flux_np = np.load('fluxvec.npy') + flux_np = np.load("fluxvec.npy") # Load flux from data file flux_dat = np.loadtxt("fluxvec.dat") # Compare flux from numpy file, .dat file, and from simulation - assert(np.all(np.isclose(flux_np, cmfd_run._phi))) - assert(np.all(np.isclose(flux_np, flux_dat))) + assert np.all(np.isclose(flux_np, cmfd_run._phi)) + assert np.all(np.isclose(flux_np, flux_dat)) def test_cmfd_feed(): @@ -132,23 +132,24 @@ def test_cmfd_feed(): cmfd_run.mesh = cmfd_mesh cmfd_run.tally_begin = 5 cmfd_run.solver_begin = 5 - cmfd_run.display = {'dominance': True} + cmfd_run.display = {"dominance": True} cmfd_run.feedback = True - cmfd_run.gauss_seidel_tolerance = [1.e-15, 1.e-20] + cmfd_run.gauss_seidel_tolerance = [1.0e-15, 1.0e-20] cmfd_run.run() # Initialize and run CMFD test harness - harness = CMFDTestHarness('statepoint.20.h5', cmfd_run) + 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' + cmfd_mesh.mesh_type = "rectilinear" x_grid = np.linspace(-10, 10, 11) - y_grid = [-1., 1.] - z_grid = [-1., 1.] + y_grid = [-1.0, 1.0] + z_grid = [-1.0, 1.0] cmfd_mesh.grid = [x_grid, y_grid, z_grid] cmfd_mesh.albedo = (0.0, 0.0, 1.0, 1.0, 1.0, 1.0) @@ -157,15 +158,16 @@ def test_cmfd_feed_rectlin(): cmfd_run.mesh = cmfd_mesh cmfd_run.tally_begin = 5 cmfd_run.solver_begin = 5 - cmfd_run.display = {'dominance': True} + cmfd_run.display = {"dominance": True} cmfd_run.feedback = True - cmfd_run.gauss_seidel_tolerance = [1.e-15, 1.e-20] + cmfd_run.gauss_seidel_tolerance = [1.0e-15, 1.0e-20] cmfd_run.run() # Initialize and run CMFD test harness - harness = CMFDTestHarness('statepoint.20.h5', cmfd_run) + 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 @@ -180,12 +182,12 @@ def test_cmfd_multithread(): cmfd_run.mesh = cmfd_mesh cmfd_run.tally_begin = 5 cmfd_run.solver_begin = 5 - cmfd_run.display = {'dominance': True} + cmfd_run.display = {"dominance": True} cmfd_run.feedback = True - cmfd_run.gauss_seidel_tolerance = [1.e-15, 1.e-20] + cmfd_run.gauss_seidel_tolerance = [1.0e-15, 1.0e-20] cmfd_run.use_all_threads = True cmfd_run.run() # Initialize and run CMFD test harness - harness = CMFDTestHarness('statepoint.20.h5', cmfd_run) + harness = CMFDTestHarness("statepoint.20.h5", cmfd_run) harness.main() diff --git a/tests/regression_tests/cmfd_feed_2g/test.py b/tests/regression_tests/cmfd_feed_2g/test.py index d3af8998b60..dee61bc15db 100644 --- a/tests/regression_tests/cmfd_feed_2g/test.py +++ b/tests/regression_tests/cmfd_feed_2g/test.py @@ -18,12 +18,12 @@ def test_cmfd_feed_2g(): cmfd_run.mesh = cmfd_mesh cmfd_run.tally_begin = 5 cmfd_run.solver_begin = 5 - cmfd_run.display = {'dominance': True} + cmfd_run.display = {"dominance": True} cmfd_run.feedback = True cmfd_run.downscatter = True - cmfd_run.gauss_seidel_tolerance = [1.e-15, 1.e-20] + cmfd_run.gauss_seidel_tolerance = [1.0e-15, 1.0e-20] cmfd_run.run() # Initialize and run CMFD test harness - harness = CMFDTestHarness('statepoint.20.h5', cmfd_run) + harness = CMFDTestHarness("statepoint.20.h5", cmfd_run) harness.main() diff --git a/tests/regression_tests/cmfd_feed_expanding_window/test.py b/tests/regression_tests/cmfd_feed_expanding_window/test.py index 964d4f2253a..317509b67b6 100644 --- a/tests/regression_tests/cmfd_feed_expanding_window/test.py +++ b/tests/regression_tests/cmfd_feed_expanding_window/test.py @@ -17,10 +17,10 @@ def test_cmfd_feed_rolling_window(): cmfd_run.tally_begin = 5 cmfd_run.solver_begin = 10 cmfd_run.feedback = True - cmfd_run.gauss_seidel_tolerance = [1.e-15, 1.e-20] - cmfd_run.window_type = 'expanding' + cmfd_run.gauss_seidel_tolerance = [1.0e-15, 1.0e-20] + cmfd_run.window_type = "expanding" cmfd_run.run() # Initialize and run CMFD test harness - harness = CMFDTestHarness('statepoint.20.h5', cmfd_run) + harness = CMFDTestHarness("statepoint.20.h5", cmfd_run) harness.main() diff --git a/tests/regression_tests/cmfd_feed_ng/test.py b/tests/regression_tests/cmfd_feed_ng/test.py index a2a522e9c4d..1b3341f5ed8 100644 --- a/tests/regression_tests/cmfd_feed_ng/test.py +++ b/tests/regression_tests/cmfd_feed_ng/test.py @@ -19,12 +19,12 @@ def test_cmfd_feed_ng(): cmfd_run.reset = [5] cmfd_run.tally_begin = 10 cmfd_run.solver_begin = 10 - cmfd_run.display = {'dominance': True} + cmfd_run.display = {"dominance": True} cmfd_run.feedback = True cmfd_run.downscatter = True - cmfd_run.gauss_seidel_tolerance = [1.e-15, 1.e-20] + cmfd_run.gauss_seidel_tolerance = [1.0e-15, 1.0e-20] cmfd_run.run() # Initialize and run CMFD test harness - harness = CMFDTestHarness('statepoint.20.h5', cmfd_run) + harness = CMFDTestHarness("statepoint.20.h5", cmfd_run) harness.main() diff --git a/tests/regression_tests/cmfd_feed_rectlin/test.py b/tests/regression_tests/cmfd_feed_rectlin/test.py index 8739e800d2e..25213fa7356 100644 --- a/tests/regression_tests/cmfd_feed_rectlin/test.py +++ b/tests/regression_tests/cmfd_feed_rectlin/test.py @@ -8,10 +8,26 @@ 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.mesh_type = "rectilinear" + x_grid = [ + -10.0, + -9.0, + -7.0, + -6.0, + -4.0, + -3.0, + -1.0, + 0.0, + 1.0, + 3.0, + 4.0, + 6.0, + 7.0, + 9.0, + 10.0, + ] + y_grid = [-1.0, 1.0] + z_grid = [-1.0, 1.0] cmfd_mesh.grid = [x_grid, y_grid, z_grid] cmfd_mesh.albedo = (0.0, 0.0, 1.0, 1.0, 1.0, 1.0) @@ -20,11 +36,11 @@ def test_cmfd_feed_rectlin(): cmfd_run.mesh = cmfd_mesh cmfd_run.tally_begin = 5 cmfd_run.solver_begin = 5 - cmfd_run.display = {'dominance': True} + cmfd_run.display = {"dominance": True} cmfd_run.feedback = True - cmfd_run.gauss_seidel_tolerance = [1.e-15, 1.e-20] + cmfd_run.gauss_seidel_tolerance = [1.0e-15, 1.0e-20] cmfd_run.run() # Initialize and run CMFD test harness - harness = CMFDTestHarness('statepoint.20.h5', cmfd_run) + harness = CMFDTestHarness("statepoint.20.h5", cmfd_run) harness.main() diff --git a/tests/regression_tests/cmfd_feed_ref_d/test.py b/tests/regression_tests/cmfd_feed_ref_d/test.py index 120d94b6b45..b5e7c55a918 100644 --- a/tests/regression_tests/cmfd_feed_ref_d/test.py +++ b/tests/regression_tests/cmfd_feed_ref_d/test.py @@ -17,11 +17,11 @@ def test_cmfd_feed_rolling_window(): cmfd_run.tally_begin = 5 cmfd_run.solver_begin = 10 cmfd_run.feedback = True - cmfd_run.gauss_seidel_tolerance = [1.e-15, 1.e-20] - cmfd_run.window_type = 'expanding' + cmfd_run.gauss_seidel_tolerance = [1.0e-15, 1.0e-20] + cmfd_run.window_type = "expanding" cmfd_run.ref_d = [0.542] cmfd_run.run() # Initialize and run CMFD test harness - harness = CMFDTestHarness('statepoint.20.h5', cmfd_run) + harness = CMFDTestHarness("statepoint.20.h5", cmfd_run) harness.main() diff --git a/tests/regression_tests/cmfd_feed_rolling_window/test.py b/tests/regression_tests/cmfd_feed_rolling_window/test.py index 2c7b7f242cb..aa5bf96b431 100644 --- a/tests/regression_tests/cmfd_feed_rolling_window/test.py +++ b/tests/regression_tests/cmfd_feed_rolling_window/test.py @@ -17,11 +17,11 @@ def test_cmfd_feed_rolling_window(): cmfd_run.tally_begin = 5 cmfd_run.solver_begin = 10 cmfd_run.feedback = True - cmfd_run.gauss_seidel_tolerance = [1.e-15, 1.e-20] - cmfd_run.window_type = 'rolling' + cmfd_run.gauss_seidel_tolerance = [1.0e-15, 1.0e-20] + cmfd_run.window_type = "rolling" cmfd_run.window_size = 5 cmfd_run.run() # Initialize and run CMFD test harness - harness = CMFDTestHarness('statepoint.20.h5', cmfd_run) + harness = CMFDTestHarness("statepoint.20.h5", cmfd_run) harness.main() diff --git a/tests/regression_tests/cmfd_nofeed/test.py b/tests/regression_tests/cmfd_nofeed/test.py index 7ab72f86ad7..2edb01640fe 100644 --- a/tests/regression_tests/cmfd_nofeed/test.py +++ b/tests/regression_tests/cmfd_nofeed/test.py @@ -16,11 +16,11 @@ def test_cmfd_nofeed(): cmfd_run = cmfd.CMFDRun() cmfd_run.mesh = cmfd_mesh cmfd_run.solver_begin = 5 - cmfd_run.display = {'dominance': True} + cmfd_run.display = {"dominance": True} cmfd_run.feedback = False - cmfd_run.gauss_seidel_tolerance = [1.e-15, 1.e-20] + cmfd_run.gauss_seidel_tolerance = [1.0e-15, 1.0e-20] cmfd_run.run() # Initialize and run CMFD test harness - harness = CMFDTestHarness('statepoint.20.h5', cmfd_run) + harness = CMFDTestHarness("statepoint.20.h5", cmfd_run) harness.main() diff --git a/tests/regression_tests/cmfd_restart/test.py b/tests/regression_tests/cmfd_restart/test.py index 8f8410a7aa1..76451dc046d 100644 --- a/tests/regression_tests/cmfd_restart/test.py +++ b/tests/regression_tests/cmfd_restart/test.py @@ -26,7 +26,7 @@ def execute_test(self): statepoint = glob.glob(os.path.join(os.getcwd(), self._restart_sp)) assert len(statepoint) == 1 statepoint = statepoint[0] - self._cmfd_restart_run.run(args=['-r', statepoint]) + self._cmfd_restart_run.run(args=["-r", statepoint]) # Compare results from second CMFD run self._test_output_created() @@ -55,7 +55,7 @@ def test_cmfd_restart(): cmfd_run.tally_begin = 5 cmfd_run.solver_begin = 5 cmfd_run.feedback = True - cmfd_run.gauss_seidel_tolerance = [1.e-15, 1.e-20] + cmfd_run.gauss_seidel_tolerance = [1.0e-15, 1.0e-20] cmfd_run.run() # Initialize second CMFDRun object which will be run from restart file @@ -64,9 +64,10 @@ def test_cmfd_restart(): cmfd_run2.tally_begin = 5 cmfd_run2.solver_begin = 5 cmfd_run2.feedback = True - cmfd_run2.gauss_seidel_tolerance = [1.e-15, 1.e-20] + cmfd_run2.gauss_seidel_tolerance = [1.0e-15, 1.0e-20] # Initialize and run CMFD restart test harness - harness = CMFDRestartTestHarness('statepoint.20.h5', 'statepoint.15.h5', - cmfd_run, cmfd_run2) + harness = CMFDRestartTestHarness( + "statepoint.20.h5", "statepoint.15.h5", cmfd_run, cmfd_run2 + ) harness.main() diff --git a/tests/regression_tests/complex_cell/test.py b/tests/regression_tests/complex_cell/test.py index 77cbd6cb7d8..b40ac61576a 100755 --- a/tests/regression_tests/complex_cell/test.py +++ b/tests/regression_tests/complex_cell/test.py @@ -2,5 +2,5 @@ def test_complex_cell(): - harness = TestHarness('statepoint.10.h5') + harness = TestHarness("statepoint.10.h5") harness.main() diff --git a/tests/regression_tests/confidence_intervals/test.py b/tests/regression_tests/confidence_intervals/test.py index 32274182801..8d86cdbc20a 100755 --- a/tests/regression_tests/confidence_intervals/test.py +++ b/tests/regression_tests/confidence_intervals/test.py @@ -2,5 +2,5 @@ def test_confidence_intervals(): - harness = TestHarness('statepoint.10.h5') + harness = TestHarness("statepoint.10.h5") harness.main() diff --git a/tests/regression_tests/conftest.py b/tests/regression_tests/conftest.py index 1cdf414e767..f44aaa3ce08 100644 --- a/tests/regression_tests/conftest.py +++ b/tests/regression_tests/conftest.py @@ -4,13 +4,14 @@ import pytest -@pytest.fixture(scope='module', autouse=True) +@pytest.fixture(scope="module", autouse=True) def numpy_version_requirement(): - assert parse(np.__version__) >= parse("1.14"), \ - "Regression tests require NumPy 1.14 or greater" + assert parse(np.__version__) >= parse( + "1.14" + ), "Regression tests require NumPy 1.14 or greater" -@pytest.fixture(scope='module', autouse=True) +@pytest.fixture(scope="module", autouse=True) def setup_regression_test(request): # Reset autogenerated IDs assigned to OpenMC objects openmc.reset_auto_ids() diff --git a/tests/regression_tests/cpp_driver/test.py b/tests/regression_tests/cpp_driver/test.py index b80e82ee0e1..e0db8b4eecf 100644 --- a/tests/regression_tests/cpp_driver/test.py +++ b/tests/regression_tests/cpp_driver/test.py @@ -17,22 +17,28 @@ def cpp_driver(request): """Compile the external source""" # Get build directory and write CMakeLists.txt file - openmc_dir = Path(str(request.config.rootdir)) / 'build' - with open('CMakeLists.txt', 'w') as f: - f.write(textwrap.dedent(""" + openmc_dir = Path(str(request.config.rootdir)) / "build" + with open("CMakeLists.txt", "w") as f: + f.write( + textwrap.dedent( + """ cmake_minimum_required(VERSION 3.10 FATAL_ERROR) project(openmc_cpp_driver CXX) add_executable(cpp_driver driver.cpp) find_package(OpenMC REQUIRED HINTS {}) target_link_libraries(cpp_driver OpenMC::libopenmc) - """.format(openmc_dir))) + """.format( + openmc_dir + ) + ) + ) # Create temporary build directory and change to there - local_builddir = Path('build') + local_builddir = Path("build") local_builddir.mkdir(exist_ok=True) os.chdir(str(local_builddir)) - if config['mpi']: + if config["mpi"]: mpi_arg = "On" else: mpi_arg = "Off" @@ -40,16 +46,18 @@ def cpp_driver(request): try: print("Building driver") # Run cmake/make to build the shared libary - subprocess.run(['cmake', os.path.pardir, f'-DOPENMC_USE_MPI={mpi_arg}'], check=True) - subprocess.run(['make'], check=True) + subprocess.run( + ["cmake", os.path.pardir, f"-DOPENMC_USE_MPI={mpi_arg}"], check=True + ) + subprocess.run(["make"], check=True) os.chdir(os.path.pardir) yield "./build/cpp_driver" finally: # Remove local build directory when test is complete - shutil.rmtree(request.node.path.parent / 'build') - os.remove(request.node.path.parent / 'CMakeLists.txt') + shutil.rmtree(request.node.path.parent / "build") + os.remove(request.node.path.parent / "CMakeLists.txt") @pytest.fixture @@ -58,17 +66,17 @@ def model(): # materials u235 = openmc.Material(name="fuel") - u235.add_nuclide('U235', 1.0, 'ao') - u235.set_density('g/cc', 11) + u235.add_nuclide("U235", 1.0, "ao") + u235.set_density("g/cc", 11) - zirc = openmc.Material(name='cladding') - zirc.add_nuclide('Zr90', 1.0) - zirc.set_density('g/cc', 6.44) + zirc = openmc.Material(name="cladding") + zirc.add_nuclide("Zr90", 1.0) + zirc.set_density("g/cc", 6.44) water = openmc.Material(name="water") - water.add_nuclide('H1', 2.0, 'ao') - water.add_nuclide('O16', 1.0, 'ao') - water.set_density('g/cc', 1.0) + water.add_nuclide("H1", 2.0, "ao") + water.add_nuclide("O16", 1.0, "ao") + water.set_density("g/cc", 1.0) mats = openmc.Materials([u235, zirc, water]) model.materials = mats @@ -92,8 +100,7 @@ def model(): lattice.pitch = (4.0, 4.0) lattice.lower_left = (-4.0, -4.0) lattice.universes = [[extra_univ, extra_univ], [extra_univ, extra_univ]] - lattice_prism = openmc.model.RectangularPrism( - 8.0, 8.0, boundary_type='reflective') + lattice_prism = openmc.model.RectangularPrism(8.0, 8.0, boundary_type="reflective") lattice_cell = openmc.Cell(fill=lattice, region=-lattice_prism) model.geometry = openmc.Geometry([lattice_cell]) @@ -112,20 +119,21 @@ def __init__(self, executable, statepoint_name, model=None): self.executable = executable def _run_openmc(self): - if config['mpi']: - mpi_args = [config['mpiexec'], '-n', config['mpi_np']] - openmc.run(openmc_exec=self.executable, - mpi_args=mpi_args, - event_based=config['event']) + if config["mpi"]: + mpi_args = [config["mpiexec"], "-n", config["mpi_np"]] + openmc.run( + openmc_exec=self.executable, + mpi_args=mpi_args, + event_based=config["event"], + ) else: - openmc.run(openmc_exec=self.executable, - event_based=config['event']) + openmc.run(openmc_exec=self.executable, event_based=config["event"]) def _compare_results(self): super()._compare_results() # load the summary file - summary = openmc.Summary('summary.h5') + summary = openmc.Summary("summary.h5") # get the summary cells cells = summary.geometry.get_all_cells() @@ -140,5 +148,5 @@ def _compare_results(self): def test_cpp_driver(cpp_driver, model): - harness = ExternalDriverTestHarness(cpp_driver, 'statepoint.10.h5', model) + harness = ExternalDriverTestHarness(cpp_driver, "statepoint.10.h5", model) harness.main() diff --git a/tests/regression_tests/create_fission_neutrons/test.py b/tests/regression_tests/create_fission_neutrons/test.py index 0ca4a48b292..5b7dc519cae 100755 --- a/tests/regression_tests/create_fission_neutrons/test.py +++ b/tests/regression_tests/create_fission_neutrons/test.py @@ -7,10 +7,10 @@ class CreateFissionNeutronsTestHarness(PyAPITestHarness): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Material is composed of H-1 and U-235 - mat = openmc.Material(material_id=1, name='mat') - mat.set_density('atom/b-cm', 0.069335) - mat.add_nuclide('H1', 40.0) - mat.add_nuclide('U235', 1.0) + mat = openmc.Material(material_id=1, name="mat") + mat.set_density("atom/b-cm", 0.069335) + mat.add_nuclide("H1", 40.0) + mat.add_nuclide("U235", 1.0) self._model.materials = openmc.Materials([mat]) # Cell is box with reflective boundary @@ -21,31 +21,32 @@ def __init__(self, *args, **kwargs): z1 = openmc.ZPlane(surface_id=5, z0=-1) z2 = openmc.ZPlane(surface_id=6, z0=1) for surface in [x1, x2, y1, y2, z1, z2]: - surface.boundary_type = 'reflective' - box = openmc.Cell(cell_id=1, name='box') + surface.boundary_type = "reflective" + box = openmc.Cell(cell_id=1, name="box") box.region = +x1 & -x2 & +y1 & -y2 & +z1 & -z2 box.fill = mat - root = openmc.Universe(universe_id=0, name='root universe') + root = openmc.Universe(universe_id=0, name="root universe") root.add_cell(box) self._model.geometry = openmc.Geometry(root) # Set the running parameters settings_file = openmc.Settings() - settings_file.run_mode = 'fixed source' + settings_file.run_mode = "fixed source" settings_file.batches = 10 settings_file.particles = 100 settings_file.create_fission_neutrons = False bounds = [-1, -1, -1, 1, 1, 1] uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:]) watt_dist = openmc.stats.Watt() - settings_file.source = openmc.IndependentSource(space=uniform_dist, - energy=watt_dist) + settings_file.source = openmc.IndependentSource( + space=uniform_dist, energy=watt_dist + ) self._model.settings = settings_file # Create tallies tallies = openmc.Tallies() tally = openmc.Tally(1) - tally.scores = ['flux'] + tally.scores = ["flux"] tallies.append(tally) self._model.tallies = tallies @@ -55,16 +56,15 @@ def _get_results(self): sp = openmc.StatePoint(self._sp_name) # Write out tally data. - outstr = '' + outstr = "" t = sp.get_tally() - outstr += 'tally {}:\n'.format(t.id) - outstr += 'sum = {:12.6E}\n'.format(t.sum[0, 0, 0]) - outstr += 'sum_sq = {:12.6E}\n'.format(t.sum_sq[0, 0, 0]) + outstr += "tally {}:\n".format(t.id) + outstr += "sum = {:12.6E}\n".format(t.sum[0, 0, 0]) + outstr += "sum_sq = {:12.6E}\n".format(t.sum_sq[0, 0, 0]) return outstr def test_create_fission_neutrons(): - harness = CreateFissionNeutronsTestHarness('statepoint.10.h5', - model=openmc.Model()) + harness = CreateFissionNeutronsTestHarness("statepoint.10.h5", model=openmc.Model()) harness.main() diff --git a/tests/regression_tests/dagmc/external/test.py b/tests/regression_tests/dagmc/external/test.py index 57bc9ea7fd6..f8eaf5d9714 100644 --- a/tests/regression_tests/dagmc/external/test.py +++ b/tests/regression_tests/dagmc/external/test.py @@ -12,19 +12,22 @@ from tests.testing_harness import PyAPITestHarness pytestmark = pytest.mark.skipif( - not openmc.lib._dagmc_enabled(), - reason="DAGMC is not enabled.") + not openmc.lib._dagmc_enabled(), reason="DAGMC is not enabled." +) # Test that an external DAGMC instance can be passed in through the C API + @pytest.fixture def cpp_driver(request): """Compile the external source""" # Get build directory and write CMakeLists.txt file - openmc_dir = Path(str(request.config.rootdir)) / 'build' - with open('CMakeLists.txt', 'w') as f: - f.write(textwrap.dedent(""" + openmc_dir = Path(str(request.config.rootdir)) / "build" + with open("CMakeLists.txt", "w") as f: + f.write( + textwrap.dedent( + """ cmake_minimum_required(VERSION 3.10 FATAL_ERROR) project(openmc_cpp_driver CXX) add_executable(main main.cpp) @@ -33,27 +36,34 @@ def cpp_driver(request): target_compile_features(main PUBLIC cxx_std_14) set(CMAKE_CXX_FLAGS "-pedantic-errors") add_compile_definitions(DAGMC=1) - """.format(openmc_dir))) + """.format( + openmc_dir + ) + ) + ) # Create temporary build directory and change to there - local_builddir = Path('build') + local_builddir = Path("build") local_builddir.mkdir(exist_ok=True) os.chdir(local_builddir) - mpi_arg = "On" if config['mpi'] else "Off" + mpi_arg = "On" if config["mpi"] else "Off" try: # Run cmake/make to build the shared libary - subprocess.run(['cmake', os.path.pardir, f'-DOPENMC_USE_MPI={mpi_arg}'], check=True) - subprocess.run(['make'], check=True) + subprocess.run( + ["cmake", os.path.pardir, f"-DOPENMC_USE_MPI={mpi_arg}"], check=True + ) + subprocess.run(["make"], check=True) os.chdir(os.path.pardir) yield "./build/main" finally: # Remove local build directory when test is complete - shutil.rmtree('build') - os.remove('CMakeLists.txt') + shutil.rmtree("build") + os.remove("CMakeLists.txt") + @pytest.fixture def model(): @@ -63,11 +73,10 @@ def model(): model.settings.batches = 5 model.settings.inactive = 0 model.settings.particles = 100 - source_box = openmc.stats.Box([-4, -4, -4], - [ 4, 4, 4]) + source_box = openmc.stats.Box([-4, -4, -4], [4, 4, 4]) source = openmc.IndependentSource(space=source_box) model.settings.source = source - model.settings.temperature['default'] = 293 + model.settings.temperature["default"] = 293 # Geometry dag_univ = openmc.DAGMCUniverse("dagmc.h5m") @@ -75,26 +84,27 @@ def model(): # Tallies tally = openmc.Tally() - tally.scores = ['total'] + tally.scores = ["total"] tally.filters = [openmc.CellFilter(1)] model.tallies = [tally] # Materials u235 = openmc.Material(name="no-void fuel") - u235.add_nuclide('U235', 1.0, 'ao') - u235.set_density('g/cc', 11) + u235.add_nuclide("U235", 1.0, "ao") + u235.set_density("g/cc", 11) u235.id = 40 water = openmc.Material(name="water") - water.add_nuclide('H1', 2.0, 'ao') - water.add_nuclide('O16', 1.0, 'ao') - water.set_density('g/cc', 1.0) - water.add_s_alpha_beta('c_H_in_H2O') + water.add_nuclide("H1", 2.0, "ao") + water.add_nuclide("O16", 1.0, "ao") + water.set_density("g/cc", 1.0) + water.add_s_alpha_beta("c_H_in_H2O") water.id = 41 mats = openmc.Materials([u235, water]) model.materials = mats return model + class ExternalDAGMCTest(PyAPITestHarness): def __init__(self, executable, statepoint_name, model): super().__init__(statepoint_name, model) @@ -109,20 +119,22 @@ def _run_openmc(self): executable compiled from main.cpp. This future-proofs the test - we only care that the two routes are equivalent. """ - if config['update']: + if config["update"]: # Generate the results file with internal python API - openmc.run(openmc_exec=config['exe'], event_based=config['event']) - elif config['mpi']: - mpi_args = [config['mpiexec'], '-n', config['mpi_np']] + openmc.run(openmc_exec=config["exe"], event_based=config["event"]) + elif config["mpi"]: + mpi_args = [config["mpiexec"], "-n", config["mpi_np"]] # Run main cpp executable with MPI - openmc.run(openmc_exec=self.executable, - mpi_args=mpi_args, - event_based=config['event']) + openmc.run( + openmc_exec=self.executable, + mpi_args=mpi_args, + event_based=config["event"], + ) else: # Run main cpp executable - openmc.run(openmc_exec=self.executable, - event_based=config['event']) + openmc.run(openmc_exec=self.executable, event_based=config["event"]) + def test_external_dagmc(cpp_driver, model): - harness = ExternalDAGMCTest(cpp_driver, 'statepoint.5.h5', model) + harness = ExternalDAGMCTest(cpp_driver, "statepoint.5.h5", model) harness.main() diff --git a/tests/regression_tests/dagmc/legacy/test.py b/tests/regression_tests/dagmc/legacy/test.py index b6b1e376b00..76483f6c14f 100644 --- a/tests/regression_tests/dagmc/legacy/test.py +++ b/tests/regression_tests/dagmc/legacy/test.py @@ -10,8 +10,9 @@ from tests.testing_harness import PyAPITestHarness, config pytestmark = pytest.mark.skipif( - not openmc.lib._dagmc_enabled(), - reason="DAGMC CAD geometry is not enabled.") + not openmc.lib._dagmc_enabled(), reason="DAGMC CAD geometry is not enabled." +) + @pytest.fixture def model(): @@ -24,8 +25,7 @@ def model(): model.settings.inactive = 0 model.settings.particles = 100 - source_box = openmc.stats.Box([-4, -4, -4], - [ 4, 4, 4]) + source_box = openmc.stats.Box([-4, -4, -4], [4, 4, 4]) source = openmc.IndependentSource(space=source_box) model.settings.source = source @@ -36,21 +36,21 @@ def model(): # tally tally = openmc.Tally() - tally.scores = ['total'] + tally.scores = ["total"] tally.filters = [openmc.CellFilter(1)] model.tallies = [tally] # materials u235 = openmc.Material(name="no-void fuel") - u235.add_nuclide('U235', 1.0, 'ao') - u235.set_density('g/cc', 11) + u235.add_nuclide("U235", 1.0, "ao") + u235.set_density("g/cc", 11) u235.id = 40 water = openmc.Material(name="water") - water.add_nuclide('H1', 2.0, 'ao') - water.add_nuclide('O16', 1.0, 'ao') - water.set_density('g/cc', 1.0) - water.add_s_alpha_beta('c_H_in_H2O') + water.add_nuclide("H1", 2.0, "ao") + water.add_nuclide("O16", 1.0, "ao") + water.set_density("g/cc", 1.0) + water.add_s_alpha_beta("c_H_in_H2O") water.id = 41 mats = openmc.Materials([u235, water]) @@ -81,27 +81,27 @@ def test_surf_source(model): # create a surface source read on this model to ensure # particles are being generated correctly n = 100 - model.settings.surf_source_write = {'surface_ids': [1], 'max_particles': n} + model.settings.surf_source_write = {"surface_ids": [1], "max_particles": n} # If running in MPI mode, setup proper keyword arguments for run() - kwargs = {'openmc_exec': config['exe']} - if config['mpi']: - kwargs['mpi_args'] = [config['mpiexec'], '-n', config['mpi_np']] + kwargs = {"openmc_exec": config["exe"]} + if config["mpi"]: + kwargs["mpi_args"] = [config["mpiexec"], "-n", config["mpi_np"]] model.run(**kwargs) - with h5py.File('surface_source.h5') as fh: - assert fh.attrs['filetype'] == b'source' - arr = fh['source_bank'][...] - expected_size = n * int(config['mpi_np']) if config['mpi'] else n + with h5py.File("surface_source.h5") as fh: + assert fh.attrs["filetype"] == b"source" + arr = fh["source_bank"][...] + expected_size = n * int(config["mpi_np"]) if config["mpi"] else n assert arr.size == expected_size # check that all particles are on surface 1 (radius = 7) - xs = arr[:]['r']['x'] - ys = arr[:]['r']['y'] + xs = arr[:]["r"]["x"] + ys = arr[:]["r"]["y"] rad = np.sqrt(xs**2 + ys**2) assert np.allclose(rad, 7.0) def test_dagmc(model): - harness = PyAPITestHarness('statepoint.5.h5', model) - harness.main() \ No newline at end of file + harness = PyAPITestHarness("statepoint.5.h5", model) + harness.main() diff --git a/tests/regression_tests/dagmc/refl/test.py b/tests/regression_tests/dagmc/refl/test.py index a13acc0256a..f5e35199adb 100644 --- a/tests/regression_tests/dagmc/refl/test.py +++ b/tests/regression_tests/dagmc/refl/test.py @@ -6,8 +6,9 @@ from tests.testing_harness import PyAPITestHarness pytestmark = pytest.mark.skipif( - not openmc.lib._uwuw_enabled(), - reason="UWUW is not enabled.") + not openmc.lib._uwuw_enabled(), reason="UWUW is not enabled." +) + class UWUWTest(PyAPITestHarness): def __init__(self, *args, **kwargs): @@ -18,8 +19,7 @@ def __init__(self, *args, **kwargs): self._model.settings.inactive = 0 self._model.settings.particles = 100 - source = openmc.IndependentSource(space=Box([-4, -4, -4], - [ 4, 4, 4])) + source = openmc.IndependentSource(space=Box([-4, -4, -4], [4, 4, 4])) self._model.settings.source = source # geometry @@ -28,11 +28,11 @@ def __init__(self, *args, **kwargs): # tally tally = openmc.Tally() - tally.scores = ['total'] + tally.scores = ["total"] tally.filters = [openmc.CellFilter(2)] self._model.tallies = [tally] def test_refl(): - harness = UWUWTest('statepoint.5.h5', model=openmc.Model()) + harness = UWUWTest("statepoint.5.h5", model=openmc.Model()) harness.main() diff --git a/tests/regression_tests/dagmc/universes/test.py b/tests/regression_tests/dagmc/universes/test.py index 057a7b0d477..974104bd1a3 100644 --- a/tests/regression_tests/dagmc/universes/test.py +++ b/tests/regression_tests/dagmc/universes/test.py @@ -7,8 +7,8 @@ from tests.testing_harness import PyAPITestHarness pytestmark = pytest.mark.skipif( - not openmc.lib._dagmc_enabled(), - reason="DAGMC CAD geometry is not enabled.") + not openmc.lib._dagmc_enabled(), reason="DAGMC CAD geometry is not enabled." +) class DAGMCUniverseTest(PyAPITestHarness): @@ -16,37 +16,39 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) ### MATERIALS ### - fuel = openmc.Material(name='no-void fuel') - fuel.set_density('g/cc', 10.29769) - fuel.add_nuclide('U234', 0.93120485) - fuel.add_nuclide('U235', 0.00055815) - fuel.add_nuclide('U238', 0.022408) - fuel.add_nuclide('O16', 0.045829) - - cladding = openmc.Material(name='clad') - cladding.set_density('g/cc', 6.55) - cladding.add_nuclide('Zr90', 0.021827) - cladding.add_nuclide('Zr91', 0.00476) - cladding.add_nuclide('Zr92', 0.0072758) - cladding.add_nuclide('Zr94', 0.0073734) - cladding.add_nuclide('Zr96', 0.0011879) - - water = openmc.Material(name='water') - water.set_density('g/cc', 0.740582) - water.add_nuclide('H1', 0.049457) - water.add_nuclide('O16', 0.024672) - water.add_nuclide('B10', 8.0042e-06) - water.add_nuclide('B11', 3.2218e-05) - water.add_s_alpha_beta('c_H_in_H2O') + fuel = openmc.Material(name="no-void fuel") + fuel.set_density("g/cc", 10.29769) + fuel.add_nuclide("U234", 0.93120485) + fuel.add_nuclide("U235", 0.00055815) + fuel.add_nuclide("U238", 0.022408) + fuel.add_nuclide("O16", 0.045829) + + cladding = openmc.Material(name="clad") + cladding.set_density("g/cc", 6.55) + cladding.add_nuclide("Zr90", 0.021827) + cladding.add_nuclide("Zr91", 0.00476) + cladding.add_nuclide("Zr92", 0.0072758) + cladding.add_nuclide("Zr94", 0.0073734) + cladding.add_nuclide("Zr96", 0.0011879) + + water = openmc.Material(name="water") + water.set_density("g/cc", 0.740582) + water.add_nuclide("H1", 0.049457) + water.add_nuclide("O16", 0.024672) + water.add_nuclide("B10", 8.0042e-06) + water.add_nuclide("B11", 3.2218e-05) + water.add_s_alpha_beta("c_H_in_H2O") self._model.materials = openmc.Materials([fuel, cladding, water]) ### GEOMETRY ### # create the DAGMC universe - pincell_univ = openmc.DAGMCUniverse(filename='dagmc.h5m', auto_geom_ids=True) + pincell_univ = openmc.DAGMCUniverse(filename="dagmc.h5m", auto_geom_ids=True) # creates another DAGMC universe, this time with within a bounded cell - bound_pincell_universe = openmc.DAGMCUniverse(filename='dagmc.h5m').bounded_universe() + bound_pincell_universe = openmc.DAGMCUniverse( + filename="dagmc.h5m" + ).bounded_universe() # uses the bound_dag_cell as the root argument to test the type checks in openmc.Geometry bound_pincell_geometry = openmc.Geometry(root=bound_pincell_universe) # assigns the bound_dag_geometry to the model to test the type checks in model.Geometry setter @@ -59,13 +61,13 @@ def __init__(self, *args, **kwargs): lattice.universes = [[pincell_univ] * 2] * 2 lattice.lower_left = -pitch - left = openmc.XPlane(x0=-pitch[0], name='left', boundary_type='reflective') - right = openmc.XPlane(x0=pitch[0], name='right', boundary_type='reflective') - front = openmc.YPlane(y0=-pitch[1], name='front', boundary_type='reflective') - back = openmc.YPlane(y0=pitch[1], name='back', boundary_type='reflective') + left = openmc.XPlane(x0=-pitch[0], name="left", boundary_type="reflective") + right = openmc.XPlane(x0=pitch[0], name="right", boundary_type="reflective") + front = openmc.YPlane(y0=-pitch[1], name="front", boundary_type="reflective") + back = openmc.YPlane(y0=pitch[1], name="back", boundary_type="reflective") # clip the DAGMC geometry at +/- 10 cm w/ CSG planes - bottom = openmc.ZPlane(z0=-10.0, name='bottom', boundary_type='reflective') - top = openmc.ZPlane(z0=10.0, name='top', boundary_type='reflective') + bottom = openmc.ZPlane(z0=-10.0, name="bottom", boundary_type="reflective") + top = openmc.ZPlane(z0=10.0, name="top", boundary_type="reflective") bounding_region = +left & -right & +front & -back & +bottom & -top bounding_cell = openmc.Cell(fill=lattice, region=bounding_region) @@ -73,19 +75,22 @@ def __init__(self, *args, **kwargs): self._model.geometry = openmc.Geometry([bounding_cell]) # add a cell instance tally - tally = openmc.Tally(name='cell instance tally') + tally = openmc.Tally(name="cell instance tally") # using scattering - cell_instance_filter = openmc.CellInstanceFilter(((4, 0), (4, 1), (4, 2), (4, 3), (4, 4))) + cell_instance_filter = openmc.CellInstanceFilter( + ((4, 0), (4, 1), (4, 2), (4, 3), (4, 4)) + ) tally.filters = [cell_instance_filter] - tally.scores = ['scatter'] + tally.scores = ["scatter"] self._model.tallies = [tally] # settings self._model.settings.particles = 100 self._model.settings.batches = 10 self._model.settings.inactive = 5 - self._model.settings.output = {'summary' : False} + self._model.settings.output = {"summary": False} + def test_univ(): - harness = DAGMCUniverseTest('statepoint.10.h5', model=openmc.Model()) + harness = DAGMCUniverseTest("statepoint.10.h5", model=openmc.Model()) harness.main() diff --git a/tests/regression_tests/dagmc/uwuw/test.py b/tests/regression_tests/dagmc/uwuw/test.py index bea464cfabc..18583be9e91 100644 --- a/tests/regression_tests/dagmc/uwuw/test.py +++ b/tests/regression_tests/dagmc/uwuw/test.py @@ -6,8 +6,9 @@ from tests.testing_harness import PyAPITestHarness pytestmark = pytest.mark.skipif( - not openmc.lib._uwuw_enabled(), - reason="UWUW is not enabled.") + not openmc.lib._uwuw_enabled(), reason="UWUW is not enabled." +) + class UWUWTest(PyAPITestHarness): def __init__(self, *args, **kwargs): @@ -18,8 +19,7 @@ def __init__(self, *args, **kwargs): self._model.settings.inactive = 0 self._model.settings.particles = 100 - source = openmc.IndependentSource(space=Box([-4, -4, -4], - [ 4, 4, 4])) + source = openmc.IndependentSource(space=Box([-4, -4, -4], [4, 4, 4])) self._model.settings.source = source # geometry @@ -28,11 +28,11 @@ def __init__(self, *args, **kwargs): # tally tally = openmc.Tally() - tally.scores = ['total'] + tally.scores = ["total"] tally.filters = [openmc.CellFilter(1)] self._model.tallies = [tally] def test_uwuw(): - harness = UWUWTest('statepoint.5.h5', model=openmc.Model()) + harness = UWUWTest("statepoint.5.h5", model=openmc.Model()) harness.main() diff --git a/tests/regression_tests/density/test.py b/tests/regression_tests/density/test.py index f3ae6b4144a..2418263e178 100644 --- a/tests/regression_tests/density/test.py +++ b/tests/regression_tests/density/test.py @@ -2,5 +2,5 @@ def test_density(): - harness = TestHarness('statepoint.10.h5') + harness = TestHarness("statepoint.10.h5") harness.main() diff --git a/tests/regression_tests/deplete_decay_only/test.py b/tests/regression_tests/deplete_decay_only/test.py index 4345b86b898..c389b08101a 100644 --- a/tests/regression_tests/deplete_decay_only/test.py +++ b/tests/regression_tests/deplete_decay_only/test.py @@ -47,50 +47,47 @@ def model(): return openmc.Model(geometry, materials, settings) + @pytest.fixture(scope="module") def micro_xs(): - micro_xs_file = Path(__file__).parents[2] / 'micro_xs_simple.csv' + micro_xs_file = Path(__file__).parents[2] / "micro_xs_simple.csv" return MicroXS.from_csv(micro_xs_file) @pytest.fixture(scope="module") def chain_file(): - return Path(__file__).parents[2] / 'chain_simple_decay.xml' + return Path(__file__).parents[2] / "chain_simple_decay.xml" @pytest.mark.parametrize("operator_type", ["coupled", "independent"]) def test_decay_only(run_in_tmpdir, operator_type, model, micro_xs, chain_file): - """Transport free system test suite. - - """ + """Transport free system test suite.""" # Create operator if operator_type == "coupled": op = CoupledOperator(model, chain_file=chain_file) else: - op = IndependentOperator(openmc.Materials([model.materials[0]]), - [1e15], - [micro_xs], - chain_file) + op = IndependentOperator( + openmc.Materials([model.materials[0]]), [1e15], [micro_xs], chain_file + ) # Power and timesteps dt = [917.4, 2262.6] # one Xe135_m1 half life and one Cs135_m1 half life # Perform simulation using the predictor algorithm - openmc.deplete.PredictorIntegrator(op, - dt, - power=0.0, - timestep_units='s').integrate() + openmc.deplete.PredictorIntegrator( + op, dt, power=0.0, timestep_units="s" + ).integrate() # Get path to test and reference results - path_test = op.output_dir / 'depletion_results.h5' + path_test = op.output_dir / "depletion_results.h5" # Load the reference/test results res_test = openmc.deplete.Results(path_test) - _, xe135m1_atoms = res_test.get_atoms('1', 'Xe135_m1') - _, xe135_atoms = res_test.get_atoms('1', 'Xe135') - _, cs135m1_atoms = res_test.get_atoms('1', 'Cs135_m1') - _, cs135_atoms = res_test.get_atoms('1', 'Cs135') + _, xe135m1_atoms = res_test.get_atoms("1", "Xe135_m1") + _, xe135_atoms = res_test.get_atoms("1", "Xe135") + _, cs135m1_atoms = res_test.get_atoms("1", "Cs135_m1") + _, cs135_atoms = res_test.get_atoms("1", "Cs135") tol = 1.0e-14 assert xe135m1_atoms[0] == pytest.approx(xe135m1_atoms[1] * 2, rel=tol) diff --git a/tests/regression_tests/deplete_no_transport/test.py b/tests/regression_tests/deplete_no_transport/test.py index 63ae584e116..1bd6cec0cb4 100644 --- a/tests/regression_tests/deplete_no_transport/test.py +++ b/tests/regression_tests/deplete_no_transport/test.py @@ -9,8 +9,12 @@ import openmc.deplete from openmc.deplete import IndependentOperator, MicroXS -from tests.regression_tests import config, assert_atoms_equal, \ - assert_reaction_rates_equal, assert_same_mats +from tests.regression_tests import ( + config, + assert_atoms_equal, + assert_reaction_rates_equal, + assert_same_mats, +) @pytest.fixture(scope="module") @@ -21,42 +25,49 @@ def fuel(): fuel.set_density("g/cc", 10.4) fuel.depletable = True - fuel.volume = np.pi * 0.42 ** 2 + fuel.volume = np.pi * 0.42**2 return fuel + @pytest.fixture(scope="module") def micro_xs(): - micro_xs_file = Path(__file__).parents[2] / 'micro_xs_simple.csv' + micro_xs_file = Path(__file__).parents[2] / "micro_xs_simple.csv" return MicroXS.from_csv(micro_xs_file) @pytest.fixture(scope="module") def chain_file(): - return Path(__file__).parents[2] / 'chain_simple.xml' + return Path(__file__).parents[2] / "chain_simple.xml" neutron_per_cm2_sec = 1164719970082145.0 -@pytest.mark.parametrize("multiproc, from_nuclides, normalization_mode, power, source_rate", [ - (True, True, 'source-rate', None, 1.0), - (False, True, 'source-rate', None, 1.0), - (True, True, 'fission-q', 174, None), - (False, True, 'fission-q', 174, None), - (True, False, 'source-rate', None, 1.0), - (False, False, 'source-rate', None, 1.0), - (True, False, 'fission-q', 174, None), - (False, False, 'fission-q', 174, None)]) -def test_against_self(run_in_tmpdir, - fuel, - micro_xs, - chain_file, - multiproc, - from_nuclides, - normalization_mode, - power, - source_rate): +@pytest.mark.parametrize( + "multiproc, from_nuclides, normalization_mode, power, source_rate", + [ + (True, True, "source-rate", None, 1.0), + (False, True, "source-rate", None, 1.0), + (True, True, "fission-q", 174, None), + (False, True, "fission-q", 174, None), + (True, False, "source-rate", None, 1.0), + (False, False, "source-rate", None, 1.0), + (True, False, "fission-q", 174, None), + (False, False, "fission-q", 174, None), + ], +) +def test_against_self( + run_in_tmpdir, + fuel, + micro_xs, + chain_file, + multiproc, + from_nuclides, + normalization_mode, + power, + source_rate, +): """Transport free system test suite. Runs an OpenMC transport-free depletion calculation and verifies @@ -65,34 +76,29 @@ def test_against_self(run_in_tmpdir, """ # Create operator flux = neutron_per_cm2_sec * fuel.volume - op = _create_operator(from_nuclides, - fuel, - flux, - micro_xs, - chain_file, - normalization_mode) + op = _create_operator( + from_nuclides, fuel, flux, micro_xs, chain_file, normalization_mode + ) # Power and timesteps dt = [360] # single step # Perform simulation using the predictor algorithm openmc.deplete.pool.USE_MULTIPROCESSING = multiproc - openmc.deplete.PredictorIntegrator(op, - dt, - power=power, - source_rates=source_rate, - timestep_units='s').integrate() + openmc.deplete.PredictorIntegrator( + op, dt, power=power, source_rates=source_rate, timestep_units="s" + ).integrate() # Get path to test and reference results - path_test = op.output_dir / 'depletion_results.h5' + path_test = op.output_dir / "depletion_results.h5" if power is None: - ref_path = 'test_reference_source_rate.h5' + ref_path = "test_reference_source_rate.h5" else: - ref_path = 'test_reference_fission_q.h5' + ref_path = "test_reference_fission_q.h5" path_reference = Path(__file__).with_name(ref_path) # If updating results, do so and return - if config['update']: + if config["update"]: shutil.copyfile(str(path_test), str(path_reference)) return @@ -108,28 +114,34 @@ def test_against_self(run_in_tmpdir, assert_reaction_rates_equal(res_ref, res_test, tol) -@pytest.mark.parametrize("multiproc, dt, time_units, time_type, atom_tol, rx_tol ", [ - (True, 360, 's', 'minutes', 2.0e-3, 3.0e-2), - (False, 360, 's', 'minutes', 2.0e-3, 3.0e-2), - (True, 4, 'h', 'hours', 2.0e-3, 6.0e-2), - (False, 4, 'h', 'hours', 2.0e-3, 6.0e-2), - (True, 5, 'd', 'days', 2.0e-3, 5.0e-2), - (False, 5, 'd', 'days', 2.0e-3, 5.0e-2), - (True, 100, 'd', 'months', 4.0e-3, 9.0e-2), - (False, 100, 'd', 'months', 4.0e-3, 9.0e-2)]) -def test_against_coupled(run_in_tmpdir, - fuel, - micro_xs, - chain_file, - multiproc, - dt, - time_units, - time_type, - atom_tol, - rx_tol): +@pytest.mark.parametrize( + "multiproc, dt, time_units, time_type, atom_tol, rx_tol ", + [ + (True, 360, "s", "minutes", 2.0e-3, 3.0e-2), + (False, 360, "s", "minutes", 2.0e-3, 3.0e-2), + (True, 4, "h", "hours", 2.0e-3, 6.0e-2), + (False, 4, "h", "hours", 2.0e-3, 6.0e-2), + (True, 5, "d", "days", 2.0e-3, 5.0e-2), + (False, 5, "d", "days", 2.0e-3, 5.0e-2), + (True, 100, "d", "months", 4.0e-3, 9.0e-2), + (False, 100, "d", "months", 4.0e-3, 9.0e-2), + ], +) +def test_against_coupled( + run_in_tmpdir, + fuel, + micro_xs, + chain_file, + multiproc, + dt, + time_units, + time_type, + atom_tol, + rx_tol, +): # Create operator flux = neutron_per_cm2_sec * fuel.volume - op = _create_operator(False, fuel, flux, micro_xs, chain_file, 'fission-q') + op = _create_operator(False, fuel, flux, micro_xs, chain_file, "fission-q") # Power and timesteps dt = [dt] # single step @@ -137,16 +149,17 @@ def test_against_coupled(run_in_tmpdir, # Perform simulation using the predictor algorithm openmc.deplete.pool.USE_MULTIPROCESSING = multiproc openmc.deplete.PredictorIntegrator( - op, dt, power=174, timestep_units=time_units).integrate() + op, dt, power=174, timestep_units=time_units + ).integrate() # Get path to test and reference results - path_test = op.output_dir / 'depletion_results.h5' + path_test = op.output_dir / "depletion_results.h5" - ref_path = f'test_reference_coupled_{time_type}.h5' + ref_path = f"test_reference_coupled_{time_type}.h5" path_reference = Path(__file__).with_name(ref_path) # If updating results, do so and return - if config['update']: + if config["update"]: shutil.copyfile(str(path_test), str(path_reference)) return @@ -161,30 +174,31 @@ def test_against_coupled(run_in_tmpdir, assert_reaction_rates_equal(res_ref, res_test, rx_tol) -def _create_operator(from_nuclides, - fuel, - flux, - micro_xs, - chain_file, - normalization_mode): +def _create_operator( + from_nuclides, fuel, flux, micro_xs, chain_file, normalization_mode +): if from_nuclides: nuclides = {} for nuc, dens in fuel.get_nuclide_atom_densities().items(): nuclides[nuc] = dens openmc.reset_auto_ids() - op = IndependentOperator.from_nuclides(fuel.volume, - nuclides, - flux, - micro_xs, - chain_file, - normalization_mode=normalization_mode) + op = IndependentOperator.from_nuclides( + fuel.volume, + nuclides, + flux, + micro_xs, + chain_file, + normalization_mode=normalization_mode, + ) else: - op = IndependentOperator(openmc.Materials([fuel]), - [flux], - [micro_xs], - chain_file, - normalization_mode=normalization_mode) + op = IndependentOperator( + openmc.Materials([fuel]), + [flux], + [micro_xs], + chain_file, + normalization_mode=normalization_mode, + ) return op diff --git a/tests/regression_tests/deplete_with_transfer_rates/test.py b/tests/regression_tests/deplete_with_transfer_rates/test.py index 10d60866a69..1ef6e7829bb 100644 --- a/tests/regression_tests/deplete_with_transfer_rates/test.py +++ b/tests/regression_tests/deplete_with_transfer_rates/test.py @@ -10,8 +10,12 @@ import openmc.deplete from openmc.deplete import CoupledOperator -from tests.regression_tests import config, assert_reaction_rates_equal, \ - assert_atoms_equal, assert_same_mats +from tests.regression_tests import ( + config, + assert_reaction_rates_equal, + assert_atoms_equal, + assert_same_mats, +) @pytest.fixture @@ -30,12 +34,12 @@ def model(): radii = [0.42, 0.45] f.volume = np.pi * radii[0] ** 2 - w.volume = np.pi * (radii[1]**2 - radii[0]**2) + w.volume = np.pi * (radii[1] ** 2 - radii[0] ** 2) materials = openmc.Materials([f, w]) surf_f = openmc.Sphere(r=radii[0]) - surf_w = openmc.Sphere(r=radii[1], boundary_type='reflective') + surf_w = openmc.Sphere(r=radii[1], boundary_type="reflective") cell_f = openmc.Cell(fill=f, region=-surf_f) cell_w = openmc.Cell(fill=w, region=+surf_f & -surf_w) geometry = openmc.Geometry([cell_f, cell_w]) @@ -47,35 +51,39 @@ def model(): return openmc.Model(geometry, materials, settings) -@pytest.mark.parametrize("rate, dest_mat, power, ref_result", [ - (1e-5, None, 0.0, 'no_depletion_only_removal'), - (-1e-5, None, 0.0, 'no_depletion_only_feed'), - (1e-5, None, 174.0, 'depletion_with_removal'), - (-1e-5, None, 174.0, 'depletion_with_feed'), - (-1e-5, 'w', 0.0, 'no_depletion_with_transfer'), - (1e-5, 'w', 174.0, 'depletion_with_transfer'), - ]) + +@pytest.mark.parametrize( + "rate, dest_mat, power, ref_result", + [ + (1e-5, None, 0.0, "no_depletion_only_removal"), + (-1e-5, None, 0.0, "no_depletion_only_feed"), + (1e-5, None, 174.0, "depletion_with_removal"), + (-1e-5, None, 174.0, "depletion_with_feed"), + (-1e-5, "w", 0.0, "no_depletion_with_transfer"), + (1e-5, "w", 174.0, "depletion_with_transfer"), + ], +) def test_transfer_rates(run_in_tmpdir, model, rate, dest_mat, power, ref_result): """Tests transfer_rates depletion class with transfer rates""" - chain_file = Path(__file__).parents[2] / 'chain_simple.xml' + chain_file = Path(__file__).parents[2] / "chain_simple.xml" - transfer_elements = ['Xe'] + transfer_elements = ["Xe"] op = CoupledOperator(model, chain_file) op.round_number = True - integrator = openmc.deplete.PredictorIntegrator( - op, [1], power, timestep_units = 'd') - integrator.add_transfer_rate('f', transfer_elements, rate, - destination_material=dest_mat) + integrator = openmc.deplete.PredictorIntegrator(op, [1], power, timestep_units="d") + integrator.add_transfer_rate( + "f", transfer_elements, rate, destination_material=dest_mat + ) integrator.integrate() # Get path to test and reference results - path_test = op.output_dir / 'depletion_results.h5' - path_reference = Path(__file__).with_name(f'ref_{ref_result}.h5') + path_test = op.output_dir / "depletion_results.h5" + path_reference = Path(__file__).with_name(f"ref_{ref_result}.h5") # If updating results, do so and return - if config['update']: + if config["update"]: shutil.copyfile(str(path_test), str(path_reference)) return diff --git a/tests/regression_tests/deplete_with_transport/example_geometry.py b/tests/regression_tests/deplete_with_transport/example_geometry.py index 84134f58f8a..a2aaeaaedae 100644 --- a/tests/regression_tests/deplete_with_transport/example_geometry.py +++ b/tests/regression_tests/deplete_with_transport/example_geometry.py @@ -27,14 +27,14 @@ def density_to_mat(dens_dict): """ mat = openmc.Material() for key in dens_dict: - mat.add_nuclide(key, 1.0e-24*dens_dict[key]) - mat.set_density('sum') + mat.add_nuclide(key, 1.0e-24 * dens_dict[key]) + mat.set_density("sum") return mat def generate_initial_number_density(): - """ Generates initial number density. + """Generates initial number density. These results were from a CASMO5 run in which the gadolinium pin was loaded with 2 wt percent of Gd-157. @@ -42,87 +42,87 @@ def generate_initial_number_density(): # Concentration to be used for all fuel pins fuel_dict = OrderedDict() - fuel_dict['U235'] = 1.05692e21 - fuel_dict['U234'] = 1.00506e19 - fuel_dict['U238'] = 2.21371e22 - fuel_dict['O16'] = 4.62954e22 - fuel_dict['O17'] = 1.127684e20 - fuel_dict['Xe135'] = 1.0e10 - fuel_dict['Xe136'] = 1.0e10 - fuel_dict['Gd156'] = 1.0e10 - fuel_dict['Gd157'] = 1.0e10 + fuel_dict["U235"] = 1.05692e21 + fuel_dict["U234"] = 1.00506e19 + fuel_dict["U238"] = 2.21371e22 + fuel_dict["O16"] = 4.62954e22 + fuel_dict["O17"] = 1.127684e20 + fuel_dict["Xe135"] = 1.0e10 + fuel_dict["Xe136"] = 1.0e10 + fuel_dict["Gd156"] = 1.0e10 + fuel_dict["Gd157"] = 1.0e10 # fuel_dict['O18'] = 9.51352e19 # Does not exist in ENDF71, merged into 17 # Concentration to be used for the gadolinium fuel pin fuel_gd_dict = OrderedDict() - fuel_gd_dict['U235'] = 1.03579e21 - fuel_gd_dict['U238'] = 2.16943e22 - fuel_gd_dict['Gd156'] = 3.95517E+10 - fuel_gd_dict['Gd157'] = 1.08156e20 - fuel_gd_dict['O16'] = 4.64035e22 - fuel_dict['Xe136'] = 1.0e10 - fuel_dict['Xe135'] = 1.0e10 + fuel_gd_dict["U235"] = 1.03579e21 + fuel_gd_dict["U238"] = 2.16943e22 + fuel_gd_dict["Gd156"] = 3.95517e10 + fuel_gd_dict["Gd157"] = 1.08156e20 + fuel_gd_dict["O16"] = 4.64035e22 + fuel_dict["Xe136"] = 1.0e10 + fuel_dict["Xe135"] = 1.0e10 # There are a whole bunch of 1e-10 stuff here. # Concentration to be used for cladding clad_dict = OrderedDict() - clad_dict['O16'] = 3.07427e20 - clad_dict['O17'] = 7.48868e17 - clad_dict['Cr50'] = 3.29620e18 - clad_dict['Cr52'] = 6.35639e19 - clad_dict['Cr53'] = 7.20763e18 - clad_dict['Cr54'] = 1.79413e18 - clad_dict['Fe54'] = 5.57350e18 - clad_dict['Fe56'] = 8.74921e19 - clad_dict['Fe57'] = 2.02057e18 - clad_dict['Fe58'] = 2.68901e17 - clad_dict['Cr50'] = 3.29620e18 - clad_dict['Cr52'] = 6.35639e19 - clad_dict['Cr53'] = 7.20763e18 - clad_dict['Cr54'] = 1.79413e18 - clad_dict['Ni58'] = 2.51631e19 - clad_dict['Ni60'] = 9.69278e18 - clad_dict['Ni61'] = 4.21338e17 - clad_dict['Ni62'] = 1.34341e18 - clad_dict['Ni64'] = 3.43127e17 - clad_dict['Zr90'] = 2.18320e22 - clad_dict['Zr91'] = 4.76104e21 - clad_dict['Zr92'] = 7.27734e21 - clad_dict['Zr94'] = 7.37494e21 - clad_dict['Zr96'] = 1.18814e21 - clad_dict['Sn112'] = 4.67352e18 - clad_dict['Sn114'] = 3.17992e18 - clad_dict['Sn115'] = 1.63814e18 - clad_dict['Sn116'] = 7.00546e19 - clad_dict['Sn117'] = 3.70027e19 - clad_dict['Sn118'] = 1.16694e20 - clad_dict['Sn119'] = 4.13872e19 - clad_dict['Sn120'] = 1.56973e20 - clad_dict['Sn122'] = 2.23076e19 - clad_dict['Sn124'] = 2.78966e19 + clad_dict["O16"] = 3.07427e20 + clad_dict["O17"] = 7.48868e17 + clad_dict["Cr50"] = 3.29620e18 + clad_dict["Cr52"] = 6.35639e19 + clad_dict["Cr53"] = 7.20763e18 + clad_dict["Cr54"] = 1.79413e18 + clad_dict["Fe54"] = 5.57350e18 + clad_dict["Fe56"] = 8.74921e19 + clad_dict["Fe57"] = 2.02057e18 + clad_dict["Fe58"] = 2.68901e17 + clad_dict["Cr50"] = 3.29620e18 + clad_dict["Cr52"] = 6.35639e19 + clad_dict["Cr53"] = 7.20763e18 + clad_dict["Cr54"] = 1.79413e18 + clad_dict["Ni58"] = 2.51631e19 + clad_dict["Ni60"] = 9.69278e18 + clad_dict["Ni61"] = 4.21338e17 + clad_dict["Ni62"] = 1.34341e18 + clad_dict["Ni64"] = 3.43127e17 + clad_dict["Zr90"] = 2.18320e22 + clad_dict["Zr91"] = 4.76104e21 + clad_dict["Zr92"] = 7.27734e21 + clad_dict["Zr94"] = 7.37494e21 + clad_dict["Zr96"] = 1.18814e21 + clad_dict["Sn112"] = 4.67352e18 + clad_dict["Sn114"] = 3.17992e18 + clad_dict["Sn115"] = 1.63814e18 + clad_dict["Sn116"] = 7.00546e19 + clad_dict["Sn117"] = 3.70027e19 + clad_dict["Sn118"] = 1.16694e20 + clad_dict["Sn119"] = 4.13872e19 + clad_dict["Sn120"] = 1.56973e20 + clad_dict["Sn122"] = 2.23076e19 + clad_dict["Sn124"] = 2.78966e19 # Gap concentration # Funny enough, the example problem uses air. gap_dict = OrderedDict() - gap_dict['O16'] = 7.86548e18 - gap_dict['O17'] = 2.99548e15 - gap_dict['N14'] = 3.38646e19 - gap_dict['N15'] = 1.23717e17 + gap_dict["O16"] = 7.86548e18 + gap_dict["O17"] = 2.99548e15 + gap_dict["N14"] = 3.38646e19 + gap_dict["N15"] = 1.23717e17 # Concentration to be used for coolant # No boron cool_dict = OrderedDict() - cool_dict['H1'] = 4.68063e22 - cool_dict['O16'] = 2.33427e22 - cool_dict['O17'] = 8.89086e18 + cool_dict["H1"] = 4.68063e22 + cool_dict["O16"] = 2.33427e22 + cool_dict["O17"] = 8.89086e18 # Store these dictionaries in the initial conditions dictionary initial_density = OrderedDict() - initial_density['fuel_gd'] = fuel_gd_dict - initial_density['fuel'] = fuel_dict - initial_density['gap'] = gap_dict - initial_density['clad'] = clad_dict - initial_density['cool'] = cool_dict + initial_density["fuel_gd"] = fuel_gd_dict + initial_density["fuel"] = fuel_dict + initial_density["gap"] = gap_dict + initial_density["clad"] = clad_dict + initial_density["cool"] = cool_dict # Set up libraries to use temperature = OrderedDict() @@ -132,35 +132,35 @@ def generate_initial_number_density(): MCNP = False if MCNP: - temperature['fuel_gd'] = 900.0 - temperature['fuel'] = 900.0 + temperature["fuel_gd"] = 900.0 + temperature["fuel"] = 900.0 # We approximate temperature of everything as 600K, even though it was # actually 580K. - temperature['gap'] = 600.0 - temperature['clad'] = 600.0 - temperature['cool'] = 600.0 + temperature["gap"] = 600.0 + temperature["clad"] = 600.0 + temperature["cool"] = 600.0 else: - temperature['fuel_gd'] = 293.6 - temperature['fuel'] = 293.6 - temperature['gap'] = 293.6 - temperature['clad'] = 293.6 - temperature['cool'] = 293.6 + temperature["fuel_gd"] = 293.6 + temperature["fuel"] = 293.6 + temperature["gap"] = 293.6 + temperature["clad"] = 293.6 + temperature["cool"] = 293.6 - sab['cool'] = 'c_H_in_H2O' + sab["cool"] = "c_H_in_H2O" # Set up burnable materials burn = OrderedDict() - burn['fuel_gd'] = True - burn['fuel'] = True - burn['gap'] = False - burn['clad'] = False - burn['cool'] = False + burn["fuel_gd"] = True + burn["fuel"] = True + burn["gap"] = False + burn["clad"] = False + burn["cool"] = False return temperature, sab, initial_density, burn def segment_pin(n_rings, n_wedges, r_fuel, r_gap, r_clad): - """ Calculates a segmented pin. + """Calculates a segmented pin. Separates a pin with n_rings and n_wedges. All cells have equal volume. Pin is centered at origin. @@ -177,17 +177,18 @@ def segment_pin(n_rings, n_wedges, r_fuel, r_gap, r_clad): r_rings = np.zeros(n_rings) for i in range(n_rings): - r_rings[i] = math.sqrt(1.0/(math.pi) * v_ring * (i+1)) + r_rings[i] = math.sqrt(1.0 / (math.pi) * v_ring * (i + 1)) # Compute thetas - theta = np.linspace(0, 2*math.pi, n_wedges + 1) + theta = np.linspace(0, 2 * math.pi, n_wedges + 1) # Compute surfaces - fuel_rings = [openmc.ZCylinder(x0=0, y0=0, r=r_rings[i]) - for i in range(n_rings)] + fuel_rings = [openmc.ZCylinder(x0=0, y0=0, r=r_rings[i]) for i in range(n_rings)] - fuel_wedges = [openmc.Plane(a=math.cos(theta[i]), b=math.sin(theta[i])) - for i in range(n_wedges)] + fuel_wedges = [ + openmc.Plane(a=math.cos(theta[i]), b=math.sin(theta[i])) + for i in range(n_wedges) + ] gap_ring = openmc.ZCylinder(x0=0, y0=0, r=r_gap) clad_ring = openmc.ZCylinder(x0=0, y0=0, r=r_clad) @@ -196,50 +197,52 @@ def segment_pin(n_rings, n_wedges, r_fuel, r_gap, r_clad): fuel_cells = [] if n_wedges == 1: for i in range(n_rings): - cell = openmc.Cell(name='fuel') + cell = openmc.Cell(name="fuel") if i == 0: cell.region = -fuel_rings[0] else: - cell.region = +fuel_rings[i-1] & -fuel_rings[i] + cell.region = +fuel_rings[i - 1] & -fuel_rings[i] fuel_cells.append(cell) else: for i in range(n_rings): for j in range(n_wedges): - cell = openmc.Cell(name='fuel') + cell = openmc.Cell(name="fuel") if i == 0: - if j != n_wedges-1: - cell.region = (-fuel_rings[0] - & +fuel_wedges[j] - & -fuel_wedges[j+1]) + if j != n_wedges - 1: + cell.region = ( + -fuel_rings[0] & +fuel_wedges[j] & -fuel_wedges[j + 1] + ) else: - cell.region = (-fuel_rings[0] - & +fuel_wedges[j] - & -fuel_wedges[0]) + cell.region = -fuel_rings[0] & +fuel_wedges[j] & -fuel_wedges[0] else: - if j != n_wedges-1: - cell.region = (+fuel_rings[i-1] - & -fuel_rings[i] - & +fuel_wedges[j] - & -fuel_wedges[j+1]) + if j != n_wedges - 1: + cell.region = ( + +fuel_rings[i - 1] + & -fuel_rings[i] + & +fuel_wedges[j] + & -fuel_wedges[j + 1] + ) else: - cell.region = (+fuel_rings[i-1] - & -fuel_rings[i] - & +fuel_wedges[j] - & -fuel_wedges[0]) + cell.region = ( + +fuel_rings[i - 1] + & -fuel_rings[i] + & +fuel_wedges[j] + & -fuel_wedges[0] + ) fuel_cells.append(cell) # Gap ring - gap_cell = openmc.Cell(name='gap') + gap_cell = openmc.Cell(name="gap") gap_cell.region = +fuel_rings[-1] & -gap_ring fuel_cells.append(gap_cell) # Clad ring - clad_cell = openmc.Cell(name='clad') + clad_cell = openmc.Cell(name="clad") clad_cell.region = +gap_ring & -clad_ring fuel_cells.append(clad_cell) # Moderator - mod_cell = openmc.Cell(name='cool') + mod_cell = openmc.Cell(name="cool") mod_cell.region = +clad_ring fuel_cells.append(mod_cell) @@ -251,7 +254,7 @@ def segment_pin(n_rings, n_wedges, r_fuel, r_gap, r_clad): def generate_geometry(n_rings, n_wedges): - """ Generates example geometry. + """Generates example geometry. This function creates the initial geometry, a 9 pin reflective problem. One pin, containing gadolinium, is discretized into sectors. @@ -279,53 +282,61 @@ def generate_geometry(n_rings, n_wedges): # This table describes the 'fuel' to actual type mapping # It's not necessary to do it this way. Just adjust the initial conditions # below. - mapping = ['fuel', 'fuel', 'fuel', - 'fuel', 'fuel_gd', 'fuel', - 'fuel', 'fuel', 'fuel'] + mapping = [ + "fuel", + "fuel", + "fuel", + "fuel", + "fuel_gd", + "fuel", + "fuel", + "fuel", + "fuel", + ] # Form pin cell - fuel_u, v_segment, v_gap, v_clad = segment_pin(n_rings, n_wedges, r_fuel, r_gap, r_clad) + fuel_u, v_segment, v_gap, v_clad = segment_pin( + n_rings, n_wedges, r_fuel, r_gap, r_clad + ) # Form lattice - all_water_c = openmc.Cell(name='cool') - all_water_u = openmc.Universe(cells=(all_water_c, )) + all_water_c = openmc.Cell(name="cool") + all_water_u = openmc.Universe(cells=(all_water_c,)) lattice = openmc.RectLattice() - lattice.pitch = [pitch]*2 - lattice.lower_left = [-pitch*n_pin/2, -pitch*n_pin/2] + lattice.pitch = [pitch] * 2 + lattice.lower_left = [-pitch * n_pin / 2, -pitch * n_pin / 2] lattice_array = [[fuel_u for i in range(n_pin)] for j in range(n_pin)] lattice.universes = lattice_array lattice.outer = all_water_u # Bound universe - x_low = openmc.XPlane(-pitch*n_pin/2, boundary_type='reflective') - x_high = openmc.XPlane(pitch*n_pin/2, boundary_type='reflective') - y_low = openmc.YPlane(-pitch*n_pin/2, boundary_type='reflective') - y_high = openmc.YPlane(pitch*n_pin/2, boundary_type='reflective') - z_low = openmc.ZPlane(-10, boundary_type='reflective') - z_high = openmc.ZPlane(10, boundary_type='reflective') + x_low = openmc.XPlane(-pitch * n_pin / 2, boundary_type="reflective") + x_high = openmc.XPlane(pitch * n_pin / 2, boundary_type="reflective") + y_low = openmc.YPlane(-pitch * n_pin / 2, boundary_type="reflective") + y_high = openmc.YPlane(pitch * n_pin / 2, boundary_type="reflective") + z_low = openmc.ZPlane(-10, boundary_type="reflective") + z_high = openmc.ZPlane(10, boundary_type="reflective") # Compute bounding box - lower_left = [-pitch*n_pin/2, -pitch*n_pin/2, -10] - upper_right = [pitch*n_pin/2, pitch*n_pin/2, 10] + lower_left = [-pitch * n_pin / 2, -pitch * n_pin / 2, -10] + upper_right = [pitch * n_pin / 2, pitch * n_pin / 2, 10] root_c = openmc.Cell(fill=lattice) - root_c.region = (+x_low & -x_high - & +y_low & -y_high - & +z_low & -z_high) - root_u = openmc.Universe(universe_id=0, cells=(root_c, )) + root_c.region = +x_low & -x_high & +y_low & -y_high & +z_low & -z_high + root_u = openmc.Universe(universe_id=0, cells=(root_c,)) geometry = openmc.Geometry(root_u) v_cool = pitch**2 - (v_gap + v_clad + n_rings * n_wedges * v_segment) # Store volumes for later usage - volume = {'fuel': v_segment, 'gap': v_gap, 'clad': v_clad, 'cool': v_cool} + volume = {"fuel": v_segment, "gap": v_gap, "clad": v_clad, "cool": v_cool} return geometry, volume, mapping, lower_left, upper_right def generate_problem(n_rings=5, n_wedges=8): - """ Merges geometry and materials. + """Merges geometry and materials. This function initializes the materials for each cell using the dictionaries provided by generate_initial_number_density. It is assumed a cell named @@ -341,13 +352,15 @@ def generate_problem(n_rings=5, n_wedges=8): # Get materials dictionary, geometry, and volumes temperature, sab, initial_density, burn = generate_initial_number_density() - geometry, volume, mapping, lower_left, upper_right = generate_geometry(n_rings, n_wedges) + geometry, volume, mapping, lower_left, upper_right = generate_geometry( + n_rings, n_wedges + ) # Apply distribmats, fill geometry cells = geometry.root_universe.get_all_cells() for cell_id in cells: cell = cells[cell_id] - if cell.name == 'fuel': + if cell.name == "fuel": omc_mats = [] @@ -358,12 +371,12 @@ def generate_problem(n_rings=5, n_wedges=8): omc_mat.add_s_alpha_beta(sab[cell_type]) omc_mat.temperature = temperature[cell_type] omc_mat.depletable = burn[cell_type] - omc_mat.volume = volume['fuel'] + omc_mat.volume = volume["fuel"] omc_mats.append(omc_mat) cell.fill = omc_mats - elif cell.name != '': + elif cell.name != "": omc_mat = density_to_mat(initial_density[cell.name]) if cell.name in sab: diff --git a/tests/regression_tests/deplete_with_transport/test.py b/tests/regression_tests/deplete_with_transport/test.py index 0f7ebf00f31..4da6cd515df 100644 --- a/tests/regression_tests/deplete_with_transport/test.py +++ b/tests/regression_tests/deplete_with_transport/test.py @@ -53,27 +53,27 @@ def test_full(run_in_tmpdir, problem, multiproc): model = openmc.Model(geometry=geometry, settings=settings) # Create operator - chain_file = Path(__file__).parents[2] / 'chain_simple.xml' + chain_file = Path(__file__).parents[2] / "chain_simple.xml" op = openmc.deplete.CoupledOperator(model, chain_file) op.round_number = True # Power and timesteps - dt1 = 15.*24*60*60 # 15 days - dt2 = 1.5*30*24*60*60 # 1.5 months - N = floor(dt2/dt1) + dt1 = 15.0 * 24 * 60 * 60 # 15 days + dt2 = 1.5 * 30 * 24 * 60 * 60 # 1.5 months + N = floor(dt2 / dt1) dt = np.full(N, dt1) - power = 2.337e15*4*JOULE_PER_EV*1e6 # MeV/second cm from CASMO + power = 2.337e15 * 4 * JOULE_PER_EV * 1e6 # MeV/second cm from CASMO # Perform simulation using the predictor algorithm openmc.deplete.pool.USE_MULTIPROCESSING = multiproc openmc.deplete.PredictorIntegrator(op, dt, power).integrate() # Get path to test and reference results - path_test = op.output_dir / 'depletion_results.h5' - path_reference = Path(__file__).with_name('test_reference.h5') + path_test = op.output_dir / "depletion_results.h5" + path_reference = Path(__file__).with_name("test_reference.h5") # If updating results, do so and return - if config['update']: + if config["update"]: shutil.copyfile(str(path_test), str(path_reference)) return @@ -83,18 +83,20 @@ def test_full(run_in_tmpdir, problem, multiproc): # Assert same mats for mat in res_ref[0].index_mat: - assert mat in res_test[0].index_mat, \ - "Material {} not in new results.".format(mat) + assert mat in res_test[0].index_mat, "Material {} not in new results.".format( + mat + ) for nuc in res_ref[0].index_nuc: - assert nuc in res_test[0].index_nuc, \ - "Nuclide {} not in new results.".format(nuc) + assert nuc in res_test[0].index_nuc, "Nuclide {} not in new results.".format( + nuc + ) for mat in res_test[0].index_mat: - assert mat in res_ref[0].index_mat, \ - "Material {} not in old results.".format(mat) + assert mat in res_ref[0].index_mat, "Material {} not in old results.".format( + mat + ) for nuc in res_test[0].index_nuc: - assert nuc in res_ref[0].index_nuc, \ - "Nuclide {} not in old results.".format(nuc) + assert nuc in res_ref[0].index_nuc, "Nuclide {} not in old results.".format(nuc) assert_atoms_equal(res_ref, res_test, tol=1e-6) @@ -126,14 +128,14 @@ def test_full(run_in_tmpdir, problem, multiproc): runtimes = {k: np.array(v) for k, v in runtimes.items()} # Check that runtimes are qualitatively correct - assert runtimes['reading cross sections'][0] != 0 - assert runtimes['total initialization'][0] != 0 - assert np.all(runtimes['reading cross sections'][1:] == 0) - assert np.all(runtimes['total initialization'][1:] == 0) - assert np.all(runtimes['inactive batches'] == 0) - del runtimes['reading cross sections'] - del runtimes['total initialization'] - del runtimes['inactive batches'] + assert runtimes["reading cross sections"][0] != 0 + assert runtimes["total initialization"][0] != 0 + assert np.all(runtimes["reading cross sections"][1:] == 0) + assert np.all(runtimes["total initialization"][1:] == 0) + assert np.all(runtimes["inactive batches"] == 0) + del runtimes["reading cross sections"] + del runtimes["total initialization"] + del runtimes["inactive batches"] for measure, times in runtimes.items(): assert np.all(times != 0) @@ -141,7 +143,7 @@ def test_full(run_in_tmpdir, problem, multiproc): def test_depletion_results_to_material(run_in_tmpdir, problem): """Checks openmc.Materials objects can be created from depletion results""" # Load the reference/test results - path_reference = Path(__file__).with_name('test_reference.h5') + path_reference = Path(__file__).with_name("test_reference.h5") res_ref = openmc.deplete.Results(path_reference) # Firstly need to export materials.xml file for the initial simulation state @@ -156,16 +158,16 @@ def test_depletion_results_to_material(run_in_tmpdir, problem): last_step_materials = res_ref.export_to_materials(-1) # Export final depletion step materials to XML - output_xml_file = 'last_step_materials.xml' + output_xml_file = "last_step_materials.xml" last_step_materials.export_to_xml(path=output_xml_file) - with open(output_xml_file, 'r') as result_file: + with open(output_xml_file, "r") as result_file: result_file_lines = result_file.readlines() # If updating results, do so and return. We write out the last-step # depleted materials as an XML, and save the list of lines to diff. - reference_file = Path(__file__).with_name('last_step_reference_materials.xml') - if config['update']: - with open(reference_file, 'w') as ref_file: + reference_file = Path(__file__).with_name("last_step_reference_materials.xml") + if config["update"]: + with open(reference_file, "w") as ref_file: ref_file.writelines(result_file_lines) return diff --git a/tests/regression_tests/diff_tally/test.py b/tests/regression_tests/diff_tally/test.py index 89460df9152..4e1e079755e 100644 --- a/tests/regression_tests/diff_tally/test.py +++ b/tests/regression_tests/diff_tally/test.py @@ -16,9 +16,10 @@ def __init__(self, *args, **kwargs): self._model.settings.batches = 3 self._model.settings.inactive = 0 self._model.settings.particles = 100 - self._model.settings.source = openmc.IndependentSource(space=openmc.stats.Box( - [-160, -160, -183], [160, 160, 183])) - self._model.settings.temperature['multipole'] = True + self._model.settings.source = openmc.IndependentSource( + space=openmc.stats.Box([-160, -160, -183], [160, 160, 183]) + ) + self._model.settings.temperature["multipole"] = True filt_mats = openmc.MaterialFilter((1, 3)) filt_eout = openmc.EnergyoutFilter((0.0, 0.625, 20.0e6)) @@ -26,29 +27,29 @@ def __init__(self, *args, **kwargs): # We want density derivatives for both water and fuel to get coverage # for both fissile and non-fissile materials. d1 = openmc.TallyDerivative(derivative_id=1) - d1.variable = 'density' + d1.variable = "density" d1.material = 3 d2 = openmc.TallyDerivative(derivative_id=2) - d2.variable = 'density' + d2.variable = "density" d2.material = 1 # O-16 is a good nuclide to test against because it is present in both # water and fuel. Some routines need to recognize that they have the # perturbed nuclide but not the perturbed material. d3 = openmc.TallyDerivative(derivative_id=3) - d3.variable = 'nuclide_density' + d3.variable = "nuclide_density" d3.material = 1 - d3.nuclide = 'O16' + d3.nuclide = "O16" # A fissile nuclide, just for good measure. d4 = openmc.TallyDerivative(derivative_id=4) - d4.variable = 'nuclide_density' + d4.variable = "nuclide_density" d4.material = 1 - d4.nuclide = 'U235' + d4.nuclide = "U235" # Temperature derivatives. d5 = openmc.TallyDerivative(derivative_id=5) - d5.variable = 'temperature' + d5.variable = "temperature" d5.material = 1 derivs = [d1, d2, d3, d4, d5] @@ -56,7 +57,7 @@ def __init__(self, *args, **kwargs): # Cover the flux score. for i in range(5): t = openmc.Tally() - t.scores = ['flux'] + t.scores = ["flux"] t.filters = [filt_mats] t.derivative = derivs[i] self._model.tallies.append(t) @@ -64,36 +65,36 @@ def __init__(self, *args, **kwargs): # Cover supported scores with a collision estimator. for i in range(5): t = openmc.Tally() - t.scores = ['total', 'absorption', 'scatter', 'fission', 'nu-fission'] + t.scores = ["total", "absorption", "scatter", "fission", "nu-fission"] t.filters = [filt_mats] - t.nuclides = ['total', 'U235'] + t.nuclides = ["total", "U235"] t.derivative = derivs[i] self._model.tallies.append(t) # Cover an analog estimator. for i in range(5): t = openmc.Tally() - t.scores = ['absorption'] + t.scores = ["absorption"] t.filters = [filt_mats] - t.estimator = 'analog' + t.estimator = "analog" t.derivative = derivs[i] self._model.tallies.append(t) # Energyout filter and total nuclide for the density derivatives. for i in range(2): t = openmc.Tally() - t.scores = ['nu-fission', 'scatter'] + t.scores = ["nu-fission", "scatter"] t.filters = [filt_mats, filt_eout] - t.nuclides = ['total', 'U235'] + t.nuclides = ["total", "U235"] t.derivative = derivs[i] self._model.tallies.append(t) # Energyout filter without total nuclide for other derivatives. for i in range(2, 5): t = openmc.Tally() - t.scores = ['nu-fission', 'scatter'] + t.scores = ["nu-fission", "scatter"] t.filters = [filt_mats, filt_eout] - t.nuclides = ['U235'] + t.nuclides = ["U235"] t.derivative = derivs[i] self._model.tallies.append(t) @@ -107,11 +108,10 @@ def _get_results(self): df = pd.concat(tally_dfs, ignore_index=True) # Extract the relevant data as a CSV string. - cols = ('d_material', 'd_nuclide', 'd_variable', 'score', 'mean', - 'std. dev.') - return df.to_csv(None, columns=cols, index=False, float_format='%.7e') + cols = ("d_material", "d_nuclide", "d_variable", "score", "mean", "std. dev.") + return df.to_csv(None, columns=cols, index=False, float_format="%.7e") def test_diff_tally(): - harness = DiffTallyTestHarness('statepoint.3.h5') + harness = DiffTallyTestHarness("statepoint.3.h5") harness.main() diff --git a/tests/regression_tests/distribmat/test.py b/tests/regression_tests/distribmat/test.py index 02f7e773e58..e3048300576 100644 --- a/tests/regression_tests/distribmat/test.py +++ b/tests/regression_tests/distribmat/test.py @@ -11,20 +11,19 @@ def __init__(self, *args, **kwargs): #################### moderator = openmc.Material(material_id=1) - moderator.set_density('g/cc', 1.0) - moderator.add_nuclide('H1', 2.0) - moderator.add_nuclide('O16', 1.0) + moderator.set_density("g/cc", 1.0) + moderator.add_nuclide("H1", 2.0) + moderator.add_nuclide("O16", 1.0) dense_fuel = openmc.Material(material_id=2) - dense_fuel.set_density('g/cc', 4.5) - dense_fuel.add_nuclide('U235', 1.0) + dense_fuel.set_density("g/cc", 4.5) + dense_fuel.add_nuclide("U235", 1.0) light_fuel = openmc.Material(material_id=3) - light_fuel.set_density('g/cc', 2.0) - light_fuel.add_nuclide('U235', 1.0) + light_fuel.set_density("g/cc", 2.0) + light_fuel.add_nuclide("U235", 1.0) - self._model.materials = openmc.Materials([moderator, dense_fuel, - light_fuel]) + self._model.materials = openmc.Materials([moderator, dense_fuel, light_fuel]) #################### # Geometry @@ -42,7 +41,7 @@ def __init__(self, *args, **kwargs): lat = openmc.RectLattice(lattice_id=101) lat.lower_left = [-2.0, -2.0] lat.pitch = [2.0, 2.0] - lat.universes = [[fuel_univ]*2]*2 + lat.universes = [[fuel_univ] * 2] * 2 lat.outer = mod_univ x0 = openmc.XPlane(x0=-3.0) @@ -50,7 +49,7 @@ def __init__(self, *args, **kwargs): y0 = openmc.YPlane(y0=-3.0) y1 = openmc.YPlane(y0=3.0) for s in [x0, x1, y0, y1]: - s.boundary_type = 'reflective' + s.boundary_type = "reflective" c101 = openmc.Cell(cell_id=101, fill=lat) c101.region = +x0 & -x1 & +y0 & -y1 root_univ = openmc.Universe(universe_id=0, cells=[c101]) @@ -65,8 +64,9 @@ def __init__(self, *args, **kwargs): sets_file.batches = 5 sets_file.inactive = 0 sets_file.particles = 1000 - sets_file.source = openmc.IndependentSource(space=openmc.stats.Box( - [-1, -1, -1], [1, 1, 1])) + sets_file.source = openmc.IndependentSource( + space=openmc.stats.Box([-1, -1, -1], [1, 1, 1]) + ) self._model.settings = sets_file #################### @@ -74,17 +74,17 @@ def __init__(self, *args, **kwargs): #################### plot1 = openmc.Plot(plot_id=1) - plot1.basis = 'xy' - plot1.color_by = 'cell' - plot1.filename = 'cellplot' + plot1.basis = "xy" + plot1.color_by = "cell" + plot1.filename = "cellplot" plot1.origin = (0, 0, 0) plot1.width = (7, 7) plot1.pixels = (400, 400) plot2 = openmc.Plot(plot_id=2) - plot2.basis = 'xy' - plot2.color_by = 'material' - plot2.filename = 'matplot' + plot2.basis = "xy" + plot2.color_by = "material" + plot2.filename = "matplot" plot2.origin = (0, 0, 0) plot2.width = (7, 7) plot2.pixels = (400, 400) @@ -93,11 +93,11 @@ def __init__(self, *args, **kwargs): def _get_results(self): outstr = super()._get_results() - su = openmc.Summary('summary.h5') + su = openmc.Summary("summary.h5") outstr += str(su.geometry.get_all_cells()[11]) return outstr def test_distribmat(): - harness = DistribmatTestHarness('statepoint.5.h5', model=openmc.Model()) + harness = DistribmatTestHarness("statepoint.5.h5", model=openmc.Model()) harness.main() diff --git a/tests/regression_tests/eigenvalue_genperbatch/test.py b/tests/regression_tests/eigenvalue_genperbatch/test.py index 17c6dff7f97..eb8d1821cc9 100644 --- a/tests/regression_tests/eigenvalue_genperbatch/test.py +++ b/tests/regression_tests/eigenvalue_genperbatch/test.py @@ -9,11 +9,11 @@ def model(): model = openmc.model.Model() m = openmc.Material() - m.set_density('g/cm3', 4.5) - m.add_nuclide('U235', 1.0) + m.set_density("g/cm3", 4.5) + m.add_nuclide("U235", 1.0) model.materials.append(m) - sph = openmc.Sphere(r=10.0, boundary_type='vacuum') + sph = openmc.Sphere(r=10.0, boundary_type="vacuum") c = openmc.Cell(fill=m, region=-sph) model.geometry = openmc.Geometry([c]) @@ -21,16 +21,16 @@ def model(): model.settings.inactive = 3 model.settings.batches = 7 model.settings.generations_per_batch = 3 - space = openmc.stats.Box((-4.0, -4.0, -4.0), (4.0, 4.0, 4.)) + space = openmc.stats.Box((-4.0, -4.0, -4.0), (4.0, 4.0, 4.0)) model.settings.source = openmc.IndependentSource(space=space) t = openmc.Tally() - t.scores = ['flux'] + t.scores = ["flux"] model.tallies.append(t) return model def test_eigenvalue_genperbatch(model): - harness = PyAPITestHarness('statepoint.7.h5', model) + harness = PyAPITestHarness("statepoint.7.h5", model) harness.main() diff --git a/tests/regression_tests/eigenvalue_no_inactive/test.py b/tests/regression_tests/eigenvalue_no_inactive/test.py index 5ab49001530..255f0987881 100644 --- a/tests/regression_tests/eigenvalue_no_inactive/test.py +++ b/tests/regression_tests/eigenvalue_no_inactive/test.py @@ -2,5 +2,5 @@ def test_eigenvalue_no_inactive(): - harness = TestHarness('statepoint.10.h5') + harness = TestHarness("statepoint.10.h5") harness.main() diff --git a/tests/regression_tests/energy_cutoff/test.py b/tests/regression_tests/energy_cutoff/test.py index 9aca802fa35..86bd6da9871 100755 --- a/tests/regression_tests/energy_cutoff/test.py +++ b/tests/regression_tests/energy_cutoff/test.py @@ -10,9 +10,9 @@ def __init__(self, *args, **kwargs): energy_cutoff = 4.0 # Material is composed of H-1 - mat = openmc.Material(material_id=1, name='mat') - mat.set_density('atom/b-cm', 0.069335) - mat.add_nuclide('H1', 40.0) + mat = openmc.Material(material_id=1, name="mat") + mat.set_density("atom/b-cm", 0.069335) + mat.add_nuclide("H1", 40.0) self._model.materials = openmc.Materials([mat]) # Cell is box with reflective boundary @@ -23,31 +23,32 @@ def __init__(self, *args, **kwargs): z1 = openmc.ZPlane(surface_id=5, z0=-1) z2 = openmc.ZPlane(surface_id=6, z0=1) for surface in [x1, x2, y1, y2, z1, z2]: - surface.boundary_type = 'reflective' - box = openmc.Cell(cell_id=1, name='box') + surface.boundary_type = "reflective" + box = openmc.Cell(cell_id=1, name="box") box.region = +x1 & -x2 & +y1 & -y2 & +z1 & -z2 box.fill = mat - root = openmc.Universe(universe_id=0, name='root universe') + root = openmc.Universe(universe_id=0, name="root universe") root.add_cell(box) self._model.geometry = openmc.Geometry(root) # Set the running parameters settings_file = openmc.Settings() - settings_file.run_mode = 'fixed source' + settings_file.run_mode = "fixed source" settings_file.batches = 10 settings_file.particles = 100 - settings_file.cutoff = {'energy_neutron': energy_cutoff} + settings_file.cutoff = {"energy_neutron": energy_cutoff} bounds = [-1, -1, -1, 1, 1, 1] uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:]) watt_dist = openmc.stats.Watt() - settings_file.source = openmc.IndependentSource(space=uniform_dist, - energy=watt_dist) + settings_file.source = openmc.IndependentSource( + space=uniform_dist, energy=watt_dist + ) self._model.settings = settings_file # Tally flux under energy cutoff tallies = openmc.Tallies() tally = openmc.Tally(1) - tally.scores = ['flux'] + tally.scores = ["flux"] energy_filter = openmc.filter.EnergyFilter((0.0, energy_cutoff)) tally.filters = [energy_filter] tallies.append(tally) @@ -59,15 +60,15 @@ def _get_results(self): sp = openmc.StatePoint(self._sp_name) # Write out tally data. - outstr = '' + outstr = "" t = sp.get_tally() - outstr += 'tally {}:\n'.format(t.id) - outstr += 'sum = {:12.6E}\n'.format(t.sum[0, 0, 0]) - outstr += 'sum_sq = {:12.6E}\n'.format(t.sum_sq[0, 0, 0]) + outstr += "tally {}:\n".format(t.id) + outstr += "sum = {:12.6E}\n".format(t.sum[0, 0, 0]) + outstr += "sum_sq = {:12.6E}\n".format(t.sum_sq[0, 0, 0]) return outstr def test_energy_cutoff(): - harness = EnergyCutoffTestHarness('statepoint.10.h5', model=openmc.Model()) + harness = EnergyCutoffTestHarness("statepoint.10.h5", model=openmc.Model()) harness.main() diff --git a/tests/regression_tests/energy_grid/test.py b/tests/regression_tests/energy_grid/test.py index 889bdfbc61f..8aa0a795b15 100644 --- a/tests/regression_tests/energy_grid/test.py +++ b/tests/regression_tests/energy_grid/test.py @@ -2,5 +2,5 @@ def test_energy_grid(): - harness = TestHarness('statepoint.10.h5') + harness = TestHarness("statepoint.10.h5") harness.main() diff --git a/tests/regression_tests/energy_laws/test.py b/tests/regression_tests/energy_laws/test.py index 1746b2d31b1..754f8eda5d5 100644 --- a/tests/regression_tests/energy_laws/test.py +++ b/tests/regression_tests/energy_laws/test.py @@ -29,14 +29,14 @@ def model(): model = openmc.model.Model() m = openmc.Material() - m.set_density('g/cm3', 20.0) - m.add_nuclide('U233', 1.0) - m.add_nuclide('Am244', 1.0) - m.add_nuclide('H2', 1.0) - m.add_nuclide('Na23', 1.0) - m.add_nuclide('Ta181', 1.0) - - s = openmc.Sphere(r=100.0, boundary_type='reflective') + m.set_density("g/cm3", 20.0) + m.add_nuclide("U233", 1.0) + m.add_nuclide("Am244", 1.0) + m.add_nuclide("H2", 1.0) + m.add_nuclide("Na23", 1.0) + m.add_nuclide("Ta181", 1.0) + + s = openmc.Sphere(r=100.0, boundary_type="reflective") c = openmc.Cell(fill=m, region=-s) model.geometry = openmc.Geometry([c]) @@ -48,5 +48,5 @@ def model(): def test_energy_laws(model): - harness = PyAPITestHarness('statepoint.10.h5', model) + harness = PyAPITestHarness("statepoint.10.h5", model) harness.main() diff --git a/tests/regression_tests/enrichment/test.py b/tests/regression_tests/enrichment/test.py index a20838c7cb8..0ccfeec9834 100644 --- a/tests/regression_tests/enrichment/test.py +++ b/tests/regression_tests/enrichment/test.py @@ -12,22 +12,22 @@ def test_enrichment(): # element.expand() method expands Uranium to the proper enrichment. uranium = Material() - uranium.add_element('U', 1.0, 'wo', 4.95) + uranium.add_element("U", 1.0, "wo", 4.95) densities = uranium.get_nuclide_densities() - sum_densities = 0. + sum_densities = 0.0 for nuc in densities.keys(): - assert nuc in ('U234', 'U235', 'U236', 'U238') + assert nuc in ("U234", "U235", "U236", "U238") sum_densities += densities[nuc][1] # Compute the weight percent U235 - enrichment = densities['U235'][1] / sum_densities - assert np.isclose(enrichment, 0.0495, rtol=1.e-8) + enrichment = densities["U235"][1] / sum_densities + assert np.isclose(enrichment, 0.0495, rtol=1.0e-8) # Compute the ratio of U234/U235 - u234_to_u235 = densities['U234'][1] / densities['U235'][1] - assert np.isclose(u234_to_u235, 0.0089, rtol=1.e-8) + u234_to_u235 = densities["U234"][1] / densities["U235"][1] + assert np.isclose(u234_to_u235, 0.0089, rtol=1.0e-8) # Compute the ratio of U236/U235 - u236_to_u235 = densities['U236'][1] / densities['U235'][1] - assert np.isclose(u236_to_u235, 0.0046, rtol=1.e-8) + u236_to_u235 = densities["U236"][1] / densities["U235"][1] + assert np.isclose(u236_to_u235, 0.0046, rtol=1.0e-8) diff --git a/tests/regression_tests/entropy/test.py b/tests/regression_tests/entropy/test.py index af1fbd56a39..4c4f034eb50 100644 --- a/tests/regression_tests/entropy/test.py +++ b/tests/regression_tests/entropy/test.py @@ -13,17 +13,17 @@ def _get_results(self): statepoint = glob.glob(os.path.join(os.getcwd(), self._sp_name))[0] with StatePoint(statepoint) as sp: # Write out k-combined. - outstr = 'k-combined:\n' - outstr += '{:12.6E} {:12.6E}\n'.format(sp.keff.n, sp.keff.s) + outstr = "k-combined:\n" + outstr += "{:12.6E} {:12.6E}\n".format(sp.keff.n, sp.keff.s) # Write out entropy data. - outstr += 'entropy:\n' - results = ['{:12.6E}'.format(x) for x in sp.entropy] - outstr += '\n'.join(results) + '\n' + outstr += "entropy:\n" + results = ["{:12.6E}".format(x) for x in sp.entropy] + outstr += "\n".join(results) + "\n" return outstr def test_entropy(): - harness = EntropyTestHarness('statepoint.10.h5') + harness = EntropyTestHarness("statepoint.10.h5") harness.main() diff --git a/tests/regression_tests/external_moab/test.py b/tests/regression_tests/external_moab/test.py index ce4e78a2c25..06ec1e3c9cb 100644 --- a/tests/regression_tests/external_moab/test.py +++ b/tests/regression_tests/external_moab/test.py @@ -16,8 +16,8 @@ from tests.testing_harness import PyAPITestHarness pytestmark = pytest.mark.skipif( - not openmc.lib._dagmc_enabled(), - reason="DAGMC is not enabled.") + not openmc.lib._dagmc_enabled(), reason="DAGMC is not enabled." +) TETS_PER_VOXEL = 12 @@ -29,9 +29,11 @@ def cpp_driver(request): """Compile the external source""" # Get build directory and write CMakeLists.txt file - openmc_dir = Path(str(request.config.rootdir)) / 'build' - with open('CMakeLists.txt', 'w') as f: - f.write(textwrap.dedent(""" + openmc_dir = Path(str(request.config.rootdir)) / "build" + with open("CMakeLists.txt", "w") as f: + f.write( + textwrap.dedent( + """ cmake_minimum_required(VERSION 3.10 FATAL_ERROR) project(openmc_cpp_driver CXX) add_executable(main main.cpp) @@ -40,14 +42,18 @@ def cpp_driver(request): target_compile_features(main PUBLIC cxx_std_14) set(CMAKE_CXX_FLAGS "-pedantic-errors") add_compile_definitions(DAGMC=1) - """.format(openmc_dir))) + """.format( + openmc_dir + ) + ) + ) # Create temporary build directory and change to there - local_builddir = Path('build') + local_builddir = Path("build") local_builddir.mkdir(exist_ok=True) os.chdir(str(local_builddir)) - if config['mpi']: + if config["mpi"]: mpi_arg = "On" else: mpi_arg = "Off" @@ -55,16 +61,18 @@ def cpp_driver(request): try: print("Building driver") # Run cmake/make to build the shared libary - subprocess.run(['cmake', os.path.pardir, f'-DOPENMC_USE_MPI={mpi_arg}'], check=True) - subprocess.run(['make'], check=True) + subprocess.run( + ["cmake", os.path.pardir, f"-DOPENMC_USE_MPI={mpi_arg}"], check=True + ) + subprocess.run(["make"], check=True) os.chdir(os.path.pardir) yield "./build/main" finally: # Remove local build directory when test is complete - shutil.rmtree('build') - os.remove('CMakeLists.txt') + shutil.rmtree("build") + os.remove("CMakeLists.txt") class ExternalMoabTest(PyAPITestHarness): @@ -73,14 +81,15 @@ def __init__(self, executable, statepoint_name, model): self.executable = executable def _run_openmc(self): - if config['mpi']: - mpi_args = [config['mpiexec'], '-n', config['mpi_np']] - openmc.run(openmc_exec=self.executable, - mpi_args=mpi_args, - event_based=config['event']) + if config["mpi"]: + mpi_args = [config["mpiexec"], "-n", config["mpi_np"]] + openmc.run( + openmc_exec=self.executable, + mpi_args=mpi_args, + event_based=config["event"], + ) else: - openmc.run(openmc_exec=self.executable, - event_based=config['event']) + openmc.run(openmc_exec=self.executable, event_based=config["event"]) # Override some methods to do nothing def _get_results(self): @@ -114,28 +123,27 @@ def _compare_results(self): if isinstance(flt.mesh, openmc.UnstructuredMesh): if tally.name == "external mesh tally": - ext_data = tally.get_reshaped_data(value='mean') + ext_data = tally.get_reshaped_data(value="mean") elif tally.name == "unstructured mesh tally": - unstr_data = tally.get_reshaped_data(value='mean') + unstr_data = tally.get_reshaped_data(value="mean") # we expect these results to be the same to within at 8 # decimal places decimals = 8 - np.testing.assert_array_almost_equal(unstr_data, - ext_data, decimals) + np.testing.assert_array_almost_equal(unstr_data, ext_data, decimals) @staticmethod def get_mesh_tally_data(tally): - data = tally.get_reshaped_data(value='mean') - std_dev = tally.get_reshaped_data(value='std_dev') + data = tally.get_reshaped_data(value="mean") + std_dev = tally.get_reshaped_data(value="std_dev") data.shape = (data.size, 1) std_dev.shape = (std_dev.size, 1) return np.sum(data, axis=1), np.sum(std_dev, axis=1) def _cleanup(self): super()._cleanup() - output = glob.glob('tally*.vtk') + output = glob.glob("tally*.vtk") for f in output: if os.path.exists(f): os.remove(f) @@ -148,7 +156,7 @@ def test_external_mesh(cpp_driver): fuel_mat = openmc.Material(name="fuel") fuel_mat.add_nuclide("U235", 1.0) - fuel_mat.set_density('g/cc', 4.5) + fuel_mat.set_density("g/cc", 4.5) materials.append(fuel_mat) zirc_mat = openmc.Material(name="zircaloy") @@ -173,9 +181,14 @@ def test_external_mesh(cpp_driver): fuel_max_z = openmc.ZPlane(5.0, name="maximum z") fuel_cell = openmc.Cell(name="fuel") - fuel_cell.region = +fuel_min_x & -fuel_max_x & \ - +fuel_min_y & -fuel_max_y & \ - +fuel_min_z & -fuel_max_z + fuel_cell.region = ( + +fuel_min_x + & -fuel_max_x + & +fuel_min_y + & -fuel_max_y + & +fuel_min_z + & -fuel_max_z + ) fuel_cell.fill = fuel_mat clad_min_x = openmc.XPlane(-6.0, name="minimum x") @@ -188,44 +201,50 @@ def test_external_mesh(cpp_driver): clad_max_z = openmc.ZPlane(6.0, name="maximum z") clad_cell = openmc.Cell(name="clad") - clad_cell.region = (-fuel_min_x | +fuel_max_x | - -fuel_min_y | +fuel_max_y | - -fuel_min_z | +fuel_max_z) & \ - (+clad_min_x & -clad_max_x & - +clad_min_y & -clad_max_y & - +clad_min_z & -clad_max_z) + clad_cell.region = ( + -fuel_min_x + | +fuel_max_x + | -fuel_min_y + | +fuel_max_y + | -fuel_min_z + | +fuel_max_z + ) & ( + +clad_min_x + & -clad_max_x + & +clad_min_y + & -clad_max_y + & +clad_min_z + & -clad_max_z + ) clad_cell.fill = zirc_mat bounds = (10, 10, 10) - water_min_x = openmc.XPlane(x0=-bounds[0], - name="minimum x", - boundary_type='vacuum') - water_max_x = openmc.XPlane(x0=bounds[0], - name="maximum x", - boundary_type='vacuum') - - water_min_y = openmc.YPlane(y0=-bounds[1], - name="minimum y", - boundary_type='vacuum') - water_max_y = openmc.YPlane(y0=bounds[1], - name="maximum y", - boundary_type='vacuum') - - water_min_z = openmc.ZPlane(z0=-bounds[2], - name="minimum z", - boundary_type='vacuum') - water_max_z = openmc.ZPlane(z0=bounds[2], - name="maximum z", - boundary_type='vacuum') + water_min_x = openmc.XPlane(x0=-bounds[0], name="minimum x", boundary_type="vacuum") + water_max_x = openmc.XPlane(x0=bounds[0], name="maximum x", boundary_type="vacuum") + + water_min_y = openmc.YPlane(y0=-bounds[1], name="minimum y", boundary_type="vacuum") + water_max_y = openmc.YPlane(y0=bounds[1], name="maximum y", boundary_type="vacuum") + + water_min_z = openmc.ZPlane(z0=-bounds[2], name="minimum z", boundary_type="vacuum") + water_max_z = openmc.ZPlane(z0=bounds[2], name="maximum z", boundary_type="vacuum") water_cell = openmc.Cell(name="water") - water_cell.region = (-clad_min_x | +clad_max_x | - -clad_min_y | +clad_max_y | - -clad_min_z | +clad_max_z) & \ - (+water_min_x & -water_max_x & - +water_min_y & -water_max_y & - +water_min_z & -water_max_z) + water_cell.region = ( + -clad_min_x + | +clad_max_x + | -clad_min_y + | +clad_max_y + | -clad_min_z + | +clad_max_z + ) & ( + +water_min_x + & -water_max_x + & +water_min_y + & -water_max_y + & +water_min_z + & -water_max_z + ) water_cell.fill = water_mat # create a containing universe @@ -235,7 +254,7 @@ def test_external_mesh(cpp_driver): mesh_filename = "test_mesh_tets.h5m" # Create a normal unstructured mesh to compare to - uscd_mesh = openmc.UnstructuredMesh(mesh_filename, 'moab') + uscd_mesh = openmc.UnstructuredMesh(mesh_filename, "moab") # Create filters uscd_filter = openmc.MeshFilter(mesh=uscd_mesh) @@ -244,31 +263,28 @@ def test_external_mesh(cpp_driver): tallies = openmc.Tallies() uscd_tally = openmc.Tally(name="unstructured mesh tally") uscd_tally.filters = [uscd_filter] - uscd_tally.scores = ['flux'] - uscd_tally.estimator = 'tracklength' + uscd_tally.scores = ["flux"] + uscd_tally.estimator = "tracklength" tallies.append(uscd_tally) # Settings settings = openmc.Settings() - settings.run_mode = 'fixed source' + settings.run_mode = "fixed source" settings.particles = 100 settings.batches = 10 # Source setup space = openmc.stats.Point() angle = openmc.stats.Monodirectional((-1.0, 0.0, 0.0)) - energy = openmc.stats.Discrete(x=[15.e+06], p=[1.0]) + energy = openmc.stats.Discrete(x=[15.0e06], p=[1.0]) source = openmc.IndependentSource(space=space, energy=energy, angle=angle) settings.source = source - model = openmc.model.Model(geometry=geometry, - materials=materials, - tallies=tallies, - settings=settings) + model = openmc.model.Model( + geometry=geometry, materials=materials, tallies=tallies, settings=settings + ) - harness = ExternalMoabTest(cpp_driver, - 'statepoint.10.h5', - model) + harness = ExternalMoabTest(cpp_driver, "statepoint.10.h5", model) # Run open MC and check results harness.main() diff --git a/tests/regression_tests/filter_cellfrom/test.py b/tests/regression_tests/filter_cellfrom/test.py index 5559b4c8171..21f33208e90 100644 --- a/tests/regression_tests/filter_cellfrom/test.py +++ b/tests/regression_tests/filter_cellfrom/test.py @@ -95,9 +95,12 @@ def model(): # Surfaces box1_rpp = openmc.model.RectangularParallelepiped( - -box1_size / 2.0, box1_size / 2.0, - -box1_size / 2.0, box1_size / 2.0, - -box1_size / 2.0, box1_size / 2.0, + -box1_size / 2.0, + box1_size / 2.0, + -box1_size / 2.0, + box1_size / 2.0, + -box1_size / 2.0, + box1_size / 2.0, ) # Cell @@ -112,10 +115,13 @@ def model(): # Surfaces box2_rpp = openmc.model.RectangularParallelepiped( - -box2_size / 2.0, box2_size / 2.0, - -box2_size / 2.0, box2_size / 2.0, - -box2_size / 2.0, box2_size / 2.0, - boundary_type="vacuum" + -box2_size / 2.0, + box2_size / 2.0, + -box2_size / 2.0, + box2_size / 2.0, + -box2_size / 2.0, + box2_size / 2.0, + boundary_type="vacuum", ) # Cell @@ -144,7 +150,8 @@ def model(): ] distribution = openmc.stats.Box(bounds[:3], bounds[3:]) model.settings.source = openmc.IndependentSource( - space=distribution, constraints={'fissionable': True}) + space=distribution, constraints={"fissionable": True} + ) # ============================================================================= # Tallies diff --git a/tests/regression_tests/filter_cellinstance/test.py b/tests/regression_tests/filter_cellinstance/test.py index 61f17d88a66..794a16e9287 100644 --- a/tests/regression_tests/filter_cellinstance/test.py +++ b/tests/regression_tests/filter_cellinstance/test.py @@ -16,9 +16,13 @@ def _compare_results(self): t1 = sp.tallies[1] f1 = sp.filters[1] c2_bins = [tuple(tuple(i) for i in f1.bins if i[0] == 2)] - c2_mean = t1.get_values(filters=[openmc.CellInstanceFilter], filter_bins=c2_bins) + c2_mean = t1.get_values( + filters=[openmc.CellInstanceFilter], filter_bins=c2_bins + ) c3_bins = [tuple(tuple(i) for i in f1.bins if i[0] == 3)] - c3_mean = t1.get_values(filters=[openmc.CellInstanceFilter], filter_bins=c3_bins) + c3_mean = t1.get_values( + filters=[openmc.CellInstanceFilter], filter_bins=c3_bins + ) assert_array_almost_equal(c2_mean, c3_mean) return super()._compare_results() @@ -30,11 +34,11 @@ def model(): # Materials m1 = openmc.Material() - m1.set_density('g/cc', 4.5) - m1.add_nuclide('U235', 1.0) + m1.set_density("g/cc", 4.5) + m1.add_nuclide("U235", 1.0) m2 = openmc.Material() - m2.set_density('g/cc', 1.0) - m2.add_nuclide('H1', 1.0) + m2.set_density("g/cc", 1.0) + m2.add_nuclide("H1", 1.0) model.materials += [m1, m2] # Geometry @@ -58,9 +62,9 @@ def model(): [u2, u3, u3, u3], [u3, u2, u3, u3], [u3, u3, u2, u3], - [u3, u3, u3, u2] + [u3, u3, u3, u2], ] - box = openmc.model.RectangularPrism(8.0, 8.0, boundary_type='reflective') + box = openmc.model.RectangularPrism(8.0, 8.0, boundary_type="reflective") main_cell = openmc.Cell(fill=lat, region=-box) model.geometry.root_universe = openmc.Universe(cells=[main_cell]) model.geometry.determine_paths() @@ -71,22 +75,24 @@ def model(): model.settings.particles = 1000 model.settings.source = openmc.IndependentSource(space=openmc.stats.Point()) - instances = ([(c4, i) for i in range(c4.num_instances)] + - [(c2, i) for i in range(c2.num_instances)] + - [(c3, i) for i in range(c3.num_instances)]) + instances = ( + [(c4, i) for i in range(c4.num_instances)] + + [(c2, i) for i in range(c2.num_instances)] + + [(c3, i) for i in range(c3.num_instances)] + ) f1 = openmc.CellInstanceFilter(instances) f2 = openmc.CellInstanceFilter(instances[::-1]) t1 = openmc.Tally() t1.filters = [f1] - t1.scores = ['total'] + t1.scores = ["total"] t2 = openmc.Tally() t2.filters = [f2] - t2.scores = ['total'] + t2.scores = ["total"] model.tallies += [t1, t2] return model def test_cell_instance(model): - harness = CellInstanceFilterTest('statepoint.5.h5', model) + harness = CellInstanceFilterTest("statepoint.5.h5", model) harness.main() diff --git a/tests/regression_tests/filter_distribcell/test.py b/tests/regression_tests/filter_distribcell/test.py index 028c6a7790d..a9f437e95d7 100644 --- a/tests/regression_tests/filter_distribcell/test.py +++ b/tests/regression_tests/filter_distribcell/test.py @@ -12,9 +12,13 @@ def execute_test(self): """Run OpenMC with the appropriate arguments and check the outputs.""" base_dir = os.getcwd() try: - dirs = ('case-1', '../case-2', '../case-3', '../case-4') - sps = ('statepoint.1.*', 'statepoint.1.*', 'statepoint.3.*', - 'statepoint.1.*') + dirs = ("case-1", "../case-2", "../case-3", "../case-4") + sps = ( + "statepoint.1.*", + "statepoint.1.*", + "statepoint.3.*", + "statepoint.1.*", + ) tallies_out_present = (True, True, False, True) hash_out = (False, False, True, False) for i in range(len(dirs)): @@ -36,9 +40,13 @@ def update_results(self): """Update the results_true using the current version of OpenMC.""" base_dir = os.getcwd() try: - dirs = ('case-1', '../case-2', '../case-3', '../case-4') - sps = ('statepoint.1.h5', 'statepoint.1.h5', 'statepoint.3.h5', - 'statepoint.1.h5') + dirs = ("case-1", "../case-2", "../case-3", "../case-4") + sps = ( + "statepoint.1.h5", + "statepoint.1.h5", + "statepoint.3.h5", + "statepoint.1.h5", + ) tallies_out_present = (True, True, False, True) hash_out = (False, False, True, False) for i in range(len(dirs)): @@ -59,13 +67,12 @@ def update_results(self): def _test_output_created(self, tallies_out_present): """Make sure statepoint.* and tallies.out have been created.""" statepoint = glob.glob(os.path.join(os.getcwd(), self._sp_name)) - assert len(statepoint) == 1, 'Either multiple or no statepoint files ' \ - 'exist.' - assert statepoint[0].endswith('h5'), \ - 'Statepoint file is not a HDF5 file.' + assert len(statepoint) == 1, "Either multiple or no statepoint files " "exist." + assert statepoint[0].endswith("h5"), "Statepoint file is not a HDF5 file." if tallies_out_present: - assert os.path.exists(os.path.join(os.getcwd(), 'tallies.out')), \ - 'Tally output file does not exist.' + assert os.path.exists( + os.path.join(os.getcwd(), "tallies.out") + ), "Tally output file does not exist." def test_filter_distribcell(): diff --git a/tests/regression_tests/filter_energyfun/test.py b/tests/regression_tests/filter_energyfun/test.py index 295b8ebd8ff..975dd0a408d 100644 --- a/tests/regression_tests/filter_energyfun/test.py +++ b/tests/regression_tests/filter_energyfun/test.py @@ -9,11 +9,11 @@ def model(): model = openmc.model.Model() m = openmc.Material() - m.set_density('g/cm3', 10.0) - m.add_nuclide('Am241', 1.0) + m.set_density("g/cm3", 10.0) + m.add_nuclide("Am241", 1.0) model.materials.append(m) - s = openmc.Sphere(r=100.0, boundary_type='vacuum') + s = openmc.Sphere(r=100.0, boundary_type="vacuum") c = openmc.Cell(fill=m, region=-s) model.geometry = openmc.Geometry([c]) @@ -29,25 +29,25 @@ def model(): filt1 = openmc.EnergyFunctionFilter(x, y) # check interpolatoin property setter - assert filt1.interpolation == 'linear-linear' + assert filt1.interpolation == "linear-linear" with pytest.raises(ValueError): - filt1.interpolation = 'šŸ„' + filt1.interpolation = "šŸ„" # Also make a filter with the .from_tabulated1d constructor. Make sure # the filters are identical. tab1d = openmc.data.Tabulated1D(x, y) filt2 = openmc.EnergyFunctionFilter.from_tabulated1d(tab1d) - assert filt1 == filt2, 'Error with the .from_tabulated1d constructor' + assert filt1 == filt2, "Error with the .from_tabulated1d constructor" filt3 = openmc.EnergyFunctionFilter(x, y) - filt3.interpolation = 'log-log' + filt3.interpolation = "log-log" filt4 = openmc.EnergyFunctionFilter(x, y) - filt4.interpolation = 'linear-log' + filt4.interpolation = "linear-log" filt5 = openmc.EnergyFunctionFilter(x, y) - filt5.interpolation = 'log-linear' + filt5.interpolation = "log-linear" # define a trapezoidal function for comparison x = [0.0, 5e6, 1e7, 1.5e7] @@ -56,37 +56,34 @@ def model(): filt6 = openmc.EnergyFunctionFilter(x, y) filt7 = openmc.EnergyFunctionFilter(x, y) - filt7.interpolation = 'quadratic' + filt7.interpolation = "quadratic" filt8 = openmc.EnergyFunctionFilter(x, y) - filt8.interpolation = 'cubic' + filt8.interpolation = "cubic" filt9 = openmc.EnergyFunctionFilter(x, y) - filt9.interpolation = 'histogram' + filt9.interpolation = "histogram" filters = [filt1, filt3, filt4, filt5, filt6, filt7, filt8, filt9] # Make tallies tallies = [openmc.Tally() for _ in range(len(filters) + 1)] for t in tallies: - t.scores = ['(n,gamma)'] - t.nuclides = ['Am241'] + t.scores = ["(n,gamma)"] + t.nuclides = ["Am241"] for t, f in zip(tallies[1:], filters): t.filters = [f] model.tallies.extend(tallies) - interpolation_vals = \ - list(openmc.EnergyFunctionFilter.INTERPOLATION_SCHEMES.keys()) + interpolation_vals = list(openmc.EnergyFunctionFilter.INTERPOLATION_SCHEMES.keys()) for i_val in interpolation_vals: # breakpoint here is fake and unused - t1d = openmc.data.Tabulated1D(x, - y, - breakpoints=[1], - interpolation=[i_val]) + t1d = openmc.data.Tabulated1D(x, y, breakpoints=[1], interpolation=[i_val]) f = openmc.EnergyFunctionFilter.from_tabulated1d(t1d) - assert f.interpolation == \ - openmc.EnergyFunctionFilter.INTERPOLATION_SCHEMES[i_val] + assert ( + f.interpolation == openmc.EnergyFunctionFilter.INTERPOLATION_SCHEMES[i_val] + ) return model @@ -99,11 +96,11 @@ def _get_results(self): dataframes_string = "" # Use tally arithmetic to compute the branching ratio. br_tally = sp.tallies[2] / sp.tallies[1] - dataframes_string += br_tally.get_pandas_dataframe().to_string() + '\n' + dataframes_string += br_tally.get_pandas_dataframe().to_string() + "\n" for t_id in (3, 4, 5, 6): ef_tally = sp.tallies[t_id] - dataframes_string += ef_tally.get_pandas_dataframe().to_string() + '\n' + dataframes_string += ef_tally.get_pandas_dataframe().to_string() + "\n" # Output the tally in a Pandas DataFrame. return dataframes_string @@ -121,9 +118,11 @@ def _compare_results(self): sp_lin_lin_filt = sp_lin_lin_tally.find_filter(openmc.EnergyFunctionFilter) model_lin_lin_tally = self._model.tallies[1] - model_lin_lin_filt = model_lin_lin_tally.find_filter(openmc.EnergyFunctionFilter) + model_lin_lin_filt = model_lin_lin_tally.find_filter( + openmc.EnergyFunctionFilter + ) - assert sp_lin_lin_filt.interpolation == 'linear-linear' + assert sp_lin_lin_filt.interpolation == "linear-linear" assert all(sp_lin_lin_filt.energy == model_lin_lin_filt.energy) assert all(sp_lin_lin_filt.y == model_lin_lin_filt.y) @@ -132,9 +131,11 @@ def _compare_results(self): sp_log_log_filt = sp_log_log_tally.find_filter(openmc.EnergyFunctionFilter) model_log_log_tally = self._model.tallies[2] - model_log_log_filt = model_log_log_tally.find_filter(openmc.EnergyFunctionFilter) + model_log_log_filt = model_log_log_tally.find_filter( + openmc.EnergyFunctionFilter + ) - assert sp_log_log_filt.interpolation == 'log-log' + assert sp_log_log_filt.interpolation == "log-log" assert all(sp_log_log_filt.energy == model_log_log_filt.energy) assert all(sp_log_log_filt.y == model_log_log_filt.y) @@ -144,11 +145,11 @@ def _compare_results(self): sp_lin_log_tally = self._model.tallies[3] sp_lin_log_filt = sp_lin_log_tally.find_filter(openmc.EnergyFunctionFilter) - assert sp_lin_log_filt.interpolation == 'linear-log' + assert sp_lin_log_filt.interpolation == "linear-log" sp_log_lin_tally = self._model.tallies[4] sp_log_lin_filt = sp_log_lin_tally.find_filter(openmc.EnergyFunctionFilter) - assert sp_log_lin_filt.interpolation == 'log-linear' + assert sp_log_lin_filt.interpolation == "log-linear" # check that the cubic interpolation provides a higher value # than linear-linear @@ -164,6 +165,7 @@ def _compare_results(self): assert all(histogram_tally.mean < contrived_quadratic_tally.mean) assert all(histogram_tally.mean < contrived_cubic_tally.mean) + def test_filter_energyfun(model): - harness = FilterEnergyFunHarness('statepoint.5.h5', model) - harness.main() \ No newline at end of file + harness = FilterEnergyFunHarness("statepoint.5.h5", model) + harness.main() diff --git a/tests/regression_tests/filter_mesh/test.py b/tests/regression_tests/filter_mesh/test.py index 6214e0486c0..8697df39dcb 100644 --- a/tests/regression_tests/filter_mesh/test.py +++ b/tests/regression_tests/filter_mesh/test.py @@ -12,17 +12,17 @@ def model(): model = openmc.model.Model() fuel = openmc.Material() - fuel.set_density('g/cm3', 10.0) - fuel.add_nuclide('U235', 1.0) + fuel.set_density("g/cm3", 10.0) + fuel.add_nuclide("U235", 1.0) zr = openmc.Material() - zr.set_density('g/cm3', 1.0) - zr.add_nuclide('Zr90', 1.0) + zr.set_density("g/cm3", 1.0) + zr.add_nuclide("Zr90", 1.0) model.materials.extend([fuel, zr]) box1 = openmc.model.RectangularPrism(10.0, 10.0) - box2 = openmc.model.RectangularPrism(20.0, 20.0, boundary_type='reflective') - top = openmc.ZPlane(z0=10.0, boundary_type='vacuum') - bottom = openmc.ZPlane(z0=-10.0, boundary_type='vacuum') + box2 = openmc.model.RectangularPrism(20.0, 20.0, boundary_type="reflective") + top = openmc.ZPlane(z0=10.0, boundary_type="vacuum") + bottom = openmc.ZPlane(z0=-10.0, boundary_type="vacuum") cell1 = openmc.Cell(fill=fuel, region=-box1 & +bottom & -top) cell2 = openmc.Cell(fill=zr, region=+box1 & -box2 & +bottom & -top) model.geometry = openmc.Geometry([cell1, cell2]) @@ -47,7 +47,7 @@ def model(): mesh_3d.lower_left = [-7.5, -7.5, -7.5] mesh_3d.upper_right = [7.5, 7.5, 7.5] dx = dy = dz = 15 / 5 - reg_mesh_exp_vols = np.full(mesh_3d.dimension, dx*dy*dz) + reg_mesh_exp_vols = np.full(mesh_3d.dimension, dx * dy * dz) np.testing.assert_equal(mesh_3d.volumes, reg_mesh_exp_vols) recti_mesh = openmc.RectilinearMesh() @@ -56,17 +56,17 @@ def model(): recti_mesh.z_grid = np.logspace(0, np.log10(7.5), 11) dx = dy = 15 / 17 dz = np.diff(np.logspace(0, np.log10(7.5), 11)) - dxdy = np.full(recti_mesh.dimension[:2], dx*dy) + dxdy = np.full(recti_mesh.dimension[:2], dx * dy) recti_mesh_exp_vols = np.multiply.outer(dxdy, dz) np.testing.assert_allclose(recti_mesh.volumes, recti_mesh_exp_vols) cyl_mesh = openmc.CylindricalMesh( r_grid=np.linspace(0, 7.5, 18), - phi_grid=np.linspace(0, 2*pi, 19), + phi_grid=np.linspace(0, 2 * pi, 19), z_grid=np.linspace(-7.5, 7.5, 17), ) - dr = 0.5 * np.diff(np.linspace(0, 7.5, 18)**2) - dp = np.full(cyl_mesh.dimension[1], 2*pi / 18) + dr = 0.5 * np.diff(np.linspace(0, 7.5, 18) ** 2) + dp = np.full(cyl_mesh.dimension[1], 2 * pi / 18) dz = np.full(cyl_mesh.dimension[2], 15 / 16) drdp = np.outer(dr, dp) cyl_mesh_exp_vols = np.multiply.outer(drdp, dz) @@ -75,11 +75,11 @@ def model(): sph_mesh = openmc.SphericalMesh( r_grid=np.linspace(0, 7.5, 18), theta_grid=np.linspace(0, pi, 9), - phi_grid=np.linspace(0, 2*pi, 19) + phi_grid=np.linspace(0, 2 * pi, 19), ) - dr = np.diff(np.linspace(0, 7.5, 18)**3) / 3 + dr = np.diff(np.linspace(0, 7.5, 18) ** 3) / 3 dt = np.diff(-np.cos(np.linspace(0, pi, 9))) - dp = np.full(sph_mesh.dimension[2], 2*pi / 18) + dp = np.full(sph_mesh.dimension[2], 2 * pi / 18) drdt = np.outer(dr, dt) sph_mesh_exp_vols = np.multiply.outer(drdt, dp) np.testing.assert_allclose(sph_mesh.volumes, sph_mesh_exp_vols) @@ -91,7 +91,7 @@ def model(): openmc.MeshFilter(mesh_3d), openmc.MeshFilter(recti_mesh), openmc.MeshFilter(cyl_mesh), - openmc.MeshFilter(sph_mesh) + openmc.MeshFilter(sph_mesh), ] surf_filters = [ openmc.MeshSurfaceFilter(mesh_1d), @@ -99,23 +99,23 @@ def model(): openmc.MeshSurfaceFilter(mesh_3d), openmc.MeshSurfaceFilter(recti_mesh), openmc.MeshSurfaceFilter(cyl_mesh), - openmc.MeshSurfaceFilter(sph_mesh) + openmc.MeshSurfaceFilter(sph_mesh), ] # Create tallies for f1, f2 in zip(reg_filters, surf_filters): tally = openmc.Tally() tally.filters = [f1] - tally.scores = ['total'] + tally.scores = ["total"] model.tallies.append(tally) tally = openmc.Tally() tally.filters = [f2] - tally.scores = ['current'] + tally.scores = ["current"] model.tallies.append(tally) return model def test_filter_mesh(model): - harness = HashedPyAPITestHarness('statepoint.5.h5', model) + harness = HashedPyAPITestHarness("statepoint.5.h5", model) harness.main() diff --git a/tests/regression_tests/filter_meshborn/test.py b/tests/regression_tests/filter_meshborn/test.py index ff4adbc9f04..e8aa5c97542 100644 --- a/tests/regression_tests/filter_meshborn/test.py +++ b/tests/regression_tests/filter_meshborn/test.py @@ -36,10 +36,10 @@ def model(): model.geometry = openmc.Geometry([core]) # Settings - model.settings.run_mode = 'fixed source' + model.settings.run_mode = "fixed source" model.settings.particles = 2000 model.settings.batches = 8 - distribution = openmc.stats.Box((0., -r, -r), (r, r, r)) + distribution = openmc.stats.Box((0.0, -r, -r), (r, r, r)) model.settings.source = openmc.IndependentSource(space=distribution) # Tallies diff --git a/tests/regression_tests/filter_musurface/test.py b/tests/regression_tests/filter_musurface/test.py index f2ec96b495d..4196502258a 100644 --- a/tests/regression_tests/filter_musurface/test.py +++ b/tests/regression_tests/filter_musurface/test.py @@ -11,14 +11,14 @@ def model(): model = openmc.Model() fuel = openmc.Material() - fuel.set_density('g/cm3', 10.0) - fuel.add_nuclide('U235', 1.0) + fuel.set_density("g/cm3", 10.0) + fuel.add_nuclide("U235", 1.0) zr = openmc.Material() - zr.set_density('g/cm3', 1.0) - zr.add_nuclide('Zr90', 1.0) + zr.set_density("g/cm3", 1.0) + zr.add_nuclide("Zr90", 1.0) cyl1 = openmc.ZCylinder(r=1.0) - cyl2 = openmc.ZCylinder(r=3.0, boundary_type='vacuum') + cyl2 = openmc.ZCylinder(r=3.0, boundary_type="vacuum") cell1 = openmc.Cell(fill=fuel, region=-cyl1) cell2 = openmc.Cell(fill=zr, region=+cyl1 & -cyl2) model.geometry = openmc.Geometry([cell1, cell2]) @@ -32,12 +32,12 @@ def model(): mu_filter = openmc.MuSurfaceFilter([-1.0, -0.5, 0.0, 0.5, 1.0]) tally = openmc.Tally() tally.filters = [surf_filter, mu_filter] - tally.scores = ['current'] + tally.scores = ["current"] model.tallies.append(tally) return model def test_filter_musurface(model): - harness = PyAPITestHarness('statepoint.5.h5', model) + harness = PyAPITestHarness("statepoint.5.h5", model) harness.main() diff --git a/tests/regression_tests/filter_translations/test.py b/tests/regression_tests/filter_translations/test.py index 4f0fe7141bf..d754b3ddaac 100644 --- a/tests/regression_tests/filter_translations/test.py +++ b/tests/regression_tests/filter_translations/test.py @@ -12,17 +12,17 @@ def model(): model = openmc.model.Model() fuel = openmc.Material() - fuel.set_density('g/cm3', 10.0) - fuel.add_nuclide('U235', 1.0) + fuel.set_density("g/cm3", 10.0) + fuel.add_nuclide("U235", 1.0) zr = openmc.Material() - zr.set_density('g/cm3', 1.0) - zr.add_nuclide('Zr90', 1.0) + zr.set_density("g/cm3", 1.0) + zr.add_nuclide("Zr90", 1.0) model.materials.extend([fuel, zr]) box1 = openmc.model.RectangularPrism(10.0, 10.0) - box2 = openmc.model.RectangularPrism(20.0, 20.0, boundary_type='reflective') - top = openmc.ZPlane(z0=10.0, boundary_type='vacuum') - bottom = openmc.ZPlane(z0=-10.0, boundary_type='vacuum') + box2 = openmc.model.RectangularPrism(20.0, 20.0, boundary_type="reflective") + top = openmc.ZPlane(z0=10.0, boundary_type="vacuum") + bottom = openmc.ZPlane(z0=-10.0, boundary_type="vacuum") cell1 = openmc.Cell(fill=fuel, region=-box1 & +bottom & -top) cell2 = openmc.Cell(fill=zr, region=+box1 & -box2 & +bottom & -top) model.geometry = openmc.Geometry([cell1, cell2]) @@ -79,12 +79,12 @@ def model(): for f in filters: tally = openmc.Tally() tally.filters = [f] - tally.scores = ['total'] + tally.scores = ["total"] model.tallies.append(tally) return model def test_filter_mesh_translations(model): - harness = PyAPITestHarness('statepoint.5.h5', model) + harness = PyAPITestHarness("statepoint.5.h5", model) harness.main() diff --git a/tests/regression_tests/fixed_source/test.py b/tests/regression_tests/fixed_source/test.py index b9ee2512536..1c8bdc2ef8a 100644 --- a/tests/regression_tests/fixed_source/test.py +++ b/tests/regression_tests/fixed_source/test.py @@ -10,50 +10,53 @@ class FixedSourceTestHarness(PyAPITestHarness): def _get_results(self): """Digest info in the statepoint and return as a string.""" # Read the statepoint file. - outstr = '' + outstr = "" with openmc.StatePoint(self._sp_name) as sp: # Write out tally data. for i, tally_ind in enumerate(sp.tallies): tally = sp.tallies[tally_ind] - results = np.zeros((tally.sum.size*2, )) + results = np.zeros((tally.sum.size * 2,)) results[0::2] = tally.sum.ravel() results[1::2] = tally.sum_sq.ravel() - results = ['{0:12.6E}'.format(x) for x in results] + results = ["{0:12.6E}".format(x) for x in results] - outstr += 'tally ' + str(i + 1) + ':\n' - outstr += '\n'.join(results) + '\n' + outstr += "tally " + str(i + 1) + ":\n" + outstr += "\n".join(results) + "\n" gt = sp.global_tallies - outstr += 'leakage:\n' - outstr += '{0:12.6E}'.format(gt[gt['name'] == b'leakage'][0]['sum']) + '\n' - outstr += '{0:12.6E}'.format(gt[gt['name'] == b'leakage'][0]['sum_sq']) + '\n' + outstr += "leakage:\n" + outstr += "{0:12.6E}".format(gt[gt["name"] == b"leakage"][0]["sum"]) + "\n" + outstr += ( + "{0:12.6E}".format(gt[gt["name"] == b"leakage"][0]["sum_sq"]) + "\n" + ) return outstr def test_fixed_source(): mat = openmc.Material() - mat.add_nuclide('O16', 1.0) - mat.add_nuclide('U238', 0.0001) - mat.set_density('g/cc', 7.5) + mat.add_nuclide("O16", 1.0) + mat.add_nuclide("U238", 0.0001) + mat.set_density("g/cc", 7.5) - surf = openmc.Sphere(r=10.0, boundary_type='vacuum') + surf = openmc.Sphere(r=10.0, boundary_type="vacuum") cell = openmc.Cell(fill=mat, region=-surf) model = openmc.model.Model() model.geometry.root_universe = openmc.Universe(cells=[cell]) model.materials.append(mat) - model.settings.run_mode = 'fixed source' + model.settings.run_mode = "fixed source" model.settings.batches = 10 model.settings.particles = 100 - model.settings.temperature = {'default': 294} - model.settings.source = openmc.IndependentSource(space=openmc.stats.Point(), - strength=10.0) + model.settings.temperature = {"default": 294} + model.settings.source = openmc.IndependentSource( + space=openmc.stats.Point(), strength=10.0 + ) tally = openmc.Tally() - tally.scores = ['flux'] + tally.scores = ["flux"] model.tallies.append(tally) - harness = FixedSourceTestHarness('statepoint.10.h5', model) + harness = FixedSourceTestHarness("statepoint.10.h5", model) harness.main() diff --git a/tests/regression_tests/infinite_cell/test.py b/tests/regression_tests/infinite_cell/test.py index 59abec30037..0ff27fa7019 100644 --- a/tests/regression_tests/infinite_cell/test.py +++ b/tests/regression_tests/infinite_cell/test.py @@ -2,5 +2,5 @@ def test_infinite_cell(): - harness = TestHarness('statepoint.10.h5') + harness = TestHarness("statepoint.10.h5") harness.main() diff --git a/tests/regression_tests/iso_in_lab/test.py b/tests/regression_tests/iso_in_lab/test.py index 0e8edc21860..33de7c846d8 100644 --- a/tests/regression_tests/iso_in_lab/test.py +++ b/tests/regression_tests/iso_in_lab/test.py @@ -3,6 +3,6 @@ def test_iso_in_lab(): # Force iso-in-lab scattering. - harness = PyAPITestHarness('statepoint.10.h5') + harness = PyAPITestHarness("statepoint.10.h5") harness._model.materials.make_isotropic_in_lab() harness.main() diff --git a/tests/regression_tests/lattice/test.py b/tests/regression_tests/lattice/test.py index a32b9a629c6..2d5e5cda78f 100644 --- a/tests/regression_tests/lattice/test.py +++ b/tests/regression_tests/lattice/test.py @@ -2,5 +2,5 @@ def test_lattice(): - harness = TestHarness('statepoint.10.h5') + harness = TestHarness("statepoint.10.h5") harness.main() diff --git a/tests/regression_tests/lattice_hex/test.py b/tests/regression_tests/lattice_hex/test.py index eb63f84df54..ac059610e45 100644 --- a/tests/regression_tests/lattice_hex/test.py +++ b/tests/regression_tests/lattice_hex/test.py @@ -2,5 +2,5 @@ def test_lattice_hex(): - harness = TestHarness('statepoint.10.h5') + harness = TestHarness("statepoint.10.h5") harness.main() diff --git a/tests/regression_tests/lattice_hex_coincident/test.py b/tests/regression_tests/lattice_hex_coincident/test.py index f971098c06a..df5331f2963 100644 --- a/tests/regression_tests/lattice_hex_coincident/test.py +++ b/tests/regression_tests/lattice_hex_coincident/test.py @@ -11,53 +11,53 @@ def __init__(self, *args, **kwargs): materials = openmc.Materials() fuel_mat = openmc.Material() - fuel_mat.add_nuclide('U235', 4.9817E-03, 'ao') + fuel_mat.add_nuclide("U235", 4.9817e-03, "ao") materials.append(fuel_mat) matrix = openmc.Material() - matrix.set_density('atom/b-cm', 1.7742E-02) - matrix.add_element('C', 1.0, 'ao') - matrix.add_s_alpha_beta('c_Graphite') + matrix.set_density("atom/b-cm", 1.7742e-02) + matrix.add_element("C", 1.0, "ao") + matrix.add_s_alpha_beta("c_Graphite") materials.append(matrix) lead = openmc.Material(name="Lead") - lead.set_density('g/cm3', 10.32) - lead.add_nuclide('Pb204', 0.014, 'ao') - lead.add_nuclide('Pb206', 0.241, 'ao') - lead.add_nuclide('Pb207', 0.221, 'ao') - lead.add_nuclide('Pb208', 0.524, 'ao') + lead.set_density("g/cm3", 10.32) + lead.add_nuclide("Pb204", 0.014, "ao") + lead.add_nuclide("Pb206", 0.241, "ao") + lead.add_nuclide("Pb207", 0.221, "ao") + lead.add_nuclide("Pb208", 0.524, "ao") materials.append(lead) coolant = openmc.Material() - coolant.set_density('atom/b-cm', 5.4464E-04) - coolant.add_nuclide('He4', 1.0, 'ao') + coolant.set_density("atom/b-cm", 5.4464e-04) + coolant.add_nuclide("He4", 1.0, "ao") materials.append(coolant) zirc = openmc.Material(name="Zirc4") - zirc.add_nuclide('Zr90', 2.217E-02, 'ao') - zirc.add_nuclide('Zr91', 4.781E-03, 'ao') - zirc.add_nuclide('Zr92', 7.228E-03, 'ao') - zirc.add_nuclide('Zr94', 7.169E-03, 'ao') - zirc.add_nuclide('Zr96', 1.131E-03, 'ao') + zirc.add_nuclide("Zr90", 2.217e-02, "ao") + zirc.add_nuclide("Zr91", 4.781e-03, "ao") + zirc.add_nuclide("Zr92", 7.228e-03, "ao") + zirc.add_nuclide("Zr94", 7.169e-03, "ao") + zirc.add_nuclide("Zr96", 1.131e-03, "ao") materials.append(zirc) self._model.materials = materials ### Geometry ### - pin_rad = 0.7 # cm - assembly_pitch = 1.4 # cm - - cool_rad = 0.293 # cm - zirc_clad_thickness = 0.057 # cm - zirc_ir = cool_rad # cm - zirc_or = cool_rad + zirc_clad_thickness # cm - lead_thickness = 0.002 # cm - lead_ir = zirc_or # cm - lead_or = zirc_or + lead_thickness # cm - - cyl = openmc.ZCylinder(x0=0., y0=0., r=pin_rad) - fuel_btm = openmc.ZPlane(z0=0.0, boundary_type = 'reflective') - fuel_top = openmc.ZPlane(z0=10.0, boundary_type = 'reflective') + pin_rad = 0.7 # cm + assembly_pitch = 1.4 # cm + + cool_rad = 0.293 # cm + zirc_clad_thickness = 0.057 # cm + zirc_ir = cool_rad # cm + zirc_or = cool_rad + zirc_clad_thickness # cm + lead_thickness = 0.002 # cm + lead_ir = zirc_or # cm + lead_or = zirc_or + lead_thickness # cm + + cyl = openmc.ZCylinder(x0=0.0, y0=0.0, r=pin_rad) + fuel_btm = openmc.ZPlane(z0=0.0, boundary_type="reflective") + fuel_top = openmc.ZPlane(z0=10.0, boundary_type="reflective") region = -cyl & +fuel_btm & -fuel_top container = openmc.Cell(region=region) @@ -95,22 +95,28 @@ def __init__(self, *args, **kwargs): coolant_univ = openmc.Universe(name="coolant universe") coolant_univ.add_cells(coolant_channel) - half_width = assembly_pitch # cm - edge_length = (2./sqrt(3.0)) * half_width + half_width = assembly_pitch # cm + edge_length = (2.0 / sqrt(3.0)) * half_width inf_mat = openmc.Cell() inf_mat.fill = matrix - inf_mat_univ = openmc.Universe(cells=[inf_mat,]) + inf_mat_univ = openmc.Universe( + cells=[ + inf_mat, + ] + ) # a hex surface for the core to go inside of - hexprism = openmc.model.HexagonalPrism(edge_length=edge_length, - origin=(0.0, 0.0), - boundary_type = 'reflective', - orientation='x') + hexprism = openmc.model.HexagonalPrism( + edge_length=edge_length, + origin=(0.0, 0.0), + boundary_type="reflective", + orientation="x", + ) pincell_only_lattice = openmc.HexLattice(name="regular fuel assembly") - pincell_only_lattice.center = (0., 0.) + pincell_only_lattice.center = (0.0, 0.0) pincell_only_lattice.pitch = (assembly_pitch,) pincell_only_lattice.outer = inf_mat_univ @@ -123,14 +129,19 @@ def __init__(self, *args, **kwargs): pincell_only_cell.region = -hexprism & +fuel_btm & -fuel_top pincell_only_cell.fill = pincell_only_lattice - root_univ = openmc.Universe(name="root universe", cells=[pincell_only_cell,]) + root_univ = openmc.Universe( + name="root universe", + cells=[ + pincell_only_cell, + ], + ) self._model.geometry = openmc.Geometry(root_univ) ### Settings ### settings = openmc.Settings() - settings.run_mode = 'eigenvalue' + settings.run_mode = "eigenvalue" source = openmc.IndependentSource() corner_dist = sqrt(2) * pin_rad @@ -139,14 +150,14 @@ def __init__(self, *args, **kwargs): source.space = openmc.stats.Box(ll, ur) source.strength = 1.0 settings.source = source - settings.output = {'summary' : False} + settings.output = {"summary": False} settings.batches = 5 settings.inactive = 2 settings.particles = 1000 settings.seed = 22 self._model.settings = settings + def test_lattice_hex_coincident_surf(): - harness = HexLatticeCoincidentTestHarness('statepoint.5.h5', - model=openmc.Model()) + harness = HexLatticeCoincidentTestHarness("statepoint.5.h5", model=openmc.Model()) harness.main() diff --git a/tests/regression_tests/lattice_hex_x/test.py b/tests/regression_tests/lattice_hex_x/test.py index dd5c53d0c02..cc1aeab6c13 100644 --- a/tests/regression_tests/lattice_hex_x/test.py +++ b/tests/regression_tests/lattice_hex_x/test.py @@ -9,40 +9,40 @@ def __init__(self, *args, **kwargs): materials = openmc.Materials() fuel_mat = openmc.Material(material_id=1, name="UO2") - fuel_mat.set_density('sum') - fuel_mat.add_nuclide('U235', 0.87370e-03) - fuel_mat.add_nuclide('U238', 1.87440e-02) - fuel_mat.add_nuclide('O16', 3.92350e-02) + fuel_mat.set_density("sum") + fuel_mat.add_nuclide("U235", 0.87370e-03) + fuel_mat.add_nuclide("U238", 1.87440e-02) + fuel_mat.add_nuclide("O16", 3.92350e-02) materials.append(fuel_mat) coolant = openmc.Material(material_id=2, name="borated H2O") - coolant.set_density('sum') - coolant.add_nuclide('H1', 0.06694) - coolant.add_nuclide('O16', 0.03347) - coolant.add_nuclide('B10', 6.6262e-6) - coolant.add_nuclide('B11', 2.6839e-5) + coolant.set_density("sum") + coolant.add_nuclide("H1", 0.06694) + coolant.add_nuclide("O16", 0.03347) + coolant.add_nuclide("B10", 6.6262e-6) + coolant.add_nuclide("B11", 2.6839e-5) materials.append(coolant) absorber = openmc.Material(material_id=3, name="pellet B4C") - absorber.set_density('sum') - absorber.add_nuclide('C0', 0.01966) - absorber.add_nuclide('B11', 4.7344e-6) - absorber.add_nuclide('B10', 1.9177e-5) + absorber.set_density("sum") + absorber.add_nuclide("C0", 0.01966) + absorber.add_nuclide("B11", 4.7344e-6) + absorber.add_nuclide("B10", 1.9177e-5) materials.append(absorber) zirc = openmc.Material(material_id=4, name="Zirc4") - zirc.set_density('sum') - zirc.add_element('Zr', 4.23e-2) + zirc.set_density("sum") + zirc.add_element("Zr", 4.23e-2) materials.append(zirc) self._model.materials = materials # Geometry # - pin_rad = 0.7 # cm - assembly_pitch = 1.235 # cm - hexagonal_pitch = 23.6 # cm - length = 10.0 # cm + pin_rad = 0.7 # cm + assembly_pitch = 1.235 # cm + hexagonal_pitch = 23.6 # cm + length = 10.0 # cm # Fuel pin surfaces @@ -65,8 +65,9 @@ def __init__(self, *args, **kwargs): # Fuel universe - fuel_ch_univ = openmc.Universe(universe_id=1, name="Fuel channel", - cells=[infcell, clfcell, outfcell]) + fuel_ch_univ = openmc.Universe( + universe_id=1, name="Fuel channel", cells=[infcell, clfcell, outfcell] + ) # Central tube surfaces @@ -89,9 +90,11 @@ def __init__(self, *args, **kwargs): # Central tubel universe - tube_ch_univ = openmc.Universe(universe_id=2, - name="Central tube channel", - cells=[inctcell, clctcell, outctcell]) + tube_ch_univ = openmc.Universe( + universe_id=2, + name="Central tube channel", + cells=[inctcell, clctcell, outctcell], + ) # Absorber tube surfaces @@ -124,25 +127,25 @@ def __init__(self, *args, **kwargs): # Absorber tube universe - abs_ch_univ = openmc.Universe(universe_id=3, - name="Central tube channel", - cells=[inabscell, clabscell, - interabscell, - clatcell, outabscell]) + abs_ch_univ = openmc.Universe( + universe_id=3, + name="Central tube channel", + cells=[inabscell, clabscell, interabscell, clatcell, outabscell], + ) # Assembly surfaces - edge_length = (1./np.sqrt(3.0)) * hexagonal_pitch - fuel_bottom = openmc.ZPlane(surface_id=9, z0=0.0, - boundary_type='reflective') - fuel_top = openmc.ZPlane(surface_id=10, z0=length, - boundary_type='reflective') + edge_length = (1.0 / np.sqrt(3.0)) * hexagonal_pitch + fuel_bottom = openmc.ZPlane(surface_id=9, z0=0.0, boundary_type="reflective") + fuel_top = openmc.ZPlane(surface_id=10, z0=length, boundary_type="reflective") # a hex surface for the core to go inside of - hexprism = openmc.model.HexagonalPrism(edge_length=edge_length, - origin=(0.0, 0.0), - boundary_type='reflective', - orientation='x') + hexprism = openmc.model.HexagonalPrism( + edge_length=edge_length, + origin=(0.0, 0.0), + boundary_type="reflective", + orientation="x", + ) region = -hexprism & +fuel_bottom & -fuel_top inf_mat = openmc.Cell(cell_id=12) @@ -160,32 +163,49 @@ def __init__(self, *args, **kwargs): arr.append(fuel_ch_univ) universes.append(arr) universes[-1] = [tube_ch_univ] - channels = [(7, 2), (7, 5), (7, 8), (7, 11), (7, 14), (7, 17), (5, 0), - (4, 3), (5, 5), (4, 9), (5, 10), (4, 15), (5, 15), - (4, 21), (5, 20), (4, 27), (5, 25), (4, 33)] + channels = [ + (7, 2), + (7, 5), + (7, 8), + (7, 11), + (7, 14), + (7, 17), + (5, 0), + (4, 3), + (5, 5), + (4, 9), + (5, 10), + (4, 15), + (5, 15), + (4, 21), + (5, 20), + (4, 27), + (5, 25), + (4, 33), + ] for i, j in channels: universes[i][j] = abs_ch_univ lattice = openmc.HexLattice(lattice_id=6, name="regular fuel assembly") lattice.orientation = "x" - lattice.center = (0., 0., length/2.0) - lattice.pitch = (assembly_pitch, length/2.0) - lattice.universes = 2*[universes] + lattice.center = (0.0, 0.0, length / 2.0) + lattice.pitch = (assembly_pitch, length / 2.0) + lattice.universes = 2 * [universes] lattice.outer = inf_mat_univ - assembly_cell = openmc.Cell(cell_id=13, - name="container assembly cell") + assembly_cell = openmc.Cell(cell_id=13, name="container assembly cell") assembly_cell.region = region assembly_cell.fill = lattice - root_univ = openmc.Universe(universe_id=5, name="root universe", - cells=[assembly_cell]) + root_univ = openmc.Universe( + universe_id=5, name="root universe", cells=[assembly_cell] + ) self._model.geometry = openmc.Geometry(root_univ) # Settings # settings = openmc.Settings() - settings.run_mode = 'eigenvalue' + settings.run_mode = "eigenvalue" source = openmc.IndependentSource() ll = [-edge_length, -edge_length, 0.0] @@ -201,5 +221,5 @@ def __init__(self, *args, **kwargs): def test_lattice_hex_ox_surf(): - harness = HexLatticeOXTestHarness('statepoint.10.h5', model=openmc.Model()) + harness = HexLatticeOXTestHarness("statepoint.10.h5", model=openmc.Model()) harness.main() diff --git a/tests/regression_tests/lattice_multiple/test.py b/tests/regression_tests/lattice_multiple/test.py index 10d9e50bc68..ebe0b8850f9 100644 --- a/tests/regression_tests/lattice_multiple/test.py +++ b/tests/regression_tests/lattice_multiple/test.py @@ -9,15 +9,15 @@ def model(): model = openmc.model.Model() - uo2 = openmc.Material(name='UO2') - uo2.set_density('g/cm3', 10.0) - uo2.add_nuclide('U235', 1.0) - uo2.add_nuclide('O16', 2.0) - water = openmc.Material(name='light water') - water.add_nuclide('H1', 2.0) - water.add_nuclide('O16', 1.0) - water.set_density('g/cm3', 1.0) - water.add_s_alpha_beta('c_H_in_H2O') + uo2 = openmc.Material(name="UO2") + uo2.set_density("g/cm3", 10.0) + uo2.add_nuclide("U235", 1.0) + uo2.add_nuclide("O16", 2.0) + water = openmc.Material(name="light water") + water.add_nuclide("H1", 2.0) + water.add_nuclide("O16", 1.0) + water.set_density("g/cm3", 1.0) + water.add_s_alpha_beta("c_H_in_H2O") model.materials.extend([uo2, water]) cyl = openmc.ZCylinder(r=0.4) @@ -38,11 +38,11 @@ def model(): inner_univ = openmc.Universe(cells=[inner_cell]) lattice = openmc.RectLattice() - lattice.lower_left = (-2*d, -2*d) - lattice.pitch = (2*d, 2*d) + lattice.lower_left = (-2 * d, -2 * d) + lattice.pitch = (2 * d, 2 * d) lattice.universes = np.full((2, 2), inner_univ) - box = openmc.model.RectangularPrism(4*d, 4*d, boundary_type='reflective') + box = openmc.model.RectangularPrism(4 * d, 4 * d, boundary_type="reflective") main_cell = openmc.Cell(fill=lattice, region=-box) model.geometry = openmc.Geometry([main_cell]) @@ -54,5 +54,5 @@ def model(): def test_lattice_multiple(model): - harness = PyAPITestHarness('statepoint.10.h5', model) + harness = PyAPITestHarness("statepoint.10.h5", model) harness.main() diff --git a/tests/regression_tests/lattice_rotated/test.py b/tests/regression_tests/lattice_rotated/test.py index 63641fb84a7..1ebd282151f 100644 --- a/tests/regression_tests/lattice_rotated/test.py +++ b/tests/regression_tests/lattice_rotated/test.py @@ -9,16 +9,16 @@ def rotated_lattice_model(): # Create some materials fuel1 = openmc.Material() - fuel1.set_density('g/cm3', 10.0) - fuel1.add_nuclide('U235', 1.0) + fuel1.set_density("g/cm3", 10.0) + fuel1.add_nuclide("U235", 1.0) fuel2 = openmc.Material() - fuel2.set_density('g/cm3', 10.0) - fuel2.add_nuclide('U238', 1.0) + fuel2.set_density("g/cm3", 10.0) + fuel2.add_nuclide("U238", 1.0) water = openmc.Material() - water.set_density('g/cm3', 1.0) - water.add_nuclide('H1', 2.0) - water.add_nuclide('O16', 1.0) - water.add_s_alpha_beta('c_H_in_H2O') + water.set_density("g/cm3", 1.0) + water.add_nuclide("H1", 2.0) + water.add_nuclide("O16", 1.0) + water.add_s_alpha_beta("c_H_in_H2O") model.materials.extend([fuel1, fuel2, water]) # Create universes for lattices @@ -36,19 +36,19 @@ def rotated_lattice_model(): # Create hexagonal lattice pitch = 1.25 hexlat = openmc.HexLattice() - hexlat.center = (0., 0.) + hexlat.center = (0.0, 0.0) hexlat.pitch = [pitch] hexlat.outer = outer_universe - outer_ring = [big_pin_universe] + [pin_universe]*11 - middle_ring = [big_pin_universe] + [pin_universe]*5 + outer_ring = [big_pin_universe] + [pin_universe] * 11 + middle_ring = [big_pin_universe] + [pin_universe] * 5 inner_ring = [big_pin_universe] hexlat.universes = [outer_ring, middle_ring, inner_ring] # Create rectangular lattice rectlat = openmc.RectLattice() - rectlat.center = (0., 0.) + rectlat.center = (0.0, 0.0) rectlat.pitch = (pitch, pitch) - rectlat.lower_left = (-2*pitch, -2*pitch) + rectlat.lower_left = (-2 * pitch, -2 * pitch) rectlat.outer = outer_universe rectlat.universes = np.full((4, 4), pin_universe) rectlat.universes[0] = big_pin_universe @@ -66,7 +66,7 @@ def rotated_lattice_model(): right_cell.rotation = (0.0, 0.0, 30.0) # Finish up with the geometry - outer_cyl = openmc.ZCylinder(r=8.0, boundary_type='vacuum') + outer_cyl = openmc.ZCylinder(r=8.0, boundary_type="vacuum") main_cell = openmc.Cell(fill=water, region=-outer_cyl & +left_cyl & +right_cyl) model.geometry = openmc.Geometry([main_cell, left_cell, right_cell]) @@ -81,5 +81,5 @@ def rotated_lattice_model(): def test(): model = rotated_lattice_model() - harness = PyAPITestHarness('statepoint.5.h5', model) + harness = PyAPITestHarness("statepoint.5.h5", model) harness.main() diff --git a/tests/regression_tests/mg_basic/test.py b/tests/regression_tests/mg_basic/test.py index 7b456bb9d07..ca0de28b12b 100644 --- a/tests/regression_tests/mg_basic/test.py +++ b/tests/regression_tests/mg_basic/test.py @@ -20,12 +20,15 @@ def create_library(): capture = [0.008708, 0.02518] absorption = np.add(capture, fiss) scatter = np.array( - [[[0.31980, 0.06694], [0.004555, -0.0003972]], - [[0.00000, 0.00000], [0.424100, 0.05439000]]]) + [ + [[0.31980, 0.06694], [0.004555, -0.0003972]], + [[0.00000, 0.00000], [0.424100, 0.05439000]], + ] + ) total = [0.33588, 0.54628] - chi = [1., 0.] + chi = [1.0, 0.0] - mat_1 = openmc.XSdata('mat_1', groups) + mat_1 = openmc.XSdata("mat_1", groups) mat_1.order = 1 mat_1.set_nu_fission(np.multiply(nu, fiss)) mat_1.set_absorption(absorption) @@ -36,18 +39,18 @@ def create_library(): # Make a version of mat-1 which has a tabular representation of the # scattering vice Legendre with 33 points - mat_2 = mat_1.convert_scatter_format('tabular', 33) - mat_2.name = 'mat_2' + mat_2 = mat_1.convert_scatter_format("tabular", 33) + mat_2.name = "mat_2" mg_cross_sections_file.add_xsdata(mat_2) # Make a version of mat-1 which has a histogram representation of the # scattering vice Legendre with 33 bins - mat_3 = mat_1.convert_scatter_format('histogram', 33) - mat_3.name = 'mat_3' + mat_3 = mat_1.convert_scatter_format("histogram", 33) + mat_3.name = "mat_3" mg_cross_sections_file.add_xsdata(mat_3) # Make a version which uses a fission matrix vice chi & nu-fission - mat_4 = openmc.XSdata('mat_4', groups) + mat_4 = openmc.XSdata("mat_4", groups) mat_4.order = 1 mat_4.set_nu_fission(np.outer(np.multiply(nu, fiss), chi)) mat_4.set_absorption(absorption) @@ -56,12 +59,12 @@ def create_library(): mg_cross_sections_file.add_xsdata(mat_4) # Make an angle-dependent version of mat_1 with 2 polar and 2 azim. angles - mat_5 = mat_1.convert_representation('angle', 2, 2) - mat_5.name = 'mat_5' + mat_5 = mat_1.convert_representation("angle", 2, 2) + mat_5.name = "mat_5" mg_cross_sections_file.add_xsdata(mat_5) # Make a copy of mat_1 for testing microscopic cross sections - mat_6 = openmc.XSdata('mat_6', groups) + mat_6 = openmc.XSdata("mat_6", groups) mat_6.order = 1 mat_6.set_nu_fission(np.multiply(nu, fiss)) mat_6.set_absorption(absorption) @@ -71,27 +74,33 @@ def create_library(): mg_cross_sections_file.add_xsdata(mat_6) # Write the file - mg_cross_sections_file.export_to_hdf5('2g.h5') + mg_cross_sections_file.export_to_hdf5("2g.h5") class MGXSTestHarness(PyAPITestHarness): def _cleanup(self): super()._cleanup() - f = '2g.h5' + f = "2g.h5" if os.path.exists(f): os.remove(f) def test_mg_basic(): create_library() - mat_names = ['base leg', 'base tab', 'base hist', 'base matrix', - 'base ang', 'micro'] + mat_names = [ + "base leg", + "base tab", + "base hist", + "base matrix", + "base ang", + "micro", + ] model = slab_mg(num_regions=6, mat_names=mat_names) # Modify the last material to be a microscopic combination of nuclides - model.materials[-1] = openmc.Material(name='micro', material_id=6) + model.materials[-1] = openmc.Material(name="micro", material_id=6) model.materials[-1].set_density("sum") model.materials[-1].add_nuclide("mat_1", 0.5) model.materials[-1].add_nuclide("mat_6", 0.5) - harness = PyAPITestHarness('statepoint.10.h5', model) + harness = PyAPITestHarness("statepoint.10.h5", model) harness.main() diff --git a/tests/regression_tests/mg_basic_delayed/test.py b/tests/regression_tests/mg_basic_delayed/test.py index f0474a5675d..99dd3db764a 100644 --- a/tests/regression_tests/mg_basic_delayed/test.py +++ b/tests/regression_tests/mg_basic_delayed/test.py @@ -17,19 +17,22 @@ def create_library(): mg_cross_sections_file.num_delayed_groups = n_dg beta = np.array([0.003, 0.003]) - one_m_beta = 1. - np.sum(beta) + one_m_beta = 1.0 - np.sum(beta) nu = [2.50, 2.50] fiss = np.array([0.002817, 0.097]) capture = [0.008708, 0.02518] absorption = np.add(capture, fiss) scatter = np.array( - [[[0.31980, 0.06694], [0.004555, -0.0003972]], - [[0.00000, 0.00000], [0.424100, 0.05439000]]]) + [ + [[0.31980, 0.06694], [0.004555, -0.0003972]], + [[0.00000, 0.00000], [0.424100, 0.05439000]], + ] + ) total = [0.33588, 0.54628] - chi = [1., 0.] + chi = [1.0, 0.0] # Make the base data that uses chi & nu-fission vectors with a beta - mat_1 = openmc.XSdata('mat_1', groups) + mat_1 = openmc.XSdata("mat_1", groups) mat_1.order = 1 mat_1.num_delayed_groups = 2 mat_1.set_beta(beta) @@ -41,7 +44,7 @@ def create_library(): mg_cross_sections_file.add_xsdata(mat_1) # Make a version that uses prompt and delayed version of nufiss and chi - mat_2 = openmc.XSdata('mat_2', groups) + mat_2 = openmc.XSdata("mat_2", groups) mat_2.order = 1 mat_2.num_delayed_groups = 2 mat_2.set_prompt_nu_fission(one_m_beta * np.multiply(nu, fiss)) @@ -58,7 +61,7 @@ def create_library(): mg_cross_sections_file.add_xsdata(mat_2) # Make a version that uses a nu-fission matrix with a beta - mat_3 = openmc.XSdata('mat_3', groups) + mat_3 = openmc.XSdata("mat_3", groups) mat_3.order = 1 mat_3.num_delayed_groups = 2 mat_3.set_beta(beta) @@ -69,11 +72,10 @@ def create_library(): mg_cross_sections_file.add_xsdata(mat_3) # Make a version that uses prompt and delayed version of the nufiss matrix - mat_4 = openmc.XSdata('mat_4', groups) + mat_4 = openmc.XSdata("mat_4", groups) mat_4.order = 1 mat_4.num_delayed_groups = 2 - mat_4.set_prompt_nu_fission(one_m_beta * - np.outer(np.multiply(nu, fiss), chi)) + mat_4.set_prompt_nu_fission(one_m_beta * np.outer(np.multiply(nu, fiss), chi)) delay_nu_fiss = np.zeros((n_dg, groups.num_groups, groups.num_groups)) for dg in range(n_dg): for g in range(groups.num_groups): @@ -86,7 +88,7 @@ def create_library(): mg_cross_sections_file.add_xsdata(mat_4) # Make the base data that uses chi & nu-fiss vectors with a group-wise beta - mat_5 = openmc.XSdata('mat_5', groups) + mat_5 = openmc.XSdata("mat_5", groups) mat_5.order = 1 mat_5.num_delayed_groups = 2 mat_5.set_beta(np.stack([beta] * groups.num_groups)) @@ -98,7 +100,7 @@ def create_library(): mg_cross_sections_file.add_xsdata(mat_5) # Make a version that uses a nu-fission matrix with a group-wise beta - mat_6 = openmc.XSdata('mat_6', groups) + mat_6 = openmc.XSdata("mat_6", groups) mat_6.order = 1 mat_6.num_delayed_groups = 2 mat_6.set_beta(np.stack([beta] * groups.num_groups)) @@ -109,23 +111,30 @@ def create_library(): mg_cross_sections_file.add_xsdata(mat_6) # Write the file - mg_cross_sections_file.export_to_hdf5('2g.h5') + mg_cross_sections_file.export_to_hdf5("2g.h5") class MGXSTestHarness(PyAPITestHarness): def _cleanup(self): super()._cleanup() - f = '2g.h5' + f = "2g.h5" if os.path.exists(f): os.remove(f) def test_mg_basic_delayed(): create_library() - model = slab_mg(num_regions=6, mat_names=['vec beta', 'vec no beta', - 'matrix beta', 'matrix no beta', - 'vec group beta', - 'matrix group beta']) - - harness = PyAPITestHarness('statepoint.10.h5', model) + model = slab_mg( + num_regions=6, + mat_names=[ + "vec beta", + "vec no beta", + "matrix beta", + "matrix no beta", + "vec group beta", + "matrix group beta", + ], + ) + + harness = PyAPITestHarness("statepoint.10.h5", model) harness.main() diff --git a/tests/regression_tests/mg_convert/test.py b/tests/regression_tests/mg_convert/test.py index 8099c89a29c..2f65143bdf9 100755 --- a/tests/regression_tests/mg_convert/test.py +++ b/tests/regression_tests/mg_convert/test.py @@ -18,21 +18,22 @@ def build_mgxs_library(convert): groups = openmc.mgxs.EnergyGroups(group_edges=[1e-5, 0.625, 20.0e6]) # Instantiate the 2-group (C5G7) cross section data - uo2_xsdata = openmc.XSdata('UO2', groups) + uo2_xsdata = openmc.XSdata("UO2", groups) uo2_xsdata.order = 2 - uo2_xsdata.set_total([2., 2.]) - uo2_xsdata.set_absorption([1., 1.]) - scatter_matrix = np.array([[[0.75, 0.25], - [0.00, 1.00]], - [[0.75 / 3., 0.25 / 3.], - [0.00 / 3., 1.00 / 3.]], - [[0.75 / 4., 0.25 / 4.], - [0.00 / 4., 1.00 / 4.]]]) + uo2_xsdata.set_total([2.0, 2.0]) + uo2_xsdata.set_absorption([1.0, 1.0]) + scatter_matrix = np.array( + [ + [[0.75, 0.25], [0.00, 1.00]], + [[0.75 / 3.0, 0.25 / 3.0], [0.00 / 3.0, 1.00 / 3.0]], + [[0.75 / 4.0, 0.25 / 4.0], [0.00 / 4.0, 1.00 / 4.0]], + ] + ) scatter_matrix = np.rollaxis(scatter_matrix, 0, 3) uo2_xsdata.set_scatter_matrix(scatter_matrix) uo2_xsdata.set_fission([0.5, 0.5]) - uo2_xsdata.set_nu_fission([1., 1.]) - uo2_xsdata.set_chi([1., 0.]) + uo2_xsdata.set_nu_fission([1.0, 1.0]) + uo2_xsdata.set_chi([1.0, 0.0]) mg_cross_sections_file = openmc.MGXSLibrary(groups) mg_cross_sections_file.add_xsdatas([uo2_xsdata]) @@ -40,22 +41,24 @@ def build_mgxs_library(convert): if convert is not None: if isinstance(convert[0], list): for conv in convert: - if conv[0] in ['legendre', 'tabular', 'histogram']: - mg_cross_sections_file = \ - mg_cross_sections_file.convert_scatter_format( - conv[0], conv[1]) - elif conv[0] in ['angle', 'isotropic']: - mg_cross_sections_file = \ + if conv[0] in ["legendre", "tabular", "histogram"]: + mg_cross_sections_file = ( + mg_cross_sections_file.convert_scatter_format(conv[0], conv[1]) + ) + elif conv[0] in ["angle", "isotropic"]: + mg_cross_sections_file = ( mg_cross_sections_file.convert_representation( - conv[0], conv[1], conv[1]) - elif convert[0] in ['legendre', 'tabular', 'histogram']: - mg_cross_sections_file = \ - mg_cross_sections_file.convert_scatter_format( - convert[0], convert[1]) - elif convert[0] in ['angle', 'isotropic']: - mg_cross_sections_file = \ - mg_cross_sections_file.convert_representation( - convert[0], convert[1], convert[1]) + conv[0], conv[1], conv[1] + ) + ) + elif convert[0] in ["legendre", "tabular", "histogram"]: + mg_cross_sections_file = mg_cross_sections_file.convert_scatter_format( + convert[0], convert[1] + ) + elif convert[0] in ["angle", "isotropic"]: + mg_cross_sections_file = mg_cross_sections_file.convert_representation( + convert[0], convert[1], convert[1] + ) mg_cross_sections_file.export_to_hdf5() @@ -64,11 +67,11 @@ class MGXSTestHarness(PyAPITestHarness): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Instantiate some Macroscopic Data - uo2_data = openmc.Macroscopic('UO2') + uo2_data = openmc.Macroscopic("UO2") # Instantiate some Materials and register the appropriate objects - mat = openmc.Material(material_id=1, name='UO2 fuel') - mat.set_density('macro', 1.1) + mat = openmc.Material(material_id=1, name="UO2 fuel") + mat.set_density("macro", 1.1) mat.add_macroscopic(uo2_data) # Instantiate a Materials collection and export to XML @@ -77,18 +80,18 @@ def __init__(self, *args, **kwargs): self._model.materials = materials_file # Instantiate ZCylinder surfaces - left = openmc.XPlane(surface_id=4, x0=-5., name='left') - right = openmc.XPlane(surface_id=5, x0=5., name='right') - bottom = openmc.YPlane(surface_id=6, y0=-5., name='bottom') - top = openmc.YPlane(surface_id=7, y0=5., name='top') + left = openmc.XPlane(surface_id=4, x0=-5.0, name="left") + right = openmc.XPlane(surface_id=5, x0=5.0, name="right") + bottom = openmc.YPlane(surface_id=6, y0=-5.0, name="bottom") + top = openmc.YPlane(surface_id=7, y0=5.0, name="top") - left.boundary_type = 'reflective' - right.boundary_type = 'vacuum' - top.boundary_type = 'reflective' - bottom.boundary_type = 'reflective' + left.boundary_type = "reflective" + right.boundary_type = "vacuum" + top.boundary_type = "reflective" + bottom.boundary_type = "reflective" # Instantiate Cells - fuel = openmc.Cell(cell_id=1, name='cell 1') + fuel = openmc.Cell(cell_id=1, name="cell 1") # Use surface half-spaces to define regions fuel.region = +left & -right & +bottom & -top @@ -97,7 +100,7 @@ def __init__(self, *args, **kwargs): fuel.fill = mat # Instantiate Universe - root = openmc.Universe(universe_id=0, name='root universe') + root = openmc.Universe(universe_id=0, name="root universe") # Register Cells with Universe root.add_cells([fuel]) @@ -120,31 +123,36 @@ def __init__(self, *args, **kwargs): def _run_openmc(self): # Run multiple conversions to compare results - cases = [['legendre', 2], ['legendre', 0], - ['tabular', 33], ['histogram', 32], - [['tabular', 33], ['legendre', 1]], - [['tabular', 33], ['tabular', 3]], - [['tabular', 33], ['histogram', 32]], - [['histogram', 32], ['legendre', 1]], - [['histogram', 32], ['tabular', 3]], - [['histogram', 32], ['histogram', 16]], - ['angle', 2], [['angle', 2], ['isotropic', None]]] - - outstr = '' + cases = [ + ["legendre", 2], + ["legendre", 0], + ["tabular", 33], + ["histogram", 32], + [["tabular", 33], ["legendre", 1]], + [["tabular", 33], ["tabular", 3]], + [["tabular", 33], ["histogram", 32]], + [["histogram", 32], ["legendre", 1]], + [["histogram", 32], ["tabular", 3]], + [["histogram", 32], ["histogram", 16]], + ["angle", 2], + [["angle", 2], ["isotropic", None]], + ] + + outstr = "" for case in cases: build_mgxs_library(case) - if config['mpi']: - mpi_args = [config['mpiexec'], '-n', config['mpi_np']] - openmc.run(openmc_exec=config['exe'], mpi_args=mpi_args) + if config["mpi"]: + mpi_args = [config["mpiexec"], "-n", config["mpi_np"]] + openmc.run(openmc_exec=config["exe"], mpi_args=mpi_args) else: - openmc.run(openmc_exec=config['exe']) + openmc.run(openmc_exec=config["exe"]) - with openmc.StatePoint('statepoint.{}.h5'.format(batches)) as sp: + with openmc.StatePoint("statepoint.{}.h5".format(batches)) as sp: # Write out k-combined. - outstr += 'k-combined:\n' - form = '{:12.6E} {:12.6E}\n' + outstr += "k-combined:\n" + form = "{:12.6E} {:12.6E}\n" outstr += form.format(sp.keff.n, sp.keff.s) return outstr @@ -153,14 +161,14 @@ def _get_results(self, outstr, hash_output=False): # Hash the results if necessary. if hash_output: sha512 = hashlib.sha512() - sha512.update(outstr.encode('utf-8')) + sha512.update(outstr.encode("utf-8")) outstr = sha512.hexdigest() return outstr def _cleanup(self): super()._cleanup() - f = os.path.join(os.getcwd(), 'mgxs.h5') + f = os.path.join(os.getcwd(), "mgxs.h5") if os.path.exists(f): os.remove(f) @@ -194,5 +202,5 @@ def update_results(self): def test_mg_convert(): - harness = MGXSTestHarness('statepoint.10.h5', model=openmc.Model()) + harness = MGXSTestHarness("statepoint.10.h5", model=openmc.Model()) harness.main() diff --git a/tests/regression_tests/mg_legendre/test.py b/tests/regression_tests/mg_legendre/test.py index b5a05c706d9..07730521742 100644 --- a/tests/regression_tests/mg_legendre/test.py +++ b/tests/regression_tests/mg_legendre/test.py @@ -20,12 +20,15 @@ def create_library(): capture = [0.008708, 0.02518] absorption = np.add(capture, fiss) scatter = np.array( - [[[0.31980, 0.06694], [0.004555, -0.0003972]], - [[0.00000, 0.00000], [0.424100, 0.05439000]]]) + [ + [[0.31980, 0.06694], [0.004555, -0.0003972]], + [[0.00000, 0.00000], [0.424100, 0.05439000]], + ] + ) total = [0.33588, 0.54628] - chi = [1., 0.] + chi = [1.0, 0.0] - mat_1 = openmc.XSdata('mat_1', groups) + mat_1 = openmc.XSdata("mat_1", groups) mat_1.order = 1 mat_1.set_nu_fission(np.multiply(nu, fiss)) mat_1.set_absorption(absorption) @@ -35,13 +38,13 @@ def create_library(): mg_cross_sections_file.add_xsdata(mat_1) # Write the file - mg_cross_sections_file.export_to_hdf5('2g.h5') + mg_cross_sections_file.export_to_hdf5("2g.h5") class MGXSTestHarness(PyAPITestHarness): def _cleanup(self): super()._cleanup() - f = '2g.h5' + f = "2g.h5" if os.path.exists(f): os.remove(f) @@ -49,7 +52,7 @@ def _cleanup(self): def test_mg_legendre(): create_library() model = slab_mg() - model.settings.tabular_legendre = {'enable': False} + model.settings.tabular_legendre = {"enable": False} - harness = PyAPITestHarness('statepoint.10.h5', model) + harness = PyAPITestHarness("statepoint.10.h5", model) harness.main() diff --git a/tests/regression_tests/mg_max_order/test.py b/tests/regression_tests/mg_max_order/test.py index 97d3f57d7ab..dfaf8d84d18 100644 --- a/tests/regression_tests/mg_max_order/test.py +++ b/tests/regression_tests/mg_max_order/test.py @@ -20,12 +20,15 @@ def create_library(): capture = [0.008708, 0.02518] absorption = np.add(capture, fiss) scatter = np.array( - [[[0.31980, 0.06694, 0.003], [0.004555, -0.0003972, 0.00002]], - [[0.00000, 0.00000, 0.000], [0.424100, 0.05439000, 0.0025]]]) + [ + [[0.31980, 0.06694, 0.003], [0.004555, -0.0003972, 0.00002]], + [[0.00000, 0.00000, 0.000], [0.424100, 0.05439000, 0.0025]], + ] + ) total = [0.33588, 0.54628] - chi = [1., 0.] + chi = [1.0, 0.0] - mat_1 = openmc.XSdata('mat_1', groups) + mat_1 = openmc.XSdata("mat_1", groups) mat_1.order = 2 mat_1.set_nu_fission(np.multiply(nu, fiss)) mat_1.set_absorption(absorption) @@ -35,13 +38,13 @@ def create_library(): mg_cross_sections_file.add_xsdata(mat_1) # Write the file - mg_cross_sections_file.export_to_hdf5('2g.h5') + mg_cross_sections_file.export_to_hdf5("2g.h5") class MGXSTestHarness(PyAPITestHarness): def _cleanup(self): super()._cleanup() - f = '2g.h5' + f = "2g.h5" if os.path.exists(f): os.remove(f) @@ -51,5 +54,5 @@ def test_mg_max_order(): model = slab_mg() model.settings.max_order = 1 - harness = PyAPITestHarness('statepoint.10.h5', model) + harness = PyAPITestHarness("statepoint.10.h5", model) harness.main() diff --git a/tests/regression_tests/mg_survival_biasing/test.py b/tests/regression_tests/mg_survival_biasing/test.py index 5d75611a9d4..16e07f3dd7f 100644 --- a/tests/regression_tests/mg_survival_biasing/test.py +++ b/tests/regression_tests/mg_survival_biasing/test.py @@ -20,12 +20,15 @@ def create_library(): capture = [0.008708, 0.02518] absorption = np.add(capture, fiss) scatter = np.array( - [[[0.31980, 0.06694], [0.004555, -0.0003972]], - [[0.00000, 0.00000], [0.424100, 0.05439000]]]) + [ + [[0.31980, 0.06694], [0.004555, -0.0003972]], + [[0.00000, 0.00000], [0.424100, 0.05439000]], + ] + ) total = [0.33588, 0.54628] - chi = [1., 0.] + chi = [1.0, 0.0] - mat_1 = openmc.XSdata('mat_1', groups) + mat_1 = openmc.XSdata("mat_1", groups) mat_1.order = 1 mat_1.set_nu_fission(np.multiply(nu, fiss)) mat_1.set_absorption(absorption) @@ -35,13 +38,13 @@ def create_library(): mg_cross_sections_file.add_xsdata(mat_1) # Write the file - mg_cross_sections_file.export_to_hdf5('2g.h5') + mg_cross_sections_file.export_to_hdf5("2g.h5") class MGXSTestHarness(PyAPITestHarness): def _cleanup(self): super()._cleanup() - f = '2g.h5' + f = "2g.h5" if os.path.exists(f): os.remove(f) @@ -51,5 +54,5 @@ def test_mg_survival_biasing(): model = slab_mg() model.settings.survival_biasing = True - harness = PyAPITestHarness('statepoint.10.h5', model) + harness = PyAPITestHarness("statepoint.10.h5", model) harness.main() diff --git a/tests/regression_tests/mg_tallies/test.py b/tests/regression_tests/mg_tallies/test.py index f4e9693f6f8..39cf273b2c6 100644 --- a/tests/regression_tests/mg_tallies/test.py +++ b/tests/regression_tests/mg_tallies/test.py @@ -20,18 +20,20 @@ def create_library(): capture = np.array([0.008708, 0.02518]) absorption = capture + fiss scatter = np.array( - [[[0.31980, 0.06694], [0.004555, -0.0003972]], - [[0.00000, 0.00000], [0.424100, 0.05439000]]]) + [ + [[0.31980, 0.06694], [0.004555, -0.0003972]], + [[0.00000, 0.00000], [0.424100, 0.05439000]], + ] + ) total = np.array([0.33588, 0.54628]) - chi = np.array([1., 0.]) - decay_rate = np.array([0.013336, 0.032739, 0.12078, 0.30278, 0.84949, - 2.853]) - delayed_yield = np.array([0.00055487, 0.00286407, 0.00273429, 0.0061305, - 0.00251342, 0.00105286]) + chi = np.array([1.0, 0.0]) + decay_rate = np.array([0.013336, 0.032739, 0.12078, 0.30278, 0.84949, 2.853]) + delayed_yield = np.array( + [0.00055487, 0.00286407, 0.00273429, 0.0061305, 0.00251342, 0.00105286] + ) inv_vel = 1.0 / np.array([1.4e9, 4.4e5]) - - mat_1 = openmc.XSdata('mat_1', groups, num_delayed_groups=6) + mat_1 = openmc.XSdata("mat_1", groups, num_delayed_groups=6) mat_1.order = 1 mat_1.set_fission(fiss) mat_1.set_kappa_fission(fiss * 200e6) @@ -46,13 +48,13 @@ def create_library(): mg_cross_sections_file.add_xsdata(mat_1) # Write the file - mg_cross_sections_file.export_to_hdf5('2g.h5') + mg_cross_sections_file.export_to_hdf5("2g.h5") class MGXSTestHarness(PyAPITestHarness): def _cleanup(self): super()._cleanup() - f = '2g.h5' + f = "2g.h5" if os.path.exists(f): os.remove(f) @@ -80,16 +82,26 @@ def test_mg_tallies(): nuclides = model.xs_data scores_with_nuclides = [ - 'total', 'absorption', 'fission', 'nu-fission', 'inverse-velocity', - 'prompt-nu-fission', 'delayed-nu-fission', 'kappa-fission', 'events', - 'decay-rate'] - scores_without_nuclides = scores_with_nuclides + ['flux'] - - for do_nuclides, scores in ((False, scores_without_nuclides), - (True, scores_with_nuclides)): + "total", + "absorption", + "fission", + "nu-fission", + "inverse-velocity", + "prompt-nu-fission", + "delayed-nu-fission", + "kappa-fission", + "events", + "decay-rate", + ] + scores_without_nuclides = scores_with_nuclides + ["flux"] + + for do_nuclides, scores in ( + (False, scores_without_nuclides), + (True, scores_with_nuclides), + ): t = openmc.Tally() t.filters = [mesh_filter] - t.estimator = 'analog' + t.estimator = "analog" t.scores = scores if do_nuclides: t.nuclides = nuclides @@ -97,7 +109,7 @@ def test_mg_tallies(): t = openmc.Tally() t.filters = [mesh_filter] - t.estimator = 'tracklength' + t.estimator = "tracklength" t.scores = scores if do_nuclides: t.nuclides = nuclides @@ -115,15 +127,15 @@ def test_mg_tallies(): t = openmc.Tally() t.filters = [mat_filter, e_filter] - t.estimator = 'analog' - t.scores = scores + ['scatter', 'nu-scatter'] + t.estimator = "analog" + t.scores = scores + ["scatter", "nu-scatter"] if do_nuclides: t.nuclides = nuclides model.tallies.append(t) t = openmc.Tally() t.filters = [mat_filter, e_filter] - t.estimator = 'collision' + t.estimator = "collision" t.scores = scores if do_nuclides: t.nuclides = nuclides @@ -131,7 +143,7 @@ def test_mg_tallies(): t = openmc.Tally() t.filters = [mat_filter, e_filter] - t.estimator = 'tracklength' + t.estimator = "tracklength" t.scores = scores if do_nuclides: t.nuclides = nuclides @@ -139,10 +151,10 @@ def test_mg_tallies(): t = openmc.Tally() t.filters = [mat_filter, e_filter, eout_filter] - t.scores = ['scatter', 'nu-scatter', 'nu-fission'] + t.scores = ["scatter", "nu-scatter", "nu-fission"] if do_nuclides: t.nuclides = nuclides model.tallies.append(t) - harness = MGXSTestHarness('statepoint.10.h5', model) + harness = MGXSTestHarness("statepoint.10.h5", model) harness.main() diff --git a/tests/regression_tests/mg_temperature/build_2g.py b/tests/regression_tests/mg_temperature/build_2g.py index 1fb7234499b..e7ae1756605 100644 --- a/tests/regression_tests/mg_temperature/build_2g.py +++ b/tests/regression_tests/mg_temperature/build_2g.py @@ -1,7 +1,7 @@ import openmc import numpy as np -names = ['H', 'O', 'Zr', 'U235', 'U238'] +names = ["H", "O", "Zr", "U235", "U238"] def build_openmc_xs_lib(name, groups, temperatures, xsdict, micro=True): @@ -9,143 +9,145 @@ def build_openmc_xs_lib(name, groups, temperatures, xsdict, micro=True): xsdata = openmc.XSdata(name, groups, temperatures=temperatures) xsdata.order = 0 for tt in temperatures: - xsdata.set_absorption(xsdict[tt]['absorption'][name], temperature=tt) - xsdata.set_scatter_matrix(xsdict[tt]['scatter'][name], temperature=tt) - xsdata.set_total(xsdict[tt]['total'][name], temperature=tt) - if (name in xsdict[tt]['nu-fission'].keys()): - xsdata.set_nu_fission(xsdict[tt]['nu-fission'][name], - temperature=tt) - xsdata.set_chi(np.array([1., 0.]), temperature=tt) + xsdata.set_absorption(xsdict[tt]["absorption"][name], temperature=tt) + xsdata.set_scatter_matrix(xsdict[tt]["scatter"][name], temperature=tt) + xsdata.set_total(xsdict[tt]["total"][name], temperature=tt) + if name in xsdict[tt]["nu-fission"].keys(): + xsdata.set_nu_fission(xsdict[tt]["nu-fission"][name], temperature=tt) + xsdata.set_chi(np.array([1.0, 0.0]), temperature=tt) return xsdata def create_micro_xs_dict(): """Returns micro xs library""" xs_micro = {} - reactions = ['absorption', 'total', 'scatter', 'nu-fission'] + reactions = ["absorption", "total", "scatter", "nu-fission"] # chi is unnecessary when energy bound is in thermal region # Temperature 300K # absorption xs_micro[300] = {r: {} for r in reactions} - xs_micro[300]['absorption']['H'] = np.array([1.0285E-4, 0.0057]) - xs_micro[300]['absorption']['O'] = np.array([7.1654E-5, 3.0283E-6]) - xs_micro[300]['absorption']['Zr'] = np.array([4.5918E-5, 3.6303E-5]) - xs_micro[300]['absorption']['U235'] = np.array([0.0035, 0.1040]) - xs_micro[300]['absorption']['U238'] = np.array([0.0056, 0.0094]) + xs_micro[300]["absorption"]["H"] = np.array([1.0285e-4, 0.0057]) + xs_micro[300]["absorption"]["O"] = np.array([7.1654e-5, 3.0283e-6]) + xs_micro[300]["absorption"]["Zr"] = np.array([4.5918e-5, 3.6303e-5]) + xs_micro[300]["absorption"]["U235"] = np.array([0.0035, 0.1040]) + xs_micro[300]["absorption"]["U238"] = np.array([0.0056, 0.0094]) # nu-scatter matrix - xs_micro[300]['scatter']['H'] = np.array([[[0.0910, 0.01469], - [0.0, 0.3316]]]) - xs_micro[300]['scatter']['O'] = np.array([[[0.0814, 3.3235E-4], - [0.0, 0.0960]]]) - xs_micro[300]['scatter']['Zr'] = np.array([[[0.0311, 2.6373E-5], - [0.0, 0.0315]]]) - xs_micro[300]['scatter']['U235'] = np.array([[[0.0311, 2.6373E-5], - [0.0, 0.0315]]]) - xs_micro[300]['scatter']['U238'] = np.array([[[0.0551, 2.2341E-5], - [0.0, 0.0526]]]) + xs_micro[300]["scatter"]["H"] = np.array([[[0.0910, 0.01469], [0.0, 0.3316]]]) + xs_micro[300]["scatter"]["O"] = np.array([[[0.0814, 3.3235e-4], [0.0, 0.0960]]]) + xs_micro[300]["scatter"]["Zr"] = np.array([[[0.0311, 2.6373e-5], [0.0, 0.0315]]]) + xs_micro[300]["scatter"]["U235"] = np.array([[[0.0311, 2.6373e-5], [0.0, 0.0315]]]) + xs_micro[300]["scatter"]["U238"] = np.array([[[0.0551, 2.2341e-5], [0.0, 0.0526]]]) # nu-fission - xs_micro[300]['nu-fission']['U235'] = np.array([0.0059, 0.2160]) - xs_micro[300]['nu-fission']['U238'] = np.array([0.0019, 1.4627E-7]) + xs_micro[300]["nu-fission"]["U235"] = np.array([0.0059, 0.2160]) + xs_micro[300]["nu-fission"]["U238"] = np.array([0.0019, 1.4627e-7]) # total - xs_micro[300]['total']['H'] = xs_micro[300]['absorption']['H'] + \ - np.sum(xs_micro[300]['scatter']['H'][0], 1) - xs_micro[300]['total']['O'] = xs_micro[300]['absorption']['O'] + \ - np.sum(xs_micro[300]['scatter']['O'][0], 1) + xs_micro[300]["total"]["H"] = xs_micro[300]["absorption"]["H"] + np.sum( + xs_micro[300]["scatter"]["H"][0], 1 + ) + xs_micro[300]["total"]["O"] = xs_micro[300]["absorption"]["O"] + np.sum( + xs_micro[300]["scatter"]["O"][0], 1 + ) - xs_micro[300]['total']['Zr'] = xs_micro[300]['absorption']['Zr'] + \ - np.sum(xs_micro[300]['scatter']['Zr'][0], 1) + xs_micro[300]["total"]["Zr"] = xs_micro[300]["absorption"]["Zr"] + np.sum( + xs_micro[300]["scatter"]["Zr"][0], 1 + ) - xs_micro[300]['total']['U235'] = xs_micro[300]['absorption']['U235'] + \ - np.sum(xs_micro[300]['scatter']['U235'][0], 1) + xs_micro[300]["total"]["U235"] = xs_micro[300]["absorption"]["U235"] + np.sum( + xs_micro[300]["scatter"]["U235"][0], 1 + ) - xs_micro[300]['total']['U238'] = xs_micro[300]['absorption']['U238'] + \ - np.sum(xs_micro[300]['scatter']['U238'][0], 1) + xs_micro[300]["total"]["U238"] = xs_micro[300]["absorption"]["U238"] + np.sum( + xs_micro[300]["scatter"]["U238"][0], 1 + ) # Temperature 600K xs_micro[600] = {r: {} for r in reactions} # absorption - xs_micro[600]['absorption']['H'] = np.array([1.0356E-4, 0.0046]) - xs_micro[600]['absorption']['O'] = np.array([7.2678E-5, 2.4963E-6]) - xs_micro[600]['absorption']['Zr'] = np.array([4.7256E-5, 2.9757E-5]) - xs_micro[600]['absorption']['U235'] = np.array([0.0035, 0.0853]) - xs_micro[600]['absorption']['U238'] = np.array([0.0058, 0.0079]) + xs_micro[600]["absorption"]["H"] = np.array([1.0356e-4, 0.0046]) + xs_micro[600]["absorption"]["O"] = np.array([7.2678e-5, 2.4963e-6]) + xs_micro[600]["absorption"]["Zr"] = np.array([4.7256e-5, 2.9757e-5]) + xs_micro[600]["absorption"]["U235"] = np.array([0.0035, 0.0853]) + xs_micro[600]["absorption"]["U238"] = np.array([0.0058, 0.0079]) # nu-scatter matrix - xs_micro[600]['scatter']['H'] = np.array([[[0.0910, 0.0138], - [0.0, 0.3316]]]) - xs_micro[600]['scatter']['O'] = np.array([[[0.0814, 3.5367E-4], - [0.0, 0.0959]]]) - xs_micro[600]['scatter']['Zr'] = np.array([[[0.0311, 3.2293E-5], - [0.0, 0.0314]]]) - xs_micro[600]['scatter']['U235'] = np.array([[[0.0022, 1.9763E-6], - [9.1634E-8, 0.0039]]]) - xs_micro[600]['scatter']['U238'] = np.array([[[0.0556, 2.8803E-5], - [0.0, 0.0536]]]) + xs_micro[600]["scatter"]["H"] = np.array([[[0.0910, 0.0138], [0.0, 0.3316]]]) + xs_micro[600]["scatter"]["O"] = np.array([[[0.0814, 3.5367e-4], [0.0, 0.0959]]]) + xs_micro[600]["scatter"]["Zr"] = np.array([[[0.0311, 3.2293e-5], [0.0, 0.0314]]]) + xs_micro[600]["scatter"]["U235"] = np.array( + [[[0.0022, 1.9763e-6], [9.1634e-8, 0.0039]]] + ) + xs_micro[600]["scatter"]["U238"] = np.array([[[0.0556, 2.8803e-5], [0.0, 0.0536]]]) # nu-fission - xs_micro[600]['nu-fission']['U235'] = np.array([0.0059, 0.1767]) - xs_micro[600]['nu-fission']['U238'] = np.array([0.0019, 1.2405E-7]) + xs_micro[600]["nu-fission"]["U235"] = np.array([0.0059, 0.1767]) + xs_micro[600]["nu-fission"]["U238"] = np.array([0.0019, 1.2405e-7]) # total - xs_micro[600]['total']['H'] = xs_micro[600]['absorption']['H'] + \ - np.sum(xs_micro[600]['scatter']['H'][0], 1) - xs_micro[600]['total']['O'] = xs_micro[600]['absorption']['O'] + \ - np.sum(xs_micro[600]['scatter']['O'][0], 1) + xs_micro[600]["total"]["H"] = xs_micro[600]["absorption"]["H"] + np.sum( + xs_micro[600]["scatter"]["H"][0], 1 + ) + xs_micro[600]["total"]["O"] = xs_micro[600]["absorption"]["O"] + np.sum( + xs_micro[600]["scatter"]["O"][0], 1 + ) - xs_micro[600]['total']['Zr'] = xs_micro[600]['absorption']['Zr'] + \ - np.sum(xs_micro[600]['scatter']['Zr'][0], 1) + xs_micro[600]["total"]["Zr"] = xs_micro[600]["absorption"]["Zr"] + np.sum( + xs_micro[600]["scatter"]["Zr"][0], 1 + ) - xs_micro[600]['total']['U235'] = xs_micro[600]['absorption']['U235'] + \ - np.sum(xs_micro[600]['scatter']['U235'][0], 1) + xs_micro[600]["total"]["U235"] = xs_micro[600]["absorption"]["U235"] + np.sum( + xs_micro[600]["scatter"]["U235"][0], 1 + ) - xs_micro[600]['total']['U238'] = xs_micro[600]['absorption']['U238'] + \ - np.sum(xs_micro[600]['scatter']['U238'][0], 1) + xs_micro[600]["total"]["U238"] = xs_micro[600]["absorption"]["U238"] + np.sum( + xs_micro[600]["scatter"]["U238"][0], 1 + ) # Temperature 900K xs_micro[900] = {r: {} for r in reactions} # absorption - xs_micro[900]['absorption']['H'] = np.array([1.0529E-4, 0.0040]) - xs_micro[900]['absorption']['O'] = np.array([7.3055E-5, 2.1850E-6]) - xs_micro[900]['absorption']['Zr'] = np.array([4.7141E-5, 2.5941E-5]) - xs_micro[900]['absorption']['U235'] = np.array([0.0035, 0.0749]) - xs_micro[900]['absorption']['U238'] = np.array([0.0060, 0.0071]) + xs_micro[900]["absorption"]["H"] = np.array([1.0529e-4, 0.0040]) + xs_micro[900]["absorption"]["O"] = np.array([7.3055e-5, 2.1850e-6]) + xs_micro[900]["absorption"]["Zr"] = np.array([4.7141e-5, 2.5941e-5]) + xs_micro[900]["absorption"]["U235"] = np.array([0.0035, 0.0749]) + xs_micro[900]["absorption"]["U238"] = np.array([0.0060, 0.0071]) # total - xs_micro[900]['total']['H'] = np.array([0.2982, 0.7332]) - xs_micro[900]['total']['O'] = np.array([0.0885, 0.1004]) - xs_micro[900]['total']['Zr'] = np.array([0.0370, 0.0317]) - xs_micro[900]['total']['U235'] = np.array([0.0061, 0.0789]) - xs_micro[900]['total']['U238'] = np.array([0.0707, 0.0613]) + xs_micro[900]["total"]["H"] = np.array([0.2982, 0.7332]) + xs_micro[900]["total"]["O"] = np.array([0.0885, 0.1004]) + xs_micro[900]["total"]["Zr"] = np.array([0.0370, 0.0317]) + xs_micro[900]["total"]["U235"] = np.array([0.0061, 0.0789]) + xs_micro[900]["total"]["U238"] = np.array([0.0707, 0.0613]) # nu-scatter matrix - xs_micro[900]['scatter']['H'] = np.array([[[0.0913, 0.0147], - [0.0, 0.4020]]]) - xs_micro[900]['scatter']['O'] = np.array([[[0.0812, 4.0413E-4], - [0.0, 0.0965]]]) - xs_micro[900]['scatter']['Zr'] = np.array([[[0.0311, 3.6735E-5], - [0.0, 0.0314]]]) - xs_micro[900]['scatter']['U235'] = np.array([[[0.0022, 2.9034E-6], - [1.3117E-8, 0.0039]]]) - xs_micro[900]['scatter']['U238'] = np.array([[[0.0560, 3.7619E-5], - [0.0, 0.0538]]]) + xs_micro[900]["scatter"]["H"] = np.array([[[0.0913, 0.0147], [0.0, 0.4020]]]) + xs_micro[900]["scatter"]["O"] = np.array([[[0.0812, 4.0413e-4], [0.0, 0.0965]]]) + xs_micro[900]["scatter"]["Zr"] = np.array([[[0.0311, 3.6735e-5], [0.0, 0.0314]]]) + xs_micro[900]["scatter"]["U235"] = np.array( + [[[0.0022, 2.9034e-6], [1.3117e-8, 0.0039]]] + ) + xs_micro[900]["scatter"]["U238"] = np.array([[[0.0560, 3.7619e-5], [0.0, 0.0538]]]) # nu-fission - xs_micro[900]['nu-fission']['U235'] = np.array([0.0059, 0.1545]) - xs_micro[900]['nu-fission']['U238'] = np.array([0.0019, 1.1017E-7]) + xs_micro[900]["nu-fission"]["U235"] = np.array([0.0059, 0.1545]) + xs_micro[900]["nu-fission"]["U238"] = np.array([0.0019, 1.1017e-7]) # total - xs_micro[900]['total']['H'] = xs_micro[900]['absorption']['H'] + \ - np.sum(xs_micro[900]['scatter']['H'][0], 1) - xs_micro[900]['total']['O'] = xs_micro[900]['absorption']['O'] + \ - np.sum(xs_micro[900]['scatter']['O'][0], 1) + xs_micro[900]["total"]["H"] = xs_micro[900]["absorption"]["H"] + np.sum( + xs_micro[900]["scatter"]["H"][0], 1 + ) + xs_micro[900]["total"]["O"] = xs_micro[900]["absorption"]["O"] + np.sum( + xs_micro[900]["scatter"]["O"][0], 1 + ) - xs_micro[900]['total']['Zr'] = xs_micro[900]['absorption']['Zr'] + \ - np.sum(xs_micro[900]['scatter']['Zr'][0], 1) + xs_micro[900]["total"]["Zr"] = xs_micro[900]["absorption"]["Zr"] + np.sum( + xs_micro[900]["scatter"]["Zr"][0], 1 + ) - xs_micro[900]['total']['U235'] = xs_micro[900]['absorption']['U235'] + \ - np.sum(xs_micro[900]['scatter']['U235'][0], 1) + xs_micro[900]["total"]["U235"] = xs_micro[900]["absorption"]["U235"] + np.sum( + xs_micro[900]["scatter"]["U235"][0], 1 + ) - xs_micro[900]['total']['U238'] = xs_micro[900]['absorption']['U238'] + \ - np.sum(xs_micro[900]['scatter']['U238'][0], 1) + xs_micro[900]["total"]["U238"] = xs_micro[900]["absorption"]["U238"] + np.sum( + xs_micro[900]["scatter"]["U238"][0], 1 + ) # roll axis for scatter matrix for t in xs_micro: - for n in xs_micro[t]['scatter']: - xs_micro[t]['scatter'][n] = np.rollaxis(xs_micro[t]['scatter'][n], - 0, 3) + for n in xs_micro[t]["scatter"]: + xs_micro[t]["scatter"][n] = np.rollaxis(xs_micro[t]["scatter"][n], 0, 3) return xs_micro @@ -162,7 +164,7 @@ def create_macro_dict(xs_micro): # The name 'macro' is needed to store data at the same level # of a xs_macro dictionary as for xs_micro and use it in # function build_openmc_xs_lib - xs_macro[t][r]['macro'] = sum(temp) + xs_macro[t][r]["macro"] = sum(temp) return xs_macro @@ -176,27 +178,23 @@ def create_openmc_2mg_libs(names): # Building a micro mg library micro_cs = create_micro_xs_dict() for name in names: - mg_cross_sections_file_micro.add_xsdata(build_openmc_xs_lib(name, - groups, - [t for t in - micro_cs], - micro_cs)) + mg_cross_sections_file_micro.add_xsdata( + build_openmc_xs_lib(name, groups, [t for t in micro_cs], micro_cs) + ) # Building a macro mg library macro_xs = create_macro_dict(micro_cs) - mg_cross_sections_file_macro.add_xsdata(build_openmc_xs_lib('macro', - groups, - [t for t in - macro_xs], - macro_xs)) + mg_cross_sections_file_macro.add_xsdata( + build_openmc_xs_lib("macro", groups, [t for t in macro_xs], macro_xs) + ) # Exporting library to hdf5 files - mg_cross_sections_file_micro.export_to_hdf5('micro_2g.h5') - mg_cross_sections_file_macro.export_to_hdf5('macro_2g.h5') + mg_cross_sections_file_micro.export_to_hdf5("micro_2g.h5") + mg_cross_sections_file_macro.export_to_hdf5("macro_2g.h5") # Returning the macro_xs dict is needed for analytical solution return macro_xs def analytical_solution_2g_therm(xsmin, xsmax=None, wgt=1.0): - """ Calculate eigenvalue based on analytical solution for eq Lf = (1/k)Qf + """Calculate eigenvalue based on analytical solution for eq Lf = (1/k)Qf in two group for infinity dilution media in assumption of group boundary in thermal spectra < 1.e+3 Ev Parameters: @@ -214,24 +212,27 @@ def analytical_solution_2g_therm(xsmin, xsmax=None, wgt=1.0): analytical eigenvalue of critical eq matrix """ if xsmax is None: - sa = xsmin['absorption']['macro'] - ss12 = xsmin['scatter']['macro'][0][1][0] - nsf = xsmin['nu-fission']['macro'] + sa = xsmin["absorption"]["macro"] + ss12 = xsmin["scatter"]["macro"][0][1][0] + nsf = xsmin["nu-fission"]["macro"] else: - sa = xsmin['absorption']['macro'] * wgt + \ - xsmax['absorption']['macro'] * (1 - wgt) - ss12 = xsmin['scatter']['macro'][0][1][0] * wgt + \ - xsmax['scatter']['macro'][0][1][0] * (1 - wgt) - nsf = xsmin['nu-fission']['macro'] * wgt + \ - xsmax['nu-fission']['macro'] * (1 - wgt) + sa = xsmin["absorption"]["macro"] * wgt + xsmax["absorption"]["macro"] * ( + 1 - wgt + ) + ss12 = xsmin["scatter"]["macro"][0][1][0] * wgt + xsmax["scatter"]["macro"][0][ + 1 + ][0] * (1 - wgt) + nsf = xsmin["nu-fission"]["macro"] * wgt + xsmax["nu-fission"]["macro"] * ( + 1 - wgt + ) L = np.array([sa[0] + ss12, 0.0, -ss12, sa[1]]).reshape(2, 2) Q = np.array([nsf[0], nsf[1], 0.0, 0.0]).reshape(2, 2) arr = np.linalg.inv(L).dot(Q) return np.amax(np.linalg.eigvals(arr)) -def build_inf_model(xsnames, xslibname, temperature, tempmethod='nearest'): - """ Building an infinite medium for openmc multi-group testing +def build_inf_model(xsnames, xslibname, temperature, tempmethod="nearest"): + """Building an infinite medium for openmc multi-group testing Parameters: ---------- xsnames : list of str() @@ -244,7 +245,7 @@ def build_inf_model(xsnames, xslibname, temperature, tempmethod='nearest'): by default 'nearest' """ model = openmc.Model() - inf_medium = openmc.Material(name='test material', material_id=1) + inf_medium = openmc.Material(name="test material", material_id=1) inf_medium.set_density("sum") for xs in xsnames: inf_medium.add_nuclide(xs, 1) @@ -255,13 +256,13 @@ def build_inf_model(xsnames, xslibname, temperature, tempmethod='nearest'): model.materials = materials_file # Instantiate boundary Planes - min_x = openmc.XPlane(boundary_type='reflective', x0=-INF) - max_x = openmc.XPlane(boundary_type='reflective', x0=INF) - min_y = openmc.YPlane(boundary_type='reflective', y0=-INF) - max_y = openmc.YPlane(boundary_type='reflective', y0=INF) + min_x = openmc.XPlane(boundary_type="reflective", x0=-INF) + max_x = openmc.XPlane(boundary_type="reflective", x0=INF) + min_y = openmc.YPlane(boundary_type="reflective", y0=-INF) + max_y = openmc.YPlane(boundary_type="reflective", y0=INF) # Instantiate a Cell - cell = openmc.Cell(cell_id=1, name='cell') + cell = openmc.Cell(cell_id=1, name="cell") cell.temperature = temperature # Register bounding Surfaces with the Cell cell.region = +min_x & -max_x & +min_y & -max_y @@ -270,7 +271,7 @@ def build_inf_model(xsnames, xslibname, temperature, tempmethod='nearest'): cell.fill = inf_medium # Create root universe - root_universe = openmc.Universe(name='root universe', cells=[cell]) + root_universe = openmc.Universe(name="root universe", cells=[cell]) # Create Geometry and set root Universe model.geometry = openmc.Geometry(root_universe) @@ -285,13 +286,14 @@ def build_inf_model(xsnames, xslibname, temperature, tempmethod='nearest'): settings_file.batches = batches settings_file.inactive = inactive settings_file.particles = particles - settings_file.energy_mode = 'multi-group' - settings_file.output = {'summary': False} + settings_file.energy_mode = "multi-group" + settings_file.output = {"summary": False} # Create an initial uniform spatial source distribution over fissionable zones bounds = [-INF, -INF, -INF, INF, INF, INF] uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:]) - settings_file.temperature = {'method': tempmethod} + settings_file.temperature = {"method": tempmethod} settings_file.source = openmc.IndependentSource( - space=uniform_dist, constraints={'fissionable': True}) + space=uniform_dist, constraints={"fissionable": True} + ) model.settings = settings_file model.export_to_model_xml() diff --git a/tests/regression_tests/mg_temperature/test.py b/tests/regression_tests/mg_temperature/test.py index af66390b535..88d05db38a8 100644 --- a/tests/regression_tests/mg_temperature/test.py +++ b/tests/regression_tests/mg_temperature/test.py @@ -11,35 +11,50 @@ def execute_test(self, update=False): base_dir = os.getcwd() overall_results = [] macro_xs = create_openmc_2mg_libs(names) - types = ('micro', 'micro', - 'micro', 'micro', - 'micro', - 'macro', 'macro', - 'macro', 'macro', - 'macro') - temperatures = (300., 600., 900., - 520., 600., - 300., 600., 900., - 520., 600) - methods = 2 * (3 * ('nearest',) + 2 * ('interpolation',)) + types = ( + "micro", + "micro", + "micro", + "micro", + "micro", + "macro", + "macro", + "macro", + "macro", + "macro", + ) + temperatures = ( + 300.0, + 600.0, + 900.0, + 520.0, + 600.0, + 300.0, + 600.0, + 900.0, + 520.0, + 600, + ) + methods = 2 * (3 * ("nearest",) + 2 * ("interpolation",)) analyt_interp = 10 * [None] - analyt_interp[3] = (600. - 520.) / 300. - analyt_interp[8] = (600. - 520.) / 300. + analyt_interp[3] = (600.0 - 520.0) / 300.0 + analyt_interp[8] = (600.0 - 520.0) / 300.0 try: - if (os.path.isdir("./temp")): + if os.path.isdir("./temp"): shutil.rmtree("./temp") os.mkdir("temp") os.chdir(os.path.join(base_dir, "temp")) for cs, t, m, ai in zip(types, temperatures, methods, analyt_interp): - if (cs == 'macro'): - build_inf_model(['macro'], '../macro_2g.h5', t, m) + if cs == "macro": + build_inf_model(["macro"], "../macro_2g.h5", t, m) else: - build_inf_model(names, '../micro_2g.h5', t, m) + build_inf_model(names, "../micro_2g.h5", t, m) if not ai: kanalyt = analytical_solution_2g_therm(macro_xs[t]) else: - kanalyt = analytical_solution_2g_therm(macro_xs[300], - macro_xs[600], ai) + kanalyt = analytical_solution_2g_therm( + macro_xs[300], macro_xs[600], ai + ) self._run_openmc() self._test_output_created() string = "{}, method: {}, t: {}, {}kanalyt\n{:12.6E}\n" @@ -53,10 +68,10 @@ def execute_test(self, update=False): self._compare_results() finally: os.chdir(base_dir) - if (os.path.isdir("./temp")): + if os.path.isdir("./temp"): shutil.rmtree("./temp") self._cleanup() - for f in ['micro_2g.h5', 'macro_2g.h5']: + for f in ["micro_2g.h5", "macro_2g.h5"]: if os.path.exists(f): os.remove(f) @@ -65,5 +80,5 @@ def update_results(self): def test_mg_temperature(): - harness = MgTemperatureTestHarness('statepoint.200.h5') + harness = MgTemperatureTestHarness("statepoint.200.h5") harness.main() diff --git a/tests/regression_tests/mg_temperature_multi/test.py b/tests/regression_tests/mg_temperature_multi/test.py index 3117e29ba03..a1caeff7bed 100755 --- a/tests/regression_tests/mg_temperature_multi/test.py +++ b/tests/regression_tests/mg_temperature_multi/test.py @@ -13,51 +13,274 @@ def create_library(): groups = openmc.mgxs.EnergyGroups(egroups) # Instantiate the 7-group (C5G7) cross section data - uo2_xsdata = openmc.XSdata('UO2', groups, temperatures=[294.0, 600.0]) + uo2_xsdata = openmc.XSdata("UO2", groups, temperatures=[294.0, 600.0]) uo2_xsdata.order = 0 - scatter_matrix = np.array([[ - [0.1275370, 0.0423780, 0.0000094, 0.0000000, 0.0000000, 0.0000000, 0.0000000], - [0.0000000, 0.3244560, 0.0016314, 0.0000000, 0.0000000, 0.0000000, 0.0000000], - [0.0000000, 0.0000000, 0.4509400, 0.0026792, 0.0000000, 0.0000000, 0.0000000], - [0.0000000, 0.0000000, 0.0000000, 0.4525650, 0.0055664, 0.0000000, 0.0000000], - [0.0000000, 0.0000000, 0.0000000, 0.0001253, 0.2714010, 0.0102550, 0.0000000], - [0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0012968, 0.2658020, 0.0168090], - [0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0085458, 0.2730800] - ]]) + scatter_matrix = np.array( + [ + [ + [ + 0.1275370, + 0.0423780, + 0.0000094, + 0.0000000, + 0.0000000, + 0.0000000, + 0.0000000, + ], + [ + 0.0000000, + 0.3244560, + 0.0016314, + 0.0000000, + 0.0000000, + 0.0000000, + 0.0000000, + ], + [ + 0.0000000, + 0.0000000, + 0.4509400, + 0.0026792, + 0.0000000, + 0.0000000, + 0.0000000, + ], + [ + 0.0000000, + 0.0000000, + 0.0000000, + 0.4525650, + 0.0055664, + 0.0000000, + 0.0000000, + ], + [ + 0.0000000, + 0.0000000, + 0.0000000, + 0.0001253, + 0.2714010, + 0.0102550, + 0.0000000, + ], + [ + 0.0000000, + 0.0000000, + 0.0000000, + 0.0000000, + 0.0012968, + 0.2658020, + 0.0168090, + ], + [ + 0.0000000, + 0.0000000, + 0.0000000, + 0.0000000, + 0.0000000, + 0.0085458, + 0.2730800, + ], + ] + ] + ) scatter_matrix = np.rollaxis(scatter_matrix, 0, 3) # Original C5G7 data - uo2_xsdata.set_total([0.1779492, 0.3298048, 0.4803882, 0.5543674, 0.3118013, 0.3951678, 0.5644058], temperature=294.0) - uo2_xsdata.set_absorption([8.0248E-03, 3.7174E-03, 2.6769E-02, 9.6236E-02, 3.0020E-02, 1.1126E-01, 2.8278E-01], temperature=294.0) + uo2_xsdata.set_total( + [0.1779492, 0.3298048, 0.4803882, 0.5543674, 0.3118013, 0.3951678, 0.5644058], + temperature=294.0, + ) + uo2_xsdata.set_absorption( + [ + 8.0248e-03, + 3.7174e-03, + 2.6769e-02, + 9.6236e-02, + 3.0020e-02, + 1.1126e-01, + 2.8278e-01, + ], + temperature=294.0, + ) uo2_xsdata.set_scatter_matrix(scatter_matrix, temperature=294.0) - uo2_xsdata.set_fission([7.21206E-03, 8.19301E-04, 6.45320E-03, 1.85648E-02, 1.78084E-02, 8.30348E-02, 2.16004E-01], temperature=294.0) - uo2_xsdata.set_nu_fission([2.005998E-02, 2.027303E-03, 1.570599E-02, 4.518301E-02, 4.334208E-02, 2.020901E-01, 5.257105E-01], temperature=294.0) - uo2_xsdata.set_chi([5.8791E-01, 4.1176E-01, 3.3906E-04, 1.1761E-07, 0.0000E+00, 0.0000E+00, 0.0000E+00], temperature=294.0) + uo2_xsdata.set_fission( + [ + 7.21206e-03, + 8.19301e-04, + 6.45320e-03, + 1.85648e-02, + 1.78084e-02, + 8.30348e-02, + 2.16004e-01, + ], + temperature=294.0, + ) + uo2_xsdata.set_nu_fission( + [ + 2.005998e-02, + 2.027303e-03, + 1.570599e-02, + 4.518301e-02, + 4.334208e-02, + 2.020901e-01, + 5.257105e-01, + ], + temperature=294.0, + ) + uo2_xsdata.set_chi( + [ + 5.8791e-01, + 4.1176e-01, + 3.3906e-04, + 1.1761e-07, + 0.0000e00, + 0.0000e00, + 0.0000e00, + ], + temperature=294.0, + ) # Altered C5G7 data (permuted Chi) - uo2_xsdata.set_total([0.1779492, 0.3298048, 0.4803882, 0.5543674, 0.3118013, 0.3951678, 0.5644058], temperature=600.0) - uo2_xsdata.set_absorption([8.0248E-03, 3.7174E-03, 2.6769E-02, 9.6236E-02, 3.0020E-02, 1.1126E-01, 2.8278E-01], temperature=600.0) + uo2_xsdata.set_total( + [0.1779492, 0.3298048, 0.4803882, 0.5543674, 0.3118013, 0.3951678, 0.5644058], + temperature=600.0, + ) + uo2_xsdata.set_absorption( + [ + 8.0248e-03, + 3.7174e-03, + 2.6769e-02, + 9.6236e-02, + 3.0020e-02, + 1.1126e-01, + 2.8278e-01, + ], + temperature=600.0, + ) uo2_xsdata.set_scatter_matrix(scatter_matrix, temperature=600.0) - uo2_xsdata.set_fission([7.21206E-03, 8.19301E-04, 6.45320E-03, 1.85648E-02, 1.78084E-02, 8.30348E-02, 2.16004E-01], temperature=600.0) - uo2_xsdata.set_nu_fission([2.005998E-02, 2.027303E-03, 1.570599E-02, 4.518301E-02, 4.334208E-02, 2.020901E-01, 5.257105E-01], temperature=600.0) - uo2_xsdata.set_chi([4.1176E-01, 5.8791E-01, 3.3906E-04, 1.1761E-07, 0.0000E+00, 0.0000E+00, 0.0000E+00], temperature=600.0) - - h2o_xsdata = openmc.XSdata('LWTR', groups) + uo2_xsdata.set_fission( + [ + 7.21206e-03, + 8.19301e-04, + 6.45320e-03, + 1.85648e-02, + 1.78084e-02, + 8.30348e-02, + 2.16004e-01, + ], + temperature=600.0, + ) + uo2_xsdata.set_nu_fission( + [ + 2.005998e-02, + 2.027303e-03, + 1.570599e-02, + 4.518301e-02, + 4.334208e-02, + 2.020901e-01, + 5.257105e-01, + ], + temperature=600.0, + ) + uo2_xsdata.set_chi( + [ + 4.1176e-01, + 5.8791e-01, + 3.3906e-04, + 1.1761e-07, + 0.0000e00, + 0.0000e00, + 0.0000e00, + ], + temperature=600.0, + ) + + h2o_xsdata = openmc.XSdata("LWTR", groups) h2o_xsdata.order = 0 - h2o_xsdata.set_total([0.15920605, 0.412969593, 0.59030986, 0.58435, - 0.718, 1.2544497, 2.650379]) - h2o_xsdata.set_absorption([6.0105E-04, 1.5793E-05, 3.3716E-04, - 1.9406E-03, 5.7416E-03, 1.5001E-02, - 3.7239E-02]) - scatter_matrix = np.array([[ - [0.0444777, 0.1134000, 0.0007235, 0.0000037, 0.0000001, 0.0000000, 0.0000000], - [0.0000000, 0.2823340, 0.1299400, 0.0006234, 0.0000480, 0.0000074, 0.0000010], - [0.0000000, 0.0000000, 0.3452560, 0.2245700, 0.0169990, 0.0026443, 0.0005034], - [0.0000000, 0.0000000, 0.0000000, 0.0910284, 0.4155100, 0.0637320, 0.0121390], - [0.0000000, 0.0000000, 0.0000000, 0.0000714, 0.1391380, 0.5118200, 0.0612290], - [0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0022157, 0.6999130, 0.5373200], - [0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.1324400, 2.4807000] - ]]) + h2o_xsdata.set_total( + [0.15920605, 0.412969593, 0.59030986, 0.58435, 0.718, 1.2544497, 2.650379] + ) + h2o_xsdata.set_absorption( + [ + 6.0105e-04, + 1.5793e-05, + 3.3716e-04, + 1.9406e-03, + 5.7416e-03, + 1.5001e-02, + 3.7239e-02, + ] + ) + scatter_matrix = np.array( + [ + [ + [ + 0.0444777, + 0.1134000, + 0.0007235, + 0.0000037, + 0.0000001, + 0.0000000, + 0.0000000, + ], + [ + 0.0000000, + 0.2823340, + 0.1299400, + 0.0006234, + 0.0000480, + 0.0000074, + 0.0000010, + ], + [ + 0.0000000, + 0.0000000, + 0.3452560, + 0.2245700, + 0.0169990, + 0.0026443, + 0.0005034, + ], + [ + 0.0000000, + 0.0000000, + 0.0000000, + 0.0910284, + 0.4155100, + 0.0637320, + 0.0121390, + ], + [ + 0.0000000, + 0.0000000, + 0.0000000, + 0.0000714, + 0.1391380, + 0.5118200, + 0.0612290, + ], + [ + 0.0000000, + 0.0000000, + 0.0000000, + 0.0000000, + 0.0022157, + 0.6999130, + 0.5373200, + ], + [ + 0.0000000, + 0.0000000, + 0.0000000, + 0.0000000, + 0.0000000, + 0.1324400, + 2.4807000, + ], + ] + ] + ) scatter_matrix = np.rollaxis(scatter_matrix, 0, 3) h2o_xsdata.set_scatter_matrix(scatter_matrix) @@ -69,7 +292,7 @@ def create_library(): class MGXSTestHarness(PyAPITestHarness): def _cleanup(self): super()._cleanup() - f = 'mgxs.h5' + f = "mgxs.h5" if os.path.exists(f): os.remove(f) @@ -83,16 +306,16 @@ def test_mg_temperature_multi(): # Create materials for the problem # Instantiate some Macroscopic Data - uo2_data = openmc.Macroscopic('UO2') - h2o_data = openmc.Macroscopic('LWTR') + uo2_data = openmc.Macroscopic("UO2") + h2o_data = openmc.Macroscopic("LWTR") # Instantiate some Materials and register the appropriate Macroscopic objects - uo2 = openmc.Material(name='UO2 fuel') - uo2.set_density('macro', 1.0) + uo2 = openmc.Material(name="UO2 fuel") + uo2.set_density("macro", 1.0) uo2.add_macroscopic(uo2_data) - water = openmc.Material(name='Water') - water.set_density('macro', 1.0) + water = openmc.Material(name="Water") + water.set_density("macro", 1.0) water.add_macroscopic(h2o_data) # Instantiate a Materials collection and export to XML @@ -103,19 +326,19 @@ def test_mg_temperature_multi(): # Define problem geometry # Create a surface for the fuel outer radius - fuel_ir = openmc.ZCylinder(r=0.25, name='Fuel IR') - fuel_or = openmc.ZCylinder(r=0.54, name='Fuel OR') + fuel_ir = openmc.ZCylinder(r=0.25, name="Fuel IR") + fuel_or = openmc.ZCylinder(r=0.54, name="Fuel OR") # Create a region represented as the inside of a rectangular prism pitch = 1.26 - box = openmc.model.RectangularPrism(pitch, pitch, boundary_type='reflective') + box = openmc.model.RectangularPrism(pitch, pitch, boundary_type="reflective") # Instantiate Cells - fuel_inner = openmc.Cell(fill=uo2, region=-fuel_ir, name='fuel inner') + fuel_inner = openmc.Cell(fill=uo2, region=-fuel_ir, name="fuel inner") fuel_inner.temperature = 600.0 - fuel_outer = openmc.Cell(fill=uo2, region=+fuel_ir & -fuel_or, name='fuel outer') + fuel_outer = openmc.Cell(fill=uo2, region=+fuel_ir & -fuel_or, name="fuel outer") fuel_outer.temperature = 294.0 - moderator = openmc.Cell(fill=water, region=+fuel_or & -box, name='moderator') + moderator = openmc.Cell(fill=water, region=+fuel_or & -box, name="moderator") # Create a geometry with the two cells and export to XML geometry = openmc.Geometry([fuel_inner, fuel_outer, moderator]) @@ -131,11 +354,12 @@ def test_mg_temperature_multi(): settings.particles = 1000 # Create an initial uniform spatial source distribution over fissionable zones - lower_left = (-pitch/2, -pitch/2, -1) - upper_right = (pitch/2, pitch/2, 1) + lower_left = (-pitch / 2, -pitch / 2, -1) + upper_right = (pitch / 2, pitch / 2, 1) uniform_dist = openmc.stats.Box(lower_left, upper_right) settings.source = openmc.IndependentSource( - space=uniform_dist, constraints={'fissionable': True}) + space=uniform_dist, constraints={"fissionable": True} + ) ############################################################################### # Define tallies @@ -150,12 +374,12 @@ def test_mg_temperature_multi(): inner_tally = openmc.Tally(name="inner tally") inner_tally.filters = [energy_filter] inner_tally.filters = [inner_filter] - inner_tally.scores = ['flux'] + inner_tally.scores = ["flux"] outer_tally = openmc.Tally(name="outer tally") outer_tally.filters = [energy_filter] outer_tally.filters = [outer_filter] - outer_tally.scores = ['flux'] + outer_tally.scores = ["flux"] # Instantiate a Tallies collection and export to XML tallies = openmc.Tallies([inner_tally, outer_tally]) @@ -163,5 +387,5 @@ def test_mg_temperature_multi(): # Generate model and run test model = openmc.Model(geometry, materials, settings, tallies) - harness = MGXSTestHarness('statepoint.10.h5', model=model) + harness = MGXSTestHarness("statepoint.10.h5", model=model) harness.main() diff --git a/tests/regression_tests/mgxs_library_ce_to_mg/test.py b/tests/regression_tests/mgxs_library_ce_to_mg/test.py index 4dff6732357..2f453adad79 100644 --- a/tests/regression_tests/mgxs_library_ce_to_mg/test.py +++ b/tests/regression_tests/mgxs_library_ce_to_mg/test.py @@ -14,17 +14,22 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Initialize a two-group structure - energy_groups = openmc.mgxs.EnergyGroups(group_edges=[0, 0.625, 20.e6]) + energy_groups = openmc.mgxs.EnergyGroups(group_edges=[0, 0.625, 20.0e6]) # Initialize MGXS Library for a few cross section types self.mgxs_lib = openmc.mgxs.Library(self._model.geometry) self.mgxs_lib.by_nuclide = False - self.mgxs_lib.mgxs_types = ['total', 'absorption', 'nu-fission matrix', - 'nu-scatter matrix', 'multiplicity matrix'] + self.mgxs_lib.mgxs_types = [ + "total", + "absorption", + "nu-fission matrix", + "nu-scatter matrix", + "multiplicity matrix", + ] self.mgxs_lib.energy_groups = energy_groups self.mgxs_lib.correction = None self.mgxs_lib.legendre_order = 3 - self.mgxs_lib.domain_type = 'material' + self.mgxs_lib.domain_type = "material" self.mgxs_lib.build_library() # Initialize a tallies file @@ -32,22 +37,23 @@ def __init__(self, *args, **kwargs): def _run_openmc(self): # Initial run - if config['mpi']: - mpi_args = [config['mpiexec'], '-n', config['mpi_np']] - openmc.run(openmc_exec=config['exe'], mpi_args=mpi_args) + if config["mpi"]: + mpi_args = [config["mpiexec"], "-n", config["mpi_np"]] + openmc.run(openmc_exec=config["exe"], mpi_args=mpi_args) else: - openmc.run(openmc_exec=config['exe']) + openmc.run(openmc_exec=config["exe"]) # Build MG Inputs # Get data needed to execute Library calculations. sp = openmc.StatePoint(self._sp_name) self.mgxs_lib.load_from_statepoint(sp) - self._model.mgxs_file, self._model.materials, \ - self._model.geometry = self.mgxs_lib.create_mg_mode() + self._model.mgxs_file, self._model.materials, self._model.geometry = ( + self.mgxs_lib.create_mg_mode() + ) # Modify materials and settings so we can run in MG mode - self._model.materials.cross_sections = './mgxs.h5' - self._model.settings.energy_mode = 'multi-group' + self._model.materials.cross_sections = "./mgxs.h5" + self._model.settings.energy_mode = "multi-group" # Dont need tallies so clear them from the model self._model.tallies = openmc.Tallies() @@ -61,15 +67,15 @@ def _run_openmc(self): sp._summary._f.close() # Re-run MG mode. - if config['mpi']: - mpi_args = [config['mpiexec'], '-n', config['mpi_np']] - openmc.run(openmc_exec=config['exe'], mpi_args=mpi_args) + if config["mpi"]: + mpi_args = [config["mpiexec"], "-n", config["mpi_np"]] + openmc.run(openmc_exec=config["exe"], mpi_args=mpi_args) else: - openmc.run(openmc_exec=config['exe']) + openmc.run(openmc_exec=config["exe"]) def _cleanup(self): super()._cleanup() - f = 'mgxs.h5' + f = "mgxs.h5" if os.path.exists(f): os.remove(f) @@ -78,5 +84,5 @@ def test_mgxs_library_ce_to_mg(): # Set the input set to use the pincell model model = pwr_pin_cell() - harness = MGXSTestHarness('statepoint.10.h5', model) + harness = MGXSTestHarness("statepoint.10.h5", model) harness.main() diff --git a/tests/regression_tests/mgxs_library_ce_to_mg_nuclides/test.py b/tests/regression_tests/mgxs_library_ce_to_mg_nuclides/test.py index ee0cdf0d926..98b740c8445 100644 --- a/tests/regression_tests/mgxs_library_ce_to_mg_nuclides/test.py +++ b/tests/regression_tests/mgxs_library_ce_to_mg_nuclides/test.py @@ -14,17 +14,22 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Initialize a two-group structure - energy_groups = openmc.mgxs.EnergyGroups(group_edges=[0, 0.625, 20.e6]) + energy_groups = openmc.mgxs.EnergyGroups(group_edges=[0, 0.625, 20.0e6]) # Initialize MGXS Library for a few cross section types self.mgxs_lib = openmc.mgxs.Library(self._model.geometry) self.mgxs_lib.by_nuclide = True - self.mgxs_lib.mgxs_types = ['total', 'absorption', 'nu-fission matrix', - 'nu-scatter matrix', 'multiplicity matrix'] + self.mgxs_lib.mgxs_types = [ + "total", + "absorption", + "nu-fission matrix", + "nu-scatter matrix", + "multiplicity matrix", + ] self.mgxs_lib.energy_groups = energy_groups self.mgxs_lib.correction = None self.mgxs_lib.legendre_order = 3 - self.mgxs_lib.domain_type = 'material' + self.mgxs_lib.domain_type = "material" self.mgxs_lib.build_library() # Initialize a tallies file @@ -32,22 +37,23 @@ def __init__(self, *args, **kwargs): def _run_openmc(self): # Initial run - if config['mpi']: - mpi_args = [config['mpiexec'], '-n', config['mpi_np']] - openmc.run(openmc_exec=config['exe'], mpi_args=mpi_args) + if config["mpi"]: + mpi_args = [config["mpiexec"], "-n", config["mpi_np"]] + openmc.run(openmc_exec=config["exe"], mpi_args=mpi_args) else: - openmc.run(openmc_exec=config['exe']) + openmc.run(openmc_exec=config["exe"]) # Build MG Inputs # Get data needed to execute Library calculations. sp = openmc.StatePoint(self._sp_name) self.mgxs_lib.load_from_statepoint(sp) - self._model.mgxs_file, self._model.materials, \ - self._model.geometry = self.mgxs_lib.create_mg_mode() + self._model.mgxs_file, self._model.materials, self._model.geometry = ( + self.mgxs_lib.create_mg_mode() + ) # Modify materials and settings so we can run in MG mode - self._model.materials.cross_sections = './mgxs.h5' - self._model.settings.energy_mode = 'multi-group' + self._model.materials.cross_sections = "./mgxs.h5" + self._model.settings.energy_mode = "multi-group" # Dont need tallies so clear them from the model self._model.tallies = openmc.Tallies() @@ -61,15 +67,15 @@ def _run_openmc(self): sp._summary._f.close() # Re-run MG mode. - if config['mpi']: - mpi_args = [config['mpiexec'], '-n', config['mpi_np']] - openmc.run(openmc_exec=config['exe'], mpi_args=mpi_args) + if config["mpi"]: + mpi_args = [config["mpiexec"], "-n", config["mpi_np"]] + openmc.run(openmc_exec=config["exe"], mpi_args=mpi_args) else: - openmc.run(openmc_exec=config['exe']) + openmc.run(openmc_exec=config["exe"]) def _cleanup(self): super()._cleanup() - f = 'mgxs.h5' + f = "mgxs.h5" if os.path.exists(f): os.remove(f) @@ -78,5 +84,5 @@ def test_mgxs_library_ce_to_mg(): # Set the input set to use the pincell model model = pwr_pin_cell() - harness = MGXSTestHarness('statepoint.10.h5', model) + harness = MGXSTestHarness("statepoint.10.h5", model) harness.main() diff --git a/tests/regression_tests/mgxs_library_condense/test.py b/tests/regression_tests/mgxs_library_condense/test.py index a7e60617f08..194ee29ee7f 100644 --- a/tests/regression_tests/mgxs_library_condense/test.py +++ b/tests/regression_tests/mgxs_library_condense/test.py @@ -12,25 +12,24 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Initialize a two-group structure - energy_groups = openmc.mgxs.EnergyGroups(group_edges=[0, 0.625, 20.e6]) + energy_groups = openmc.mgxs.EnergyGroups(group_edges=[0, 0.625, 20.0e6]) # Initialize MGXS Library for a few cross section types self.mgxs_lib = openmc.mgxs.Library(self._model.geometry) self.mgxs_lib.by_nuclide = False # Test all MGXS types - self.mgxs_lib.mgxs_types = openmc.mgxs.MGXS_TYPES + \ - openmc.mgxs.MDGXS_TYPES + self.mgxs_lib.mgxs_types = openmc.mgxs.MGXS_TYPES + openmc.mgxs.MDGXS_TYPES self.mgxs_lib.energy_groups = energy_groups self.mgxs_lib.num_delayed_groups = 6 self.mgxs_lib.legendre_order = 3 - self.mgxs_lib.domain_type = 'mesh' + self.mgxs_lib.domain_type = "mesh" # Instantiate a tally mesh mesh = openmc.RegularMesh(mesh_id=1) mesh.dimension = [2, 2] - mesh.lower_left = [-100., -100.] - mesh.width = [100., 100.] + mesh.lower_left = [-100.0, -100.0] + mesh.width = [100.0, 100.0] self.mgxs_lib.domains = [mesh] self.mgxs_lib.build_library() @@ -48,21 +47,21 @@ def _get_results(self, hash_output=False): self.mgxs_lib.load_from_statepoint(sp) # Build a condensed 1-group MGXS Library - one_group = openmc.mgxs.EnergyGroups([0., 20.e6]) + one_group = openmc.mgxs.EnergyGroups([0.0, 20.0e6]) condense_lib = self.mgxs_lib.get_condensed_library(one_group) # Build a string from Pandas Dataframe for each 1-group MGXS - outstr = '' + outstr = "" for domain in condense_lib.domains: for mgxs_type in condense_lib.mgxs_types: mgxs = condense_lib.get_mgxs(domain, mgxs_type) df = mgxs.get_pandas_dataframe() - outstr += df.to_string() + '\n' + outstr += df.to_string() + "\n" # Hash the results if necessary if hash_output: sha512 = hashlib.sha512() - sha512.update(outstr.encode('utf-8')) + sha512.update(outstr.encode("utf-8")) outstr = sha512.hexdigest() return outstr @@ -71,5 +70,5 @@ def _get_results(self, hash_output=False): def test_mgxs_library_condense(): # Use the pincell model model = pwr_pin_cell() - harness = MGXSTestHarness('statepoint.10.h5', model) + harness = MGXSTestHarness("statepoint.10.h5", model) harness.main() diff --git a/tests/regression_tests/mgxs_library_correction/test.py b/tests/regression_tests/mgxs_library_correction/test.py index 05eedfef824..086fc205ba9 100644 --- a/tests/regression_tests/mgxs_library_correction/test.py +++ b/tests/regression_tests/mgxs_library_correction/test.py @@ -13,19 +13,22 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Initialize a two-group structure - energy_groups = openmc.mgxs.EnergyGroups(group_edges=[0, 0.625, 20.e6]) + energy_groups = openmc.mgxs.EnergyGroups(group_edges=[0, 0.625, 20.0e6]) # Initialize MGXS Library for a few cross section types self.mgxs_lib = openmc.mgxs.Library(self._model.geometry) self.mgxs_lib.by_nuclide = False # Test all MGXS types - self.mgxs_lib.mgxs_types = ['scatter matrix', 'nu-scatter matrix', - 'consistent scatter matrix', - 'consistent nu-scatter matrix'] + self.mgxs_lib.mgxs_types = [ + "scatter matrix", + "nu-scatter matrix", + "consistent scatter matrix", + "consistent nu-scatter matrix", + ] self.mgxs_lib.energy_groups = energy_groups - self.mgxs_lib.correction = 'P0' - self.mgxs_lib.domain_type = 'material' + self.mgxs_lib.correction = "P0" + self.mgxs_lib.domain_type = "material" self.mgxs_lib.build_library() # Add tallies @@ -41,17 +44,17 @@ def _get_results(self, hash_output=False): self.mgxs_lib.load_from_statepoint(sp) # Build a string from Pandas Dataframe for each MGXS - outstr = '' + outstr = "" for domain in self.mgxs_lib.domains: for mgxs_type in self.mgxs_lib.mgxs_types: mgxs = self.mgxs_lib.get_mgxs(domain, mgxs_type) df = mgxs.get_pandas_dataframe() - outstr += df.to_string() + '\n' + outstr += df.to_string() + "\n" # Hash the results if necessary if hash_output: sha512 = hashlib.sha512() - sha512.update(outstr.encode('utf-8')) + sha512.update(outstr.encode("utf-8")) outstr = sha512.hexdigest() return outstr @@ -59,5 +62,5 @@ def _get_results(self, hash_output=False): def test_mgxs_library_correction(): model = pwr_pin_cell() - harness = MGXSTestHarness('statepoint.10.h5', model) + harness = MGXSTestHarness("statepoint.10.h5", model) harness.main() diff --git a/tests/regression_tests/mgxs_library_distribcell/test.py b/tests/regression_tests/mgxs_library_distribcell/test.py index fd6c8e93866..7fbb847d9ce 100644 --- a/tests/regression_tests/mgxs_library_distribcell/test.py +++ b/tests/regression_tests/mgxs_library_distribcell/test.py @@ -15,7 +15,7 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Initialize a one-group structure - energy_groups = openmc.mgxs.EnergyGroups(group_edges=[0, 20.e6]) + energy_groups = openmc.mgxs.EnergyGroups(group_edges=[0, 20.0e6]) # Initialize MGXS Library for a few cross section types # for one material-filled cell in the geometry @@ -23,16 +23,16 @@ def __init__(self, *args, **kwargs): self.mgxs_lib.by_nuclide = False # Test all relevant MGXS types - relevant_MGXS_TYPES = [item for item in openmc.mgxs.MGXS_TYPES - if item != 'current'] - self.mgxs_lib.mgxs_types = tuple(relevant_MGXS_TYPES) + \ - openmc.mgxs.MDGXS_TYPES + relevant_MGXS_TYPES = [ + item for item in openmc.mgxs.MGXS_TYPES if item != "current" + ] + self.mgxs_lib.mgxs_types = tuple(relevant_MGXS_TYPES) + openmc.mgxs.MDGXS_TYPES self.mgxs_lib.energy_groups = energy_groups self.mgxs_lib.num_delayed_groups = 6 self.mgxs_lib.legendre_order = 3 - self.mgxs_lib.domain_type = 'distribcell' + self.mgxs_lib.domain_type = "distribcell" cells = self.mgxs_lib.geometry.get_all_material_cells().values() - self.mgxs_lib.domains = [c for c in cells if c.name == 'fuel'] + self.mgxs_lib.domains = [c for c in cells if c.name == "fuel"] self.mgxs_lib.build_library() # Add tallies @@ -52,17 +52,17 @@ def _get_results(self, hash_output=False): avg_lib = self.mgxs_lib.get_subdomain_avg_library() # Build a string from Pandas Dataframe for each 1-group MGXS - outstr = '' + outstr = "" for domain in avg_lib.domains: for mgxs_type in avg_lib.mgxs_types: mgxs = avg_lib.get_mgxs(domain, mgxs_type) df = mgxs.get_pandas_dataframe() - outstr += df.to_string() + '\n' + outstr += df.to_string() + "\n" # Hash the results if necessary if hash_output: sha512 = hashlib.sha512() - sha512.update(outstr.encode('utf-8')) + sha512.update(outstr.encode("utf-8")) outstr = sha512.hexdigest() return outstr @@ -70,5 +70,5 @@ def _get_results(self, hash_output=False): def test_mgxs_library_distribcell(): model = pwr_assembly() - harness = MGXSTestHarness('statepoint.10.h5', model) + harness = MGXSTestHarness("statepoint.10.h5", model) harness.main() diff --git a/tests/regression_tests/mgxs_library_hdf5/test.py b/tests/regression_tests/mgxs_library_hdf5/test.py index 06625c25f9e..063a5400164 100644 --- a/tests/regression_tests/mgxs_library_hdf5/test.py +++ b/tests/regression_tests/mgxs_library_hdf5/test.py @@ -16,25 +16,24 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Initialize a two-group structure - energy_groups = openmc.mgxs.EnergyGroups(group_edges=[0, 0.625, 20.e6]) + energy_groups = openmc.mgxs.EnergyGroups(group_edges=[0, 0.625, 20.0e6]) # Initialize MGXS Library for a few cross section types self.mgxs_lib = openmc.mgxs.Library(self._model.geometry) self.mgxs_lib.by_nuclide = False # Test all MGXS types - self.mgxs_lib.mgxs_types = openmc.mgxs.MGXS_TYPES + \ - openmc.mgxs.MDGXS_TYPES + self.mgxs_lib.mgxs_types = openmc.mgxs.MGXS_TYPES + openmc.mgxs.MDGXS_TYPES self.mgxs_lib.energy_groups = energy_groups self.mgxs_lib.num_delayed_groups = 6 self.mgxs_lib.legendre_order = 3 - self.mgxs_lib.domain_type = 'mesh' + self.mgxs_lib.domain_type = "mesh" # Instantiate a tally mesh mesh = openmc.RegularMesh(mesh_id=1) mesh.dimension = [2, 2] - mesh.lower_left = [-100., -100.] - mesh.width = [100., 100.] + mesh.lower_left = [-100.0, -100.0] + mesh.width = [100.0, 100.0] self.mgxs_lib.domains = [mesh] self.mgxs_lib.build_library() @@ -52,44 +51,44 @@ def _get_results(self, hash_output=False): self.mgxs_lib.load_from_statepoint(sp) # Export the MGXS Library to an HDF5 file - self.mgxs_lib.build_hdf5_store(directory='.') + self.mgxs_lib.build_hdf5_store(directory=".") # Test export of the MGXS Library to an Excel spreadsheet for mgxs in self.mgxs_lib.all_mgxs.values(): for xs in mgxs.values(): - xs.export_xs_data('mgxs', xs_type='macro', format='excel') + xs.export_xs_data("mgxs", xs_type="macro", format="excel") # Open the MGXS HDF5 file - with h5py.File('mgxs.h5', 'r') as f: + with h5py.File("mgxs.h5", "r") as f: # Build a string from the datasets in the HDF5 file - outstr = '' + outstr = "" for domain in self.mgxs_lib.domains: for mgxs_type in self.mgxs_lib.mgxs_types: - outstr += 'domain={0} type={1}\n'.format(domain.id, mgxs_type) - avg_key = 'mesh/{}/{}/average'.format(domain.id, mgxs_type) - std_key = 'mesh/{}/{}/std. dev.'.format(domain.id, mgxs_type) - outstr += '{}\n{}\n'.format(f[avg_key][...], f[std_key][...]) + outstr += "domain={0} type={1}\n".format(domain.id, mgxs_type) + avg_key = "mesh/{}/{}/average".format(domain.id, mgxs_type) + std_key = "mesh/{}/{}/std. dev.".format(domain.id, mgxs_type) + outstr += "{}\n{}\n".format(f[avg_key][...], f[std_key][...]) # Hash the results if necessary if hash_output: sha512 = hashlib.sha512() - sha512.update(outstr.encode('utf-8')) + sha512.update(outstr.encode("utf-8")) outstr = sha512.hexdigest() return outstr def _cleanup(self): super()._cleanup() - files = ['mgxs.h5', 'mgxs.xlsx'] + files = ["mgxs.h5", "mgxs.xlsx"] (os.remove(f) for f in files if os.path.exists(f)) def test_mgxs_library_hdf5(): try: - np.set_printoptions(formatter={'float_kind': '{:.8e}'.format}) + np.set_printoptions(formatter={"float_kind": "{:.8e}".format}) model = pwr_pin_cell() - harness = MGXSTestHarness('statepoint.10.h5', model) + harness = MGXSTestHarness("statepoint.10.h5", model) harness.main() finally: np.set_printoptions(formatter=None) diff --git a/tests/regression_tests/mgxs_library_histogram/test.py b/tests/regression_tests/mgxs_library_histogram/test.py index b9905910abc..bfd37d0c841 100644 --- a/tests/regression_tests/mgxs_library_histogram/test.py +++ b/tests/regression_tests/mgxs_library_histogram/test.py @@ -13,20 +13,23 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Initialize a two-group structure - energy_groups = openmc.mgxs.EnergyGroups(group_edges=[0, 0.625, 20.e6]) + energy_groups = openmc.mgxs.EnergyGroups(group_edges=[0, 0.625, 20.0e6]) # Initialize MGXS Library for a few cross section types self.mgxs_lib = openmc.mgxs.Library(self._model.geometry) self.mgxs_lib.by_nuclide = False # Test all MGXS types - self.mgxs_lib.mgxs_types = ['scatter matrix', 'nu-scatter matrix', - 'consistent scatter matrix', - 'consistent nu-scatter matrix'] + self.mgxs_lib.mgxs_types = [ + "scatter matrix", + "nu-scatter matrix", + "consistent scatter matrix", + "consistent nu-scatter matrix", + ] self.mgxs_lib.energy_groups = energy_groups - self.mgxs_lib.scatter_format = 'histogram' + self.mgxs_lib.scatter_format = "histogram" self.mgxs_lib.histogram_bins = 11 - self.mgxs_lib.domain_type = 'material' + self.mgxs_lib.domain_type = "material" self.mgxs_lib.build_library() # Add tallies @@ -42,17 +45,17 @@ def _get_results(self, hash_output=False): self.mgxs_lib.load_from_statepoint(sp) # Build a string from Pandas Dataframe for each MGXS - outstr = '' + outstr = "" for domain in self.mgxs_lib.domains: for mgxs_type in self.mgxs_lib.mgxs_types: mgxs = self.mgxs_lib.get_mgxs(domain, mgxs_type) df = mgxs.get_pandas_dataframe() - outstr += df.to_string() + '\n' + outstr += df.to_string() + "\n" # Hash the results if necessary if hash_output: sha512 = hashlib.sha512() - sha512.update(outstr.encode('utf-8')) + sha512.update(outstr.encode("utf-8")) outstr = sha512.hexdigest() return outstr @@ -60,5 +63,5 @@ def _get_results(self, hash_output=False): def test_mgxs_library_histogram(): model = pwr_pin_cell() - harness = MGXSTestHarness('statepoint.10.h5', model) + harness = MGXSTestHarness("statepoint.10.h5", model) harness.main() diff --git a/tests/regression_tests/mgxs_library_mesh/test.py b/tests/regression_tests/mgxs_library_mesh/test.py index 89c68a75a33..529c20b6e4b 100644 --- a/tests/regression_tests/mgxs_library_mesh/test.py +++ b/tests/regression_tests/mgxs_library_mesh/test.py @@ -12,17 +12,17 @@ def model(): model = openmc.model.Model() fuel = openmc.Material() - fuel.set_density('g/cm3', 10.0) - fuel.add_nuclide('U235', 1.0) + fuel.set_density("g/cm3", 10.0) + fuel.add_nuclide("U235", 1.0) zr = openmc.Material() - zr.set_density('g/cm3', 1.0) - zr.add_nuclide('Zr90', 1.0) + zr.set_density("g/cm3", 1.0) + zr.add_nuclide("Zr90", 1.0) model.materials.extend([fuel, zr]) box1 = openmc.model.RectangularPrism(10.0, 10.0) - box2 = openmc.model.RectangularPrism(20.0, 20.0, boundary_type='reflective') - top = openmc.ZPlane(z0=10.0, boundary_type='vacuum') - bottom = openmc.ZPlane(z0=-10.0, boundary_type='vacuum') + box2 = openmc.model.RectangularPrism(20.0, 20.0, boundary_type="reflective") + top = openmc.ZPlane(z0=10.0, boundary_type="vacuum") + bottom = openmc.ZPlane(z0=-10.0, boundary_type="vacuum") cell1 = openmc.Cell(fill=fuel, region=-box1 & +bottom & -top) cell2 = openmc.Cell(fill=zr, region=+box1 & -box2 & +bottom & -top) model.geometry = openmc.Geometry([cell1, cell2]) @@ -32,7 +32,7 @@ def model(): model.settings.particles = 1000 # Initialize a one-group structure - energy_groups = openmc.mgxs.EnergyGroups([0, 20.e6]) + energy_groups = openmc.mgxs.EnergyGroups([0, 20.0e6]) # Initialize MGXS Library for a few cross section types # for one material-filled cell in the geometry @@ -45,13 +45,13 @@ def model(): model.mgxs_lib.num_delayed_groups = 6 model.mgxs_lib.correction = None # Avoid warning about P0 correction model.mgxs_lib.legendre_order = 3 - model.mgxs_lib.domain_type = 'mesh' + model.mgxs_lib.domain_type = "mesh" # Instantiate a tally mesh mesh = openmc.RegularMesh(mesh_id=1) mesh.dimension = [2, 2] - mesh.lower_left = [-100., -100.] - mesh.width = [100., 100.] + mesh.lower_left = [-100.0, -100.0] + mesh.width = [100.0, 100.0] model.mgxs_lib.domains = [mesh] model.mgxs_lib.build_library() @@ -74,22 +74,22 @@ def _get_results(self, hash_output=False): mgxs_lib.load_from_statepoint(sp) # Build a string from Pandas Dataframe for each 1-group MGXS - outstr = '' + outstr = "" for domain in mgxs_lib.domains: for mgxs_type in mgxs_lib.mgxs_types: mgxs = mgxs_lib.get_mgxs(domain, mgxs_type) df = mgxs.get_pandas_dataframe() - outstr += df.to_string() + '\n' + outstr += df.to_string() + "\n" # Hash the results if necessary if hash_output: sha512 = hashlib.sha512() - sha512.update(outstr.encode('utf-8')) + sha512.update(outstr.encode("utf-8")) outstr = sha512.hexdigest() return outstr def test_mgxs_library_mesh(model): - harness = MGXSTestHarness('statepoint.5.h5', model) + harness = MGXSTestHarness("statepoint.5.h5", model) harness.main() diff --git a/tests/regression_tests/mgxs_library_no_nuclides/test.py b/tests/regression_tests/mgxs_library_no_nuclides/test.py index af14a5dc8f2..ce09a81a1b4 100644 --- a/tests/regression_tests/mgxs_library_no_nuclides/test.py +++ b/tests/regression_tests/mgxs_library_no_nuclides/test.py @@ -13,29 +13,41 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Initialize a two-group structure - energy_groups = openmc.mgxs.EnergyGroups(group_edges=[0, 0.625, 20.e6]) + energy_groups = openmc.mgxs.EnergyGroups(group_edges=[0, 0.625, 20.0e6]) # Initialize MGXS Library for a few cross section types self.mgxs_lib = openmc.mgxs.Library(self._model.geometry) self.mgxs_lib.by_nuclide = False # Test relevant MGXS types - relevant_MGXS_TYPES = [item for item in openmc.mgxs.MGXS_TYPES - if item != 'current'] + relevant_MGXS_TYPES = [ + item for item in openmc.mgxs.MGXS_TYPES if item != "current" + ] # Add in a subset of openmc.mgxs.ARBITRARY_VECTOR_TYPES and # openmc.mgxs.ARBITRARY_MATRIX_TYPES so we can see the code works, # but not use too much resources relevant_MGXS_TYPES += [ - "(n,elastic)", "(n,level)", "(n,2n)", "(n,na)", "(n,nc)", - "(n,gamma)", "(n,a)", "(n,Xa)", "heating", "damage-energy", - "(n,n1)", "(n,a0)", "(n,nc) matrix", "(n,n1) matrix", - "(n,2n) matrix"] - self.mgxs_lib.mgxs_types = tuple(relevant_MGXS_TYPES) + \ - openmc.mgxs.MDGXS_TYPES + "(n,elastic)", + "(n,level)", + "(n,2n)", + "(n,na)", + "(n,nc)", + "(n,gamma)", + "(n,a)", + "(n,Xa)", + "heating", + "damage-energy", + "(n,n1)", + "(n,a0)", + "(n,nc) matrix", + "(n,n1) matrix", + "(n,2n) matrix", + ] + self.mgxs_lib.mgxs_types = tuple(relevant_MGXS_TYPES) + openmc.mgxs.MDGXS_TYPES self.mgxs_lib.energy_groups = energy_groups self.mgxs_lib.num_delayed_groups = 6 self.mgxs_lib.legendre_order = 3 - self.mgxs_lib.domain_type = 'material' + self.mgxs_lib.domain_type = "material" self.mgxs_lib.build_library() # Add tallies @@ -51,17 +63,17 @@ def _get_results(self, hash_output=False): self.mgxs_lib.load_from_statepoint(sp) # Build a string from Pandas Dataframe for each MGXS - outstr = '' + outstr = "" for domain in self.mgxs_lib.domains: for mgxs_type in self.mgxs_lib.mgxs_types: mgxs = self.mgxs_lib.get_mgxs(domain, mgxs_type) df = mgxs.get_pandas_dataframe() - outstr += mgxs_type + '\n' + df.to_string() + '\n' + outstr += mgxs_type + "\n" + df.to_string() + "\n" # Hash the results if necessary if hash_output: sha512 = hashlib.sha512() - sha512.update(outstr.encode('utf-8')) + sha512.update(outstr.encode("utf-8")) outstr = sha512.hexdigest() return outstr @@ -69,5 +81,5 @@ def _get_results(self, hash_output=False): def test_mgxs_library_no_nuclides(): model = pwr_pin_cell() - harness = MGXSTestHarness('statepoint.10.h5', model) + harness = MGXSTestHarness("statepoint.10.h5", model) harness.main() diff --git a/tests/regression_tests/mgxs_library_nuclides/test.py b/tests/regression_tests/mgxs_library_nuclides/test.py index 8a7673565e5..f6723f617d8 100644 --- a/tests/regression_tests/mgxs_library_nuclides/test.py +++ b/tests/regression_tests/mgxs_library_nuclides/test.py @@ -12,27 +12,40 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Initialize a two-group structure - energy_groups = openmc.mgxs.EnergyGroups(group_edges=[0, 0.625, 20.e6]) + energy_groups = openmc.mgxs.EnergyGroups(group_edges=[0, 0.625, 20.0e6]) # Initialize MGXS Library for a few cross section types self.mgxs_lib = openmc.mgxs.Library(self._model.geometry) self.mgxs_lib.by_nuclide = True # Test relevant MGXS types - relevant_MGXS_TYPES = [item for item in openmc.mgxs.MGXS_TYPES - if item != 'current'] + relevant_MGXS_TYPES = [ + item for item in openmc.mgxs.MGXS_TYPES if item != "current" + ] # Add in a subset of openmc.mgxs.ARBITRARY_VECTOR_TYPES and # openmc.mgxs.ARBITRARY_MATRIX_TYPES so we can see the code works, # but not use too much resources relevant_MGXS_TYPES += [ - "(n,elastic)", "(n,level)", "(n,2n)", "(n,na)", "(n,nc)", - "(n,gamma)", "(n,a)", "(n,Xa)", "heating", "damage-energy", - "(n,n1)", "(n,a0)", "(n,nc) matrix", "(n,n1) matrix", - "(n,2n) matrix"] + "(n,elastic)", + "(n,level)", + "(n,2n)", + "(n,na)", + "(n,nc)", + "(n,gamma)", + "(n,a)", + "(n,Xa)", + "heating", + "damage-energy", + "(n,n1)", + "(n,a0)", + "(n,nc) matrix", + "(n,n1) matrix", + "(n,2n) matrix", + ] self.mgxs_lib.mgxs_types = tuple(relevant_MGXS_TYPES) self.mgxs_lib.energy_groups = energy_groups self.mgxs_lib.legendre_order = 3 - self.mgxs_lib.domain_type = 'material' + self.mgxs_lib.domain_type = "material" self.mgxs_lib.build_library() # Add tallies @@ -48,17 +61,17 @@ def _get_results(self, hash_output=True): self.mgxs_lib.load_from_statepoint(sp) # Build a string from Pandas Dataframe for each MGXS - outstr = '' + outstr = "" for domain in self.mgxs_lib.domains: for mgxs_type in self.mgxs_lib.mgxs_types: mgxs = self.mgxs_lib.get_mgxs(domain, mgxs_type) df = mgxs.get_pandas_dataframe() - outstr += df.to_string() + '\n' + outstr += df.to_string() + "\n" # Hash the results if necessary if hash_output: sha512 = hashlib.sha512() - sha512.update(outstr.encode('utf-8')) + sha512.update(outstr.encode("utf-8")) outstr = sha512.hexdigest() return outstr @@ -66,5 +79,5 @@ def _get_results(self, hash_output=True): def test_mgxs_library_nuclides(): model = pwr_pin_cell() - harness = MGXSTestHarness('statepoint.10.h5', model) + harness = MGXSTestHarness("statepoint.10.h5", model) harness.main() diff --git a/tests/regression_tests/mgxs_library_specific_nuclides/test.py b/tests/regression_tests/mgxs_library_specific_nuclides/test.py index 61910e539e8..223dd0ff4a6 100644 --- a/tests/regression_tests/mgxs_library_specific_nuclides/test.py +++ b/tests/regression_tests/mgxs_library_specific_nuclides/test.py @@ -12,28 +12,41 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Initialize a two-group structure - energy_groups = openmc.mgxs.EnergyGroups(group_edges=[0, 0.625, 20.e6]) + energy_groups = openmc.mgxs.EnergyGroups(group_edges=[0, 0.625, 20.0e6]) # Initialize MGXS Library for a few cross section types self.mgxs_lib = openmc.mgxs.Library(self._model.geometry) self.mgxs_lib.by_nuclide = True # Test relevant MGXS types - relevant_MGXS_TYPES = [item for item in openmc.mgxs.MGXS_TYPES - if item != 'current'] + relevant_MGXS_TYPES = [ + item for item in openmc.mgxs.MGXS_TYPES if item != "current" + ] # Add in a subset of openmc.mgxs.ARBITRARY_VECTOR_TYPES and # openmc.mgxs.ARBITRARY_MATRIX_TYPES so we can see the code works, # but not use too much resources relevant_MGXS_TYPES += [ - "(n,elastic)", "(n,level)", "(n,2n)", "(n,na)", "(n,nc)", - "(n,gamma)", "(n,a)", "(n,Xa)", "heating", "damage-energy", - "(n,n1)", "(n,a0)", "(n,nc) matrix", "(n,n1) matrix", - "(n,2n) matrix"] + "(n,elastic)", + "(n,level)", + "(n,2n)", + "(n,na)", + "(n,nc)", + "(n,gamma)", + "(n,a)", + "(n,Xa)", + "heating", + "damage-energy", + "(n,n1)", + "(n,a0)", + "(n,nc) matrix", + "(n,n1) matrix", + "(n,2n) matrix", + ] self.mgxs_lib.mgxs_types = tuple(relevant_MGXS_TYPES) self.mgxs_lib.energy_groups = energy_groups self.mgxs_lib.legendre_order = 3 - self.mgxs_lib.domain_type = 'material' - self.mgxs_lib.nuclides = ['U235', 'Zr90', 'H1'] + self.mgxs_lib.domain_type = "material" + self.mgxs_lib.nuclides = ["U235", "Zr90", "H1"] self.mgxs_lib.build_library() # Add tallies @@ -49,17 +62,17 @@ def _get_results(self, hash_output=True): self.mgxs_lib.load_from_statepoint(sp) # Build a string from Pandas Dataframe for each MGXS - outstr = '' + outstr = "" for domain in self.mgxs_lib.domains: for mgxs_type in self.mgxs_lib.mgxs_types: mgxs = self.mgxs_lib.get_mgxs(domain, mgxs_type) df = mgxs.get_pandas_dataframe() - outstr += df.to_string() + '\n' + outstr += df.to_string() + "\n" # Hash the results if necessary if hash_output: sha512 = hashlib.sha512() - sha512.update(outstr.encode('utf-8')) + sha512.update(outstr.encode("utf-8")) outstr = sha512.hexdigest() return outstr @@ -67,5 +80,5 @@ def _get_results(self, hash_output=True): def test_mgxs_library_specific_nuclides(): model = pwr_pin_cell() - harness = MGXSTestHarness('statepoint.10.h5', model) + harness = MGXSTestHarness("statepoint.10.h5", model) harness.main() diff --git a/tests/regression_tests/microxs/test.py b/tests/regression_tests/microxs/test.py index a35150a1b80..224c5445dce 100644 --- a/tests/regression_tests/microxs/test.py +++ b/tests/regression_tests/microxs/test.py @@ -1,4 +1,5 @@ """Test one-group cross section generation""" + from pathlib import Path import numpy as np @@ -10,6 +11,7 @@ CHAIN_FILE = Path(__file__).parents[2] / "chain_simple.xml" + @pytest.fixture(scope="module") def model(): fuel = openmc.Material(name="uo2") @@ -48,20 +50,32 @@ def model(): @pytest.mark.parametrize("domain_type", ["materials", "mesh"]) def test_from_model(model, domain_type): - if domain_type == 'materials': + if domain_type == "materials": domains = model.materials[:1] - elif domain_type == 'mesh': + elif domain_type == "mesh": mesh = openmc.RegularMesh() mesh.lower_left = (-0.62, -0.62) mesh.upper_right = (0.62, 0.62) mesh.dimension = (3, 3) domains = mesh - nuclides = ['U234', 'U235', 'U238', 'U236', 'O16', 'O17', 'I135', 'Xe135', - 'Xe136', 'Cs135', 'Gd157', 'Gd156'] + nuclides = [ + "U234", + "U235", + "U238", + "U236", + "O16", + "O17", + "I135", + "Xe135", + "Xe136", + "Cs135", + "Gd157", + "Gd156", + ] _, test_xs = get_microxs_and_flux(model, domains, nuclides, chain_file=CHAIN_FILE) - if config['update']: - test_xs[0].to_csv(f'test_reference_{domain_type}.csv') + if config["update"]: + test_xs[0].to_csv(f"test_reference_{domain_type}.csv") - ref_xs = MicroXS.from_csv(f'test_reference_{domain_type}.csv') + ref_xs = MicroXS.from_csv(f"test_reference_{domain_type}.csv") np.testing.assert_allclose(test_xs[0].data, ref_xs.data, rtol=1e-11) diff --git a/tests/regression_tests/model_xml/test.py b/tests/regression_tests/model_xml/test.py index c67a72ed372..c6534fc27b0 100644 --- a/tests/regression_tests/model_xml/test.py +++ b/tests/regression_tests/model_xml/test.py @@ -18,44 +18,43 @@ class ModelXMLTestHarness(PyAPITestHarness): - """Accept a results file to check against and assume inputs_true is the contents of a model.xml file. - """ + """Accept a results file to check against and assume inputs_true is the contents of a model.xml file.""" + def __init__(self, model=None, inputs_true=None, results_true=None): - statepoint_name = f'statepoint.{model.settings.batches}.h5' + statepoint_name = f"statepoint.{model.settings.batches}.h5" super().__init__(statepoint_name, model, inputs_true) - self.results_true = 'results_true.dat' if results_true is None else results_true + self.results_true = "results_true.dat" if results_true is None else results_true def _build_inputs(self): self._model.export_to_model_xml() def _get_inputs(self): - return open('model.xml').read() + return open("model.xml").read() def _compare_results(self): """Make sure the current results agree with the reference.""" - compare = filecmp.cmp('results_test.dat', self.results_true) + compare = filecmp.cmp("results_test.dat", self.results_true) if not compare: expected = open(self.results_true).readlines() - actual = open('results_test.dat').readlines() - diff = unified_diff(expected, actual, self.results_true, - 'results_test.dat') - print('Result differences:') - print(''.join(colorize(diff))) - os.rename('results_test.dat', 'results_error.dat') - assert compare, 'Results do not agree' + actual = open("results_test.dat").readlines() + diff = unified_diff(expected, actual, self.results_true, "results_test.dat") + print("Result differences:") + print("".join(colorize(diff))) + os.rename("results_test.dat", "results_error.dat") + assert compare, "Results do not agree" def _cleanup(self): super()._cleanup() - if os.path.exists('model.xml'): - os.remove('model.xml') + if os.path.exists("model.xml"): + os.remove("model.xml") test_names = [ - 'adj_cell_rotation', - 'lattice_multiple', - 'energy_laws', - 'photon_production' + "adj_cell_rotation", + "lattice_multiple", + "energy_laws", + "photon_production", ] @@ -63,13 +62,14 @@ def _cleanup(self): def test_model_xml(test_name, request): openmc.reset_auto_ids() - test_path = '../' + test_name + test_path = "../" + test_name results = test_path + "/results_true.dat" inputs = test_name + "_inputs_true.dat" model_name = test_name + "_model" harness = ModelXMLTestHarness(request.getfixturevalue(model_name), inputs, results) harness.main() + def test_input_arg(run_in_tmpdir): pincell = openmc.examples.pwr_pin_cell() @@ -81,20 +81,20 @@ def test_input_arg(run_in_tmpdir): openmc.run() # make sure the executable isn't falling back on the separate XMLs - for f in glob.glob('*.xml'): + for f in glob.glob("*.xml"): os.remove(f) # now export to a single XML file with a custom name - pincell.export_to_model_xml('pincell.xml') - assert Path('pincell.xml').exists() + pincell.export_to_model_xml("pincell.xml") + assert Path("pincell.xml").exists() # run by specifying that single file - openmc.run(path_input='pincell.xml') + openmc.run(path_input="pincell.xml") # check that this works for plotting too - openmc.plot_geometry(path_input='pincell.xml') + openmc.plot_geometry(path_input="pincell.xml") # now ensure we get an error for an incorrect filename, # even in the presence of other, valid XML files pincell.export_to_model_xml() - with pytest.raises(RuntimeError, match='ex-em-ell.xml'): - openmc.run(path_input='ex-em-ell.xml') \ No newline at end of file + with pytest.raises(RuntimeError, match="ex-em-ell.xml"): + openmc.run(path_input="ex-em-ell.xml") diff --git a/tests/regression_tests/multipole/test.py b/tests/regression_tests/multipole/test.py index 2f7369a9db5..fcaffcb2b11 100644 --- a/tests/regression_tests/multipole/test.py +++ b/tests/regression_tests/multipole/test.py @@ -12,14 +12,14 @@ def make_model(): # Materials moderator = openmc.Material(material_id=1) - moderator.set_density('g/cc', 1.0) - moderator.add_nuclide('H1', 2.0) - moderator.add_nuclide('O16', 1.0) - moderator.add_s_alpha_beta('c_H_in_H2O') + moderator.set_density("g/cc", 1.0) + moderator.add_nuclide("H1", 2.0) + moderator.add_nuclide("O16", 1.0) + moderator.add_s_alpha_beta("c_H_in_H2O") dense_fuel = openmc.Material(material_id=2) - dense_fuel.set_density('g/cc', 4.5) - dense_fuel.add_nuclide('U235', 1.0) + dense_fuel.set_density("g/cc", 4.5) + dense_fuel.add_nuclide("U235", 1.0) model.materials += [moderator, dense_fuel] @@ -37,7 +37,7 @@ def make_model(): lat.dimension = [2, 2] lat.lower_left = [-2.0, -2.0] lat.pitch = [2.0, 2.0] - lat.universes = [[fuel_univ]*2]*2 + lat.universes = [[fuel_univ] * 2] * 2 lat.outer = mod_univ x0 = openmc.XPlane(x0=-3.0) @@ -45,7 +45,7 @@ def make_model(): y0 = openmc.YPlane(y0=-3.0) y1 = openmc.YPlane(y0=3.0) for s in [x0, x1, y0, y1]: - s.boundary_type = 'reflective' + s.boundary_type = "reflective" c101 = openmc.Cell(cell_id=101, fill=lat, region=+x0 & -x1 & +y0 & -y1) model.geometry.root_universe = openmc.Universe(universe_id=0, cells=(c101,)) @@ -53,14 +53,15 @@ def make_model(): model.settings.batches = 5 model.settings.inactive = 0 model.settings.particles = 1000 - model.settings.source = openmc.IndependentSource(space=openmc.stats.Box( - [-1, -1, -1], [1, 1, 1])) - model.settings.temperature = {'tolerance': 1000, 'multipole': True} + model.settings.source = openmc.IndependentSource( + space=openmc.stats.Box([-1, -1, -1], [1, 1, 1]) + ) + model.settings.temperature = {"tolerance": 1000, "multipole": True} # Tallies tally = openmc.Tally() - tally.nuclides = ['U235', 'O16', 'total'] - tally.scores = ['total', 'fission', '(n,gamma)', 'elastic', '(n,p)'] + tally.nuclides = ["U235", "O16", "total"] + tally.scores = ["total", "fission", "(n,gamma)", "elastic", "(n,p)"] model.tallies.append(tally) return model @@ -69,12 +70,12 @@ def make_model(): class MultipoleTestHarness(PyAPITestHarness): def _get_results(self): outstr = super()._get_results() - su = openmc.Summary('summary.h5') + su = openmc.Summary("summary.h5") outstr += str(su.geometry.get_all_cells()[11]) return outstr def test_multipole(): model = make_model() - harness = MultipoleTestHarness('statepoint.5.h5', model) + harness = MultipoleTestHarness("statepoint.5.h5", model) harness.main() diff --git a/tests/regression_tests/ncrystal/test.py b/tests/regression_tests/ncrystal/test.py index e1c1e5ed6f0..480978c5ec2 100644 --- a/tests/regression_tests/ncrystal/test.py +++ b/tests/regression_tests/ncrystal/test.py @@ -10,14 +10,14 @@ from tests.testing_harness import PyAPITestHarness pytestmark = pytest.mark.skipif( - not openmc.lib._ncrystal_enabled(), - reason="NCrystal materials are not enabled.") + not openmc.lib._ncrystal_enabled(), reason="NCrystal materials are not enabled." +) def pencil_beam_model(cfg, E0, N): """Return an openmc.Model() object for a monoenergetic pencil - beam hitting a 1 mm sphere filled with the material defined by - the cfg string, and compute the angular distribution""" + beam hitting a 1 mm sphere filled with the material defined by + the cfg string, and compute the angular distribution""" # Material definition @@ -53,7 +53,7 @@ def pencil_beam_model(cfg, E0, N): tally1 = openmc.Tally(name="angular distribution") tally1.scores = ["current"] filter1 = openmc.SurfaceFilter(sample_sphere) - filter2 = openmc.PolarFilter(np.linspace(0, pi, 180+1)) + filter2 = openmc.PolarFilter(np.linspace(0, pi, 180 + 1)) filter3 = openmc.CellFromFilter(cell1) tally1.filters = [filter1, filter2, filter3] tallies = openmc.Tallies([tally1]) @@ -67,7 +67,7 @@ def _get_results(self): # Read the statepoint file. with openmc.StatePoint(self._sp_name) as sp: - tal = sp.get_tally(name='angular distribution') + tal = sp.get_tally(name="angular distribution") df = tal.get_pandas_dataframe() return df.to_string() @@ -76,9 +76,9 @@ def test_ncrystal(): n_particles = 100000 T = 293.6 # K E0 = 0.012 # eV - cfg = 'Al_sg225.ncmat' + cfg = "Al_sg225.ncmat" test = pencil_beam_model(cfg, E0, n_particles) - harness = NCrystalTest('statepoint.10.h5', model=test) + harness = NCrystalTest("statepoint.10.h5", model=test) harness.main() @@ -86,20 +86,21 @@ def test_cfg_from_xml(): """Make sure the cfg string is read by from_xml method""" n_particles = 100000 E0 = 0.012 # eV - cfg = 'Al_sg225.ncmat' + cfg = "Al_sg225.ncmat" model = pencil_beam_model(cfg, E0, n_particles) - #export the original material generated with cfg string - model.materials.export_to_xml('materials.xml.orig') - expected = open('materials.xml.orig', 'r').readlines() - #read back the original material - mats_from_xml = openmc.Materials.from_xml('materials.xml.orig') - #export again - mats_from_xml.export_to_xml('materials.xml.after') - actual = open('materials.xml.after', 'r').readlines() - compare = filecmp.cmp('materials.xml.orig','materials.xml.after') + # export the original material generated with cfg string + model.materials.export_to_xml("materials.xml.orig") + expected = open("materials.xml.orig", "r").readlines() + # read back the original material + mats_from_xml = openmc.Materials.from_xml("materials.xml.orig") + # export again + mats_from_xml.export_to_xml("materials.xml.after") + actual = open("materials.xml.after", "r").readlines() + compare = filecmp.cmp("materials.xml.orig", "materials.xml.after") if not compare: - diff = unified_diff(expected, actual, 'materials.xml.orig', - 'materials.xml.after') - print('Input differences:') - print(''.join(diff)) - assert compare, 'Materials not read correctly from XML' + diff = unified_diff( + expected, actual, "materials.xml.orig", "materials.xml.after" + ) + print("Input differences:") + print("".join(diff)) + assert compare, "Materials not read correctly from XML" diff --git a/tests/regression_tests/output/test.py b/tests/regression_tests/output/test.py index d9568ba4b0f..437f4c63d21 100644 --- a/tests/regression_tests/output/test.py +++ b/tests/regression_tests/output/test.py @@ -11,18 +11,17 @@ def _test_output_created(self): TestHarness._test_output_created(self) # Check for the summary. - summary = glob.glob(os.path.join(os.getcwd(), 'summary.*')) - assert len(summary) == 1, 'Either multiple or no summary file exists.' - assert summary[0].endswith('h5'),\ - 'Summary file is not a HDF5 file.' + summary = glob.glob(os.path.join(os.getcwd(), "summary.*")) + assert len(summary) == 1, "Either multiple or no summary file exists." + assert summary[0].endswith("h5"), "Summary file is not a HDF5 file." def _cleanup(self): TestHarness._cleanup(self) - f = 'summary.h5' + f = "summary.h5" if os.path.exists(f): os.remove(f) def test_output(): - harness = OutputTestHarness('statepoint.10.h5') + harness = OutputTestHarness("statepoint.10.h5") harness.main() diff --git a/tests/regression_tests/particle_restart_eigval/test.py b/tests/regression_tests/particle_restart_eigval/test.py index f9d22edb8a2..0aa9c5b528c 100644 --- a/tests/regression_tests/particle_restart_eigval/test.py +++ b/tests/regression_tests/particle_restart_eigval/test.py @@ -2,5 +2,5 @@ def test_particle_restart_eigval(): - harness = ParticleRestartTestHarness('particle_11_254.h5') + harness = ParticleRestartTestHarness("particle_11_254.h5") harness.main() diff --git a/tests/regression_tests/particle_restart_fixed/test.py b/tests/regression_tests/particle_restart_fixed/test.py index e87bcf8b5eb..db74543bd17 100644 --- a/tests/regression_tests/particle_restart_fixed/test.py +++ b/tests/regression_tests/particle_restart_fixed/test.py @@ -2,5 +2,5 @@ def test_particle_restart_fixed(): - harness = ParticleRestartTestHarness('particle_4_241.h5') + harness = ParticleRestartTestHarness("particle_4_241.h5") harness.main() diff --git a/tests/regression_tests/periodic/test.py b/tests/regression_tests/periodic/test.py index 73fe8a83e44..b9f4112a79e 100644 --- a/tests/regression_tests/periodic/test.py +++ b/tests/regression_tests/periodic/test.py @@ -9,32 +9,33 @@ def box_model(): model = openmc.model.Model() # Define materials water = openmc.Material() - water.add_nuclide('H1', 2.0) - water.add_nuclide('O16', 1.0) - water.add_s_alpha_beta('c_H_in_H2O') - water.set_density('g/cc', 1.0) + water.add_nuclide("H1", 2.0) + water.add_nuclide("O16", 1.0) + water.add_s_alpha_beta("c_H_in_H2O") + water.set_density("g/cc", 1.0) fuel = openmc.Material() - fuel.add_nuclide('U235', 1.0) - fuel.set_density('g/cc', 4.5) + fuel.add_nuclide("U235", 1.0) + fuel.set_density("g/cc", 4.5) # Define geometry - x_min = openmc.XPlane(surface_id=1, x0=0., boundary_type='periodic') - x_max = openmc.XPlane(surface_id=2, x0=5., boundary_type='reflective') + x_min = openmc.XPlane(surface_id=1, x0=0.0, boundary_type="periodic") + x_max = openmc.XPlane(surface_id=2, x0=5.0, boundary_type="reflective") - y_min = openmc.YPlane(surface_id=3, y0=0., boundary_type='periodic') - y_max = openmc.YPlane(surface_id=4, y0=5., boundary_type='reflective') + y_min = openmc.YPlane(surface_id=3, y0=0.0, boundary_type="periodic") + y_max = openmc.YPlane(surface_id=4, y0=5.0, boundary_type="reflective") y_min.periodic_surface = x_min - z_min = openmc.ZPlane(surface_id=5, z0=-5., boundary_type='periodic') - z_max = openmc.Plane(surface_id=6, a=0, b=0, c=1, d=5., - boundary_type='periodic') - z_cyl = openmc.ZCylinder(surface_id=7, x0=2.5, y0=0., r=2.0) + z_min = openmc.ZPlane(surface_id=5, z0=-5.0, boundary_type="periodic") + z_max = openmc.Plane(surface_id=6, a=0, b=0, c=1, d=5.0, boundary_type="periodic") + z_cyl = openmc.ZCylinder(surface_id=7, x0=2.5, y0=0.0, r=2.0) - outside_cyl = openmc.Cell(1, fill=water, region=( - +x_min & -x_max & +y_min & -y_max & +z_min & -z_max & +z_cyl)) - inside_cyl = openmc.Cell(2, fill=fuel, region=( - +y_min & +z_min & -z_max & -z_cyl)) + outside_cyl = openmc.Cell( + 1, + fill=water, + region=(+x_min & -x_max & +y_min & -y_max & +z_min & -z_max & +z_cyl), + ) + inside_cyl = openmc.Cell(2, fill=fuel, region=(+y_min & +z_min & -z_max & -z_cyl)) root_universe = openmc.Universe(0, cells=(outside_cyl, inside_cyl)) model.geometry = openmc.Geometry(root_universe) @@ -42,12 +43,12 @@ def box_model(): model.settings.particles = 1000 model.settings.batches = 4 model.settings.inactive = 0 - model.settings.source = openmc.IndependentSource(space=openmc.stats.Box( - (0, 0, 0), (5, 5, 0)) + model.settings.source = openmc.IndependentSource( + space=openmc.stats.Box((0, 0, 0), (5, 5, 0)) ) return model def test_periodic(box_model): - harness = PyAPITestHarness('statepoint.4.h5', box_model) + harness = PyAPITestHarness("statepoint.4.h5", box_model) harness.main() diff --git a/tests/regression_tests/periodic_6fold/test.py b/tests/regression_tests/periodic_6fold/test.py index 272c65df57f..b6fbe038b30 100644 --- a/tests/regression_tests/periodic_6fold/test.py +++ b/tests/regression_tests/periodic_6fold/test.py @@ -12,32 +12,32 @@ def model(): # Define materials water = openmc.Material() - water.add_nuclide('H1', 2.0) - water.add_nuclide('O16', 1.0) - water.add_s_alpha_beta('c_H_in_H2O') - water.set_density('g/cc', 1.0) + water.add_nuclide("H1", 2.0) + water.add_nuclide("O16", 1.0) + water.add_s_alpha_beta("c_H_in_H2O") + water.set_density("g/cc", 1.0) fuel = openmc.Material() - fuel.add_nuclide('U235', 1.0) - fuel.set_density('g/cc', 4.5) + fuel.add_nuclide("U235", 1.0) + fuel.set_density("g/cc", 4.5) # Define the geometry. Note that this geometry is somewhat non-sensical # (it essentially defines a circle of half-cylinders), but it is # designed so that periodic and reflective BCs will give different # answers. - theta1 = (-1/6 + 1/2) * pi - theta2 = (1/6 - 1/2) * pi - plane1 = openmc.Plane(a=cos(theta1), b=sin(theta1), boundary_type='periodic') - plane2 = openmc.Plane(a=cos(theta2), b=sin(theta2), boundary_type='periodic') + theta1 = (-1 / 6 + 1 / 2) * pi + theta2 = (1 / 6 - 1 / 2) * pi + plane1 = openmc.Plane(a=cos(theta1), b=sin(theta1), boundary_type="periodic") + plane2 = openmc.Plane(a=cos(theta2), b=sin(theta2), boundary_type="periodic") - x_max = openmc.XPlane(5., boundary_type='reflective') + x_max = openmc.XPlane(5.0, boundary_type="reflective") - z_cyl = openmc.ZCylinder(x0=3*cos(pi/6), y0=3*sin(pi/6), r=2.0) + z_cyl = openmc.ZCylinder(x0=3 * cos(pi / 6), y0=3 * sin(pi / 6), r=2.0) - outside_cyl = openmc.Cell(1, fill=water, region=( - +plane1 & +plane2 & -x_max & +z_cyl)) - inside_cyl = openmc.Cell(2, fill=fuel, region=( - +plane1 & +plane2 & -z_cyl)) + outside_cyl = openmc.Cell( + 1, fill=water, region=(+plane1 & +plane2 & -x_max & +z_cyl) + ) + inside_cyl = openmc.Cell(2, fill=fuel, region=(+plane1 & +plane2 & -z_cyl)) root_universe = openmc.Universe(0, cells=(outside_cyl, inside_cyl)) model.geometry = openmc.Geometry(root_universe) @@ -46,12 +46,12 @@ def model(): model.settings.particles = 1000 model.settings.batches = 4 model.settings.inactive = 0 - model.settings.source = openmc.IndependentSource(space=openmc.stats.Box( - (0, 0, 0), (5, 5, 0)) + model.settings.source = openmc.IndependentSource( + space=openmc.stats.Box((0, 0, 0), (5, 5, 0)) ) return model def test_periodic(model): - harness = PyAPITestHarness('statepoint.4.h5', model) + harness = PyAPITestHarness("statepoint.4.h5", model) harness.main() diff --git a/tests/regression_tests/periodic_hex/test.py b/tests/regression_tests/periodic_hex/test.py index db9f6cfd5b0..3f96bc20054 100644 --- a/tests/regression_tests/periodic_hex/test.py +++ b/tests/regression_tests/periodic_hex/test.py @@ -9,10 +9,10 @@ def hex_model(): model = openmc.model.Model() fuel = openmc.Material() - fuel.add_nuclide('U235', 1.0) - fuel.set_density('g/cc', 4.5) + fuel.add_nuclide("U235", 1.0) + fuel.set_density("g/cc", 4.5) - hex_prism = openmc.model.HexagonalPrism(10.0, boundary_type='periodic') + hex_prism = openmc.model.HexagonalPrism(10.0, boundary_type="periodic") cell = openmc.Cell(fill=fuel, region=-hex_prism) model.geometry = openmc.Geometry([cell]) @@ -24,5 +24,5 @@ def hex_model(): def test_periodic_hex(hex_model): - harness = PyAPITestHarness('statepoint.5.h5', hex_model) + harness = PyAPITestHarness("statepoint.5.h5", hex_model) harness.main() diff --git a/tests/regression_tests/photon_production/test.py b/tests/regression_tests/photon_production/test.py index 150448a12f4..43e2f184e89 100644 --- a/tests/regression_tests/photon_production/test.py +++ b/tests/regression_tests/photon_production/test.py @@ -8,14 +8,14 @@ def model(): model = openmc.model.Model() mat = openmc.Material() - mat.set_density('g/cm3', 2.6989) - mat.add_nuclide('Al27', 1.0) + mat.set_density("g/cm3", 2.6989) + mat.add_nuclide("Al27", 1.0) model.materials.append(mat) - cyl = openmc.XCylinder(r=1.0, boundary_type='vacuum') - x_plane_left = openmc.XPlane(-1.0, boundary_type='vacuum') + cyl = openmc.XCylinder(r=1.0, boundary_type="vacuum") + x_plane_left = openmc.XPlane(-1.0, boundary_type="vacuum") x_plane_center = openmc.XPlane(1.0) - x_plane_right = openmc.XPlane(1.0e9, boundary_type='vacuum') + x_plane_right = openmc.XPlane(1.0e9, boundary_type="vacuum") inner_cyl_left = openmc.Cell() inner_cyl_right = openmc.Cell() @@ -31,42 +31,48 @@ def model(): source.space = openmc.stats.Point((0, 0, 0)) source.angle = openmc.stats.Monodirectional() source.energy = openmc.stats.Discrete([14.0e6], [1.0]) - source.particle = 'neutron' + source.particle = "neutron" model.settings.particles = 10000 - model.settings.run_mode = 'fixed source' + model.settings.run_mode = "fixed source" model.settings.batches = 1 model.settings.photon_transport = True - model.settings.electron_treatment = 'ttb' - model.settings.cutoff = {'energy_photon' : 1000.0} + model.settings.electron_treatment = "ttb" + model.settings.cutoff = {"energy_photon": 1000.0} model.settings.source = source surface_filter = openmc.SurfaceFilter(cyl) - particle_filter = openmc.ParticleFilter(['neutron', 'photon', 'electron', 'positron']) + particle_filter = openmc.ParticleFilter( + ["neutron", "photon", "electron", "positron"] + ) current_tally = openmc.Tally() current_tally.filters = [surface_filter, particle_filter] - current_tally.scores = ['current'] + current_tally.scores = ["current"] tally_tracklength = openmc.Tally() tally_tracklength.filters = [particle_filter] - tally_tracklength.scores = ['total', '(n,gamma)'] # heating doesn't work with tracklength - tally_tracklength.nuclides = ['Al27', 'total'] - tally_tracklength.estimator = 'tracklength' + tally_tracklength.scores = [ + "total", + "(n,gamma)", + ] # heating doesn't work with tracklength + tally_tracklength.nuclides = ["Al27", "total"] + tally_tracklength.estimator = "tracklength" tally_collision = openmc.Tally() tally_collision.filters = [particle_filter] - tally_collision.scores = ['total', 'heating', '(n,gamma)'] - tally_collision.nuclides = ['Al27', 'total'] - tally_collision.estimator = 'collision' + tally_collision.scores = ["total", "heating", "(n,gamma)"] + tally_collision.nuclides = ["Al27", "total"] + tally_collision.estimator = "collision" tally_analog = openmc.Tally() tally_analog.filters = [particle_filter] - tally_analog.scores = ['total', 'heating', '(n,gamma)'] - tally_analog.nuclides = ['Al27', 'total'] - tally_analog.estimator = 'analog' - model.tallies.extend([current_tally, tally_tracklength, - tally_collision, tally_analog]) + tally_analog.scores = ["total", "heating", "(n,gamma)"] + tally_analog.nuclides = ["Al27", "total"] + tally_analog.estimator = "analog" + model.tallies.extend( + [current_tally, tally_tracklength, tally_collision, tally_analog] + ) return model def test_photon_production(model): - harness = PyAPITestHarness('statepoint.1.h5', model) + harness = PyAPITestHarness("statepoint.1.h5", model) harness.main() diff --git a/tests/regression_tests/photon_production_fission/test.py b/tests/regression_tests/photon_production_fission/test.py index 96665b30867..7539a828363 100644 --- a/tests/regression_tests/photon_production_fission/test.py +++ b/tests/regression_tests/photon_production_fission/test.py @@ -8,11 +8,11 @@ def model(): model = openmc.model.Model() mat = openmc.Material() - mat.set_density('g/cm3', 10.0) - mat.add_nuclide('U235', 1.0) + mat.set_density("g/cm3", 10.0) + mat.add_nuclide("U235", 1.0) model.materials.append(mat) - sph = openmc.Sphere(r=100.0, boundary_type='reflective') + sph = openmc.Sphere(r=100.0, boundary_type="reflective") cell = openmc.Cell(fill=mat, region=-sph) model.geometry = openmc.Geometry([cell]) @@ -20,29 +20,31 @@ def model(): model.settings.batches = 5 model.settings.inactive = 2 model.settings.photon_transport = True - model.settings.source = openmc.IndependentSource(space=openmc.stats.Point((0, 0, 0))) + model.settings.source = openmc.IndependentSource( + space=openmc.stats.Point((0, 0, 0)) + ) - particle_filter = openmc.ParticleFilter(['neutron', 'photon']) + particle_filter = openmc.ParticleFilter(["neutron", "photon"]) tally_tracklength = openmc.Tally() tally_tracklength.filters = [particle_filter] - tally_tracklength.scores = ['fission', 'heating-local'] - tally_tracklength.nuclides = ['U235', 'total'] - tally_tracklength.estimator = 'tracklength' + tally_tracklength.scores = ["fission", "heating-local"] + tally_tracklength.nuclides = ["U235", "total"] + tally_tracklength.estimator = "tracklength" tally_collision = openmc.Tally() tally_collision.filters = [particle_filter] - tally_collision.scores = ['fission', 'heating', 'heating-local'] - tally_collision.nuclides = ['U235', 'total'] - tally_collision.estimator = 'collision' + tally_collision.scores = ["fission", "heating", "heating-local"] + tally_collision.nuclides = ["U235", "total"] + tally_collision.estimator = "collision" tally_analog = openmc.Tally() tally_analog.filters = [particle_filter] - tally_analog.scores = ['fission', 'heating', 'heating-local'] - tally_analog.nuclides = ['U235', 'total'] - tally_analog.estimator = 'analog' + tally_analog.scores = ["fission", "heating", "heating-local"] + tally_analog.nuclides = ["U235", "total"] + tally_analog.estimator = "analog" model.tallies.extend([tally_tracklength, tally_collision, tally_analog]) return model def test_photon_production_fission(model): - harness = PyAPITestHarness('statepoint.5.h5', model) + harness = PyAPITestHarness("statepoint.5.h5", model) harness.main() diff --git a/tests/regression_tests/photon_source/test.py b/tests/regression_tests/photon_source/test.py index c2eb1476de2..2b1e844272c 100644 --- a/tests/regression_tests/photon_source/test.py +++ b/tests/regression_tests/photon_source/test.py @@ -10,12 +10,12 @@ class SourceTestHarness(PyAPITestHarness): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) mat = openmc.Material() - mat.set_density('g/cm3', 0.998207) - mat.add_element('H', 0.111894) - mat.add_element('O', 0.888106) + mat.set_density("g/cm3", 0.998207) + mat.add_element("H", 0.111894) + mat.add_element("O", 0.888106) self._model.materials = openmc.Materials([mat]) - sphere = openmc.Sphere(r=1.0e9, boundary_type='reflective') + sphere = openmc.Sphere(r=1.0e9, boundary_type="reflective") inside_sphere = openmc.Cell() inside_sphere.region = -sphere inside_sphere.fill = mat @@ -25,26 +25,26 @@ def __init__(self, *args, **kwargs): source.space = openmc.stats.Point((0, 0, 0)) source.angle = openmc.stats.Isotropic() source.energy = openmc.stats.Discrete([10.0e6], [1.0]) - source.particle = 'photon' + source.particle = "photon" settings = openmc.Settings() settings.particles = 10000 settings.batches = 1 settings.photon_transport = True - settings.electron_treatment = 'ttb' - settings.cutoff = {'energy_photon' : 1000.0} - settings.run_mode = 'fixed source' + settings.electron_treatment = "ttb" + settings.cutoff = {"energy_photon": 1000.0} + settings.run_mode = "fixed source" settings.source = source self._model.settings = settings - particle_filter = openmc.ParticleFilter('photon') + particle_filter = openmc.ParticleFilter("photon") tally = openmc.Tally() tally.filters = [particle_filter] - tally.scores = ['flux', '(n,gamma)'] + tally.scores = ["flux", "(n,gamma)"] tallies = openmc.Tallies([tally]) self._model.tallies = tallies def test_photon_source(): - harness = SourceTestHarness('statepoint.1.h5', model=openmc.Model()) + harness = SourceTestHarness("statepoint.1.h5", model=openmc.Model()) harness.main() diff --git a/tests/regression_tests/plot/test.py b/tests/regression_tests/plot/test.py index 30d9ab0d08e..62cfb5ea933 100644 --- a/tests/regression_tests/plot/test.py +++ b/tests/regression_tests/plot/test.py @@ -3,6 +3,5 @@ def test_plot(): - harness = PlotTestHarness(('plot_1.png', 'plot_2.png', 'plot_3.png', - 'plot_4.h5')) + harness = PlotTestHarness(("plot_1.png", "plot_2.png", "plot_3.png", "plot_4.h5")) harness.main() diff --git a/tests/regression_tests/plot_overlaps/test.py b/tests/regression_tests/plot_overlaps/test.py index 0828c6e2551..066856d1e35 100644 --- a/tests/regression_tests/plot_overlaps/test.py +++ b/tests/regression_tests/plot_overlaps/test.py @@ -3,6 +3,5 @@ def test_plot_overlap(): - harness = PlotTestHarness(('plot_1.png', 'plot_2.png', 'plot_3.png', - 'plot_4.h5')) + harness = PlotTestHarness(("plot_1.png", "plot_2.png", "plot_3.png", "plot_4.h5")) harness.main() diff --git a/tests/regression_tests/plot_projections/test.py b/tests/regression_tests/plot_projections/test.py index cf18503f4b8..314ecc22e8a 100644 --- a/tests/regression_tests/plot_projections/test.py +++ b/tests/regression_tests/plot_projections/test.py @@ -1,6 +1,15 @@ from tests.testing_harness import PlotTestHarness from tests.regression_tests import config + def test_plot(): - harness = PlotTestHarness(('plot_1.png', 'example1.png', 'example2.png', 'example3.png', 'orthographic_example1.png')) + harness = PlotTestHarness( + ( + "plot_1.png", + "example1.png", + "example2.png", + "example3.png", + "orthographic_example1.png", + ) + ) harness.main() diff --git a/tests/regression_tests/plot_voxel/test.py b/tests/regression_tests/plot_voxel/test.py index e034decf79e..2962c70d8a7 100644 --- a/tests/regression_tests/plot_voxel/test.py +++ b/tests/regression_tests/plot_voxel/test.py @@ -4,8 +4,11 @@ from tests.regression_tests import config -vtk = pytest.importorskip('vtk') +vtk = pytest.importorskip("vtk") + def test_plot_voxel(): - harness = PlotTestHarness(('plot_4.h5', 'plot.vti'), voxel_convert_checks=['plot_4.h5']) + harness = PlotTestHarness( + ("plot_4.h5", "plot.vti"), voxel_convert_checks=["plot_4.h5"] + ) harness.main() diff --git a/tests/regression_tests/ptables_off/test.py b/tests/regression_tests/ptables_off/test.py index cc316f8c3e2..3dabae3d3bf 100644 --- a/tests/regression_tests/ptables_off/test.py +++ b/tests/regression_tests/ptables_off/test.py @@ -2,5 +2,5 @@ def test_ptables_off(): - harness = TestHarness('statepoint.10.h5') + harness = TestHarness("statepoint.10.h5") harness.main() diff --git a/tests/regression_tests/pulse_height/test.py b/tests/regression_tests/pulse_height/test.py index 90d960f6640..ccb9cb00889 100644 --- a/tests/regression_tests/pulse_height/test.py +++ b/tests/regression_tests/pulse_height/test.py @@ -7,38 +7,38 @@ @pytest.fixture def sphere_model(): - + model = openmc.model.Model() # Define materials NaI = openmc.Material() - NaI.set_density('g/cc', 3.7) - NaI.add_element('Na', 1.0) - NaI.add_element('I', 1.0) + NaI.set_density("g/cc", 3.7) + NaI.add_element("Na", 1.0) + NaI.add_element("I", 1.0) model.materials = openmc.Materials([NaI]) # Define geometry: two spheres in each other s1 = openmc.Sphere(r=1) - s2 = openmc.Sphere(r=2, boundary_type='vacuum') - inner_sphere = openmc.Cell(name='inner sphere', fill=NaI, region=-s1) - outer_sphere = openmc.Cell(name='outer sphere', region=+s1 & -s2) + s2 = openmc.Sphere(r=2, boundary_type="vacuum") + inner_sphere = openmc.Cell(name="inner sphere", fill=NaI, region=-s1) + outer_sphere = openmc.Cell(name="outer sphere", region=+s1 & -s2) model.geometry = openmc.Geometry([inner_sphere, outer_sphere]) # Define settings - model.settings.run_mode = 'fixed source' + model.settings.run_mode = "fixed source" model.settings.batches = 5 model.settings.particles = 100 model.settings.photon_transport = True model.settings.source = openmc.IndependentSource( space=openmc.stats.Point(), energy=openmc.stats.Discrete([1e6], [1]), - particle='photon' + particle="photon", ) # Define tallies tally = openmc.Tally(name="pht tally") - tally.scores = ['pulse-height'] + tally.scores = ["pulse-height"] cell_filter = openmc.CellFilter(inner_sphere) energy_filter = openmc.EnergyFilter(np.linspace(0, 1_000_000, 101)) tally.filters = [cell_filter, energy_filter] @@ -47,7 +47,6 @@ def sphere_model(): return model - def test_pulse_height(sphere_model): - harness = PyAPITestHarness('statepoint.5.h5', sphere_model) + harness = PyAPITestHarness("statepoint.5.h5", sphere_model) harness.main() diff --git a/tests/regression_tests/quadric_surfaces/test.py b/tests/regression_tests/quadric_surfaces/test.py index 94540da040c..b352c3d176b 100755 --- a/tests/regression_tests/quadric_surfaces/test.py +++ b/tests/regression_tests/quadric_surfaces/test.py @@ -2,5 +2,5 @@ def test_quadric_surfaces(): - harness = TestHarness('statepoint.4.h5') + harness = TestHarness("statepoint.4.h5") harness.main() diff --git a/tests/regression_tests/random_ray_adjoint_fixed_source/test.py b/tests/regression_tests/random_ray_adjoint_fixed_source/test.py index 0295e36e9ac..a9bb80f89cf 100644 --- a/tests/regression_tests/random_ray_adjoint_fixed_source/test.py +++ b/tests/regression_tests/random_ray_adjoint_fixed_source/test.py @@ -8,13 +8,13 @@ class MGXSTestHarness(TolerantPyAPITestHarness): def _cleanup(self): super()._cleanup() - f = 'mgxs.h5' + f = "mgxs.h5" if os.path.exists(f): os.remove(f) def test_random_ray_adjoint_fixed_source(): model = random_ray_three_region_cube() - model.settings.random_ray['adjoint'] = True - harness = MGXSTestHarness('statepoint.10.h5', model) + model.settings.random_ray["adjoint"] = True + harness = MGXSTestHarness("statepoint.10.h5", model) harness.main() diff --git a/tests/regression_tests/random_ray_adjoint_k_eff/test.py b/tests/regression_tests/random_ray_adjoint_k_eff/test.py index 44cf1182ae6..d4683d0eaf6 100644 --- a/tests/regression_tests/random_ray_adjoint_k_eff/test.py +++ b/tests/regression_tests/random_ray_adjoint_k_eff/test.py @@ -8,13 +8,13 @@ class MGXSTestHarness(TolerantPyAPITestHarness): def _cleanup(self): super()._cleanup() - f = 'mgxs.h5' + f = "mgxs.h5" if os.path.exists(f): os.remove(f) def test_random_ray_basic(): model = random_ray_lattice() - model.settings.random_ray['adjoint'] = True - harness = MGXSTestHarness('statepoint.10.h5', model) + model.settings.random_ray["adjoint"] = True + harness = MGXSTestHarness("statepoint.10.h5", model) harness.main() diff --git a/tests/regression_tests/random_ray_entropy/test.py b/tests/regression_tests/random_ray_entropy/test.py index a3cba65ad0b..05516d0d17f 100644 --- a/tests/regression_tests/random_ray_entropy/test.py +++ b/tests/regression_tests/random_ray_entropy/test.py @@ -13,21 +13,24 @@ def _get_results(self): statepoint = glob.glob(os.path.join(os.getcwd(), self._sp_name))[0] with StatePoint(statepoint) as sp: # Write out k-combined. - outstr = 'k-combined:\n' - outstr += '{:12.6E} {:12.6E}\n'.format(sp.keff.n, sp.keff.s) + outstr = "k-combined:\n" + outstr += "{:12.6E} {:12.6E}\n".format(sp.keff.n, sp.keff.s) # Write out entropy data. - outstr += 'entropy:\n' - results = ['{:12.6E}'.format(x) for x in sp.entropy] - outstr += '\n'.join(results) + '\n' + outstr += "entropy:\n" + results = ["{:12.6E}".format(x) for x in sp.entropy] + outstr += "\n".join(results) + "\n" return outstr -''' + +""" # This test is adapted from "Monte Carlo power iteration: Entropy and spatial correlations," M. Nowak et al. The cross sections are defined explicitly so that the value for entropy is exactly 9 and the eigenvalue is exactly 1. -''' +""" + + def test_entropy(): - harness = EntropyTestHarness('statepoint.10.h5') + harness = EntropyTestHarness("statepoint.10.h5") harness.main() diff --git a/tests/regression_tests/random_ray_fixed_source_domain/test.py b/tests/regression_tests/random_ray_fixed_source_domain/test.py index 5885a92009a..6edbfaf158a 100644 --- a/tests/regression_tests/random_ray_fixed_source_domain/test.py +++ b/tests/regression_tests/random_ray_fixed_source_domain/test.py @@ -11,7 +11,7 @@ class MGXSTestHarness(TolerantPyAPITestHarness): def _cleanup(self): super()._cleanup() - f = 'mgxs.h5' + f = "mgxs.h5" if os.path.exists(f): os.remove(f) @@ -27,25 +27,25 @@ def test_random_ray_fixed_source(domain_type): source = model.settings.source[0] constraints = source.constraints - if domain_type == 'cell': + if domain_type == "cell": cells = model.geometry.get_all_cells() for key, cell in cells.items(): print(cell.name) - if cell.name == 'infinite source region': - constraints['domain_type'] = 'cell' - constraints['domain_ids'] = [cell.id] - elif domain_type == 'material': + if cell.name == "infinite source region": + constraints["domain_type"] = "cell" + constraints["domain_ids"] = [cell.id] + elif domain_type == "material": materials = model.materials for material in materials: - if material.name == 'source': - constraints['domain_type'] = 'material' - constraints['domain_ids'] = [material.id] - elif domain_type == 'universe': + if material.name == "source": + constraints["domain_type"] = "material" + constraints["domain_ids"] = [material.id] + elif domain_type == "universe": universes = model.geometry.get_all_universes() for key, universe in universes.items(): - if universe.name == 'source universe': - constraints['domain_type'] = 'universe' - constraints['domain_ids'] = [universe.id] + if universe.name == "source universe": + constraints["domain_type"] = "universe" + constraints["domain_ids"] = [universe.id] - harness = MGXSTestHarness('statepoint.10.h5', model) + harness = MGXSTestHarness("statepoint.10.h5", model) harness.main() diff --git a/tests/regression_tests/random_ray_fixed_source_linear/test.py b/tests/regression_tests/random_ray_fixed_source_linear/test.py index 99211024e6e..cf8c17974ec 100644 --- a/tests/regression_tests/random_ray_fixed_source_linear/test.py +++ b/tests/regression_tests/random_ray_fixed_source_linear/test.py @@ -12,7 +12,7 @@ class MGXSTestHarness(TolerantPyAPITestHarness): def _cleanup(self): super()._cleanup() - f = 'mgxs.h5' + f = "mgxs.h5" if os.path.exists(f): os.remove(f) @@ -22,8 +22,8 @@ def test_random_ray_fixed_source_linear(shape): with change_directory(shape): openmc.reset_auto_ids() model = random_ray_three_region_cube() - model.settings.random_ray['source_shape'] = shape + model.settings.random_ray["source_shape"] = shape model.settings.inactive = 20 model.settings.batches = 40 - harness = MGXSTestHarness('statepoint.40.h5', model) + harness = MGXSTestHarness("statepoint.40.h5", model) harness.main() diff --git a/tests/regression_tests/random_ray_fixed_source_normalization/test.py b/tests/regression_tests/random_ray_fixed_source_normalization/test.py index 3fa4ba2a63f..6e55fd72ef7 100644 --- a/tests/regression_tests/random_ray_fixed_source_normalization/test.py +++ b/tests/regression_tests/random_ray_fixed_source_normalization/test.py @@ -11,7 +11,7 @@ class MGXSTestHarness(TolerantPyAPITestHarness): def _cleanup(self): super()._cleanup() - f = 'mgxs.h5' + f = "mgxs.h5" if os.path.exists(f): os.remove(f) @@ -21,7 +21,7 @@ def test_random_ray_fixed_source(normalize): with change_directory(str(normalize)): openmc.reset_auto_ids() model = random_ray_three_region_cube() - model.settings.random_ray['volume_normalized_flux_tallies'] = normalize + model.settings.random_ray["volume_normalized_flux_tallies"] = normalize - harness = MGXSTestHarness('statepoint.10.h5', model) + harness = MGXSTestHarness("statepoint.10.h5", model) harness.main() diff --git a/tests/regression_tests/random_ray_fixed_source_subcritical/test.py b/tests/regression_tests/random_ray_fixed_source_subcritical/test.py index 6c2c569c65e..3115c319fb9 100644 --- a/tests/regression_tests/random_ray_fixed_source_subcritical/test.py +++ b/tests/regression_tests/random_ray_fixed_source_subcritical/test.py @@ -11,7 +11,7 @@ class MGXSTestHarness(TolerantPyAPITestHarness): def _cleanup(self): super()._cleanup() - f = 'mgxs.h5' + f = "mgxs.h5" if os.path.exists(f): os.remove(f) @@ -30,12 +30,12 @@ def test_random_ray_source(shape): # Begin by updating the random ray settings for fixed source settings = model.settings - settings.random_ray['source_shape'] = shape - settings.run_mode = 'fixed source' + settings.random_ray["source_shape"] = shape + settings.run_mode = "fixed source" settings.particles = 30 - settings.random_ray['distance_active'] = 40.0 - settings.random_ray['distance_inactive'] = 40.0 - settings.random_ray['volume_normalized_flux_tallies'] = False + settings.random_ray["distance_active"] = 40.0 + settings.random_ray["distance_inactive"] = 40.0 + settings.random_ray["volume_normalized_flux_tallies"] = False # This problem needs about 2k iterations to converge, # but for regression testing we only need a few hundred @@ -52,31 +52,31 @@ def test_random_ray_source(shape): pitch = 1.26 for material in model.materials: - if material.name == 'Water': + if material.name == "Water": water = material # The new geometry replaces two of the fuel pins with # moderator, reducing k-eff to around 0.84. We also # add a special universe in the corner of one of the moderator # regions to use as a domain constraint for the source - moderator_infinite = openmc.Cell(fill=water, name='moderator infinite') + moderator_infinite = openmc.Cell(fill=water, name="moderator infinite") mu = openmc.Universe(cells=[moderator_infinite]) - moderator_infinite2 = openmc.Cell(fill=water, name='moderator infinite 2') + moderator_infinite2 = openmc.Cell(fill=water, name="moderator infinite 2") mu2 = openmc.Universe(cells=[moderator_infinite2]) n_sub = 10 lattice = openmc.RectLattice() - lattice.lower_left = [-pitch/2.0, -pitch/2.0] - lattice.pitch = [pitch/n_sub, pitch/n_sub] + lattice.lower_left = [-pitch / 2.0, -pitch / 2.0] + lattice.pitch = [pitch / n_sub, pitch / n_sub] lattice.universes = [[mu] * n_sub for _ in range(n_sub)] lattice2 = openmc.RectLattice() - lattice2.lower_left = [-pitch/2.0, -pitch/2.0] - lattice2.pitch = [pitch/n_sub, pitch/n_sub] + lattice2.lower_left = [-pitch / 2.0, -pitch / 2.0] + lattice2.pitch = [pitch / n_sub, pitch / n_sub] lattice2.universes = [[mu] * n_sub for _ in range(n_sub)] - lattice2.universes[n_sub-1][n_sub-1] = mu2 + lattice2.universes[n_sub - 1][n_sub - 1] = mu2 mod_lattice_cell = openmc.Cell(fill=lattice) mod_lattice_uni = openmc.Universe(cells=[mod_lattice_cell]) @@ -90,20 +90,21 @@ def test_random_ray_source(shape): universes = model.geometry.get_all_universes() for universe in universes.values(): - if universe.name == 'pincell': + if universe.name == "pincell": pincell = universe lattice2x2.universes = [ [pincell, mod_lattice_uni], - [mod_lattice_uni, mod_lattice_uni2] + [mod_lattice_uni, mod_lattice_uni2], ] box = openmc.model.RectangularPrism( - pitch*2, pitch*2, boundary_type='reflective') + pitch * 2, pitch * 2, boundary_type="reflective" + ) - assembly = openmc.Cell(fill=lattice2x2, region=-box, name='assembly') + assembly = openmc.Cell(fill=lattice2x2, region=-box, name="assembly") - root = openmc.Universe(name='root universe', cells=[assembly]) + root = openmc.Universe(name="root universe", cells=[assembly]) model.geometry = openmc.Geometry(root) ######################################## @@ -114,20 +115,21 @@ def test_random_ray_source(shape): midpoints = [2.0e-5, 0.0735, 20.0, 2.0e2, 2.0e3, 0.75e6, 2.0e6] energy_distribution = openmc.stats.Discrete(x=midpoints, p=strengths) - lower_left_src = [pitch - pitch/10.0, -pitch, -1.0] - upper_right_src = [pitch, -pitch + pitch/10.0, 1.0] + lower_left_src = [pitch - pitch / 10.0, -pitch, -1.0] + upper_right_src = [pitch, -pitch + pitch / 10.0, 1.0] spatial_distribution = openmc.stats.Box( - lower_left_src, upper_right_src, only_fissionable=False) + lower_left_src, upper_right_src, only_fissionable=False + ) settings.source = openmc.IndependentSource( space=spatial_distribution, energy=energy_distribution, - constraints={'domains': [mu2]}, - strength=1.0 + constraints={"domains": [mu2]}, + strength=1.0, ) ######################################## # Run test - harness = MGXSTestHarness('statepoint.125.h5', model) + harness = MGXSTestHarness("statepoint.125.h5", model) harness.main() diff --git a/tests/regression_tests/random_ray_k_eff/test.py b/tests/regression_tests/random_ray_k_eff/test.py index 8dd0dd9155c..f8cb41accbc 100644 --- a/tests/regression_tests/random_ray_k_eff/test.py +++ b/tests/regression_tests/random_ray_k_eff/test.py @@ -8,12 +8,12 @@ class MGXSTestHarness(TolerantPyAPITestHarness): def _cleanup(self): super()._cleanup() - f = 'mgxs.h5' + f = "mgxs.h5" if os.path.exists(f): os.remove(f) def test_random_ray_basic(): model = random_ray_lattice() - harness = MGXSTestHarness('statepoint.10.h5', model) + harness = MGXSTestHarness("statepoint.10.h5", model) harness.main() diff --git a/tests/regression_tests/random_ray_linear/test.py b/tests/regression_tests/random_ray_linear/test.py index 510c57de8c4..6fbc2093919 100644 --- a/tests/regression_tests/random_ray_linear/test.py +++ b/tests/regression_tests/random_ray_linear/test.py @@ -11,7 +11,7 @@ class MGXSTestHarness(TolerantPyAPITestHarness): def _cleanup(self): super()._cleanup() - f = 'mgxs.h5' + f = "mgxs.h5" if os.path.exists(f): os.remove(f) @@ -21,8 +21,8 @@ def test_random_ray_source(shape): with change_directory(shape): openmc.reset_auto_ids() model = random_ray_lattice() - model.settings.random_ray['source_shape'] = shape + model.settings.random_ray["source_shape"] = shape model.settings.inactive = 20 model.settings.batches = 40 - harness = MGXSTestHarness('statepoint.40.h5', model) + harness = MGXSTestHarness("statepoint.40.h5", model) harness.main() diff --git a/tests/regression_tests/random_ray_volume_estimator/test.py b/tests/regression_tests/random_ray_volume_estimator/test.py index fba4bbbbe6d..acfd11df521 100644 --- a/tests/regression_tests/random_ray_volume_estimator/test.py +++ b/tests/regression_tests/random_ray_volume_estimator/test.py @@ -11,20 +11,17 @@ class MGXSTestHarness(TolerantPyAPITestHarness): def _cleanup(self): super()._cleanup() - f = 'mgxs.h5' + f = "mgxs.h5" if os.path.exists(f): os.remove(f) -@pytest.mark.parametrize("estimator", ["hybrid", - "simulation_averaged", - "naive" - ]) +@pytest.mark.parametrize("estimator", ["hybrid", "simulation_averaged", "naive"]) def test_random_ray_volume_estimator(estimator): with change_directory(estimator): openmc.reset_auto_ids() model = random_ray_three_region_cube() - model.settings.random_ray['volume_estimator'] = estimator + model.settings.random_ray["volume_estimator"] = estimator - harness = MGXSTestHarness('statepoint.10.h5', model) + harness = MGXSTestHarness("statepoint.10.h5", model) harness.main() diff --git a/tests/regression_tests/random_ray_volume_estimator_linear/test.py b/tests/regression_tests/random_ray_volume_estimator_linear/test.py index 94a14f3ad3b..5a2191789de 100644 --- a/tests/regression_tests/random_ray_volume_estimator_linear/test.py +++ b/tests/regression_tests/random_ray_volume_estimator_linear/test.py @@ -11,22 +11,19 @@ class MGXSTestHarness(TolerantPyAPITestHarness): def _cleanup(self): super()._cleanup() - f = 'mgxs.h5' + f = "mgxs.h5" if os.path.exists(f): os.remove(f) -@pytest.mark.parametrize("estimator", ["hybrid", - "simulation_averaged", - "naive" - ]) +@pytest.mark.parametrize("estimator", ["hybrid", "simulation_averaged", "naive"]) def test_random_ray_volume_estimator_linear(estimator): with change_directory(estimator): openmc.reset_auto_ids() model = random_ray_three_region_cube() - model.settings.random_ray['source_shape'] = 'linear' - model.settings.random_ray['volume_estimator'] = estimator + model.settings.random_ray["source_shape"] = "linear" + model.settings.random_ray["volume_estimator"] = estimator model.settings.inactive = 20 model.settings.batches = 40 - harness = MGXSTestHarness('statepoint.40.h5', model) + harness = MGXSTestHarness("statepoint.40.h5", model) harness.main() diff --git a/tests/regression_tests/reflective_plane/test.py b/tests/regression_tests/reflective_plane/test.py index 906174f338a..2dd143ab576 100644 --- a/tests/regression_tests/reflective_plane/test.py +++ b/tests/regression_tests/reflective_plane/test.py @@ -2,5 +2,5 @@ def test_reflective_plane(): - harness = TestHarness('statepoint.10.h5') + harness = TestHarness("statepoint.10.h5") harness.main() diff --git a/tests/regression_tests/resonance_scattering/test.py b/tests/regression_tests/resonance_scattering/test.py index 24629b12b72..a172f518f38 100644 --- a/tests/regression_tests/resonance_scattering/test.py +++ b/tests/regression_tests/resonance_scattering/test.py @@ -8,27 +8,27 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Materials mat = openmc.Material(material_id=1) - mat.set_density('g/cc', 1.0) - mat.add_nuclide('U238', 1.0) - mat.add_nuclide('U235', 0.02) - mat.add_nuclide('Pu239', 0.02) - mat.add_nuclide('H1', 20.0) + mat.set_density("g/cc", 1.0) + mat.add_nuclide("U238", 1.0) + mat.add_nuclide("U235", 0.02) + mat.add_nuclide("Pu239", 0.02) + mat.add_nuclide("H1", 20.0) self._model.materials = openmc.Materials([mat]) # Geometry - dumb_surface = openmc.XPlane(100, boundary_type='reflective') + dumb_surface = openmc.XPlane(100, boundary_type="reflective") c1 = openmc.Cell(cell_id=1, fill=mat, region=-dumb_surface) root_univ = openmc.Universe(universe_id=0, cells=[c1]) self._model.geometry = openmc.Geometry(root_univ) # Resonance elastic scattering settings res_scat_settings = { - 'enable': True, - 'energy_min': 1.0, - 'energy_max': 210.0, - 'method': 'rvs', - 'nuclides': ['U238', 'U235', 'Pu239'] + "enable": True, + "energy_min": 1.0, + "energy_max": 210.0, + "method": "rvs", + "nuclides": ["U238", "U235", "Pu239"], } settings = openmc.Settings() @@ -36,12 +36,12 @@ def __init__(self, *args, **kwargs): settings.inactive = 5 settings.particles = 1000 settings.source = openmc.IndependentSource( - space=openmc.stats.Box([-4, -4, -4], [4, 4, 4])) + space=openmc.stats.Box([-4, -4, -4], [4, 4, 4]) + ) settings.resonance_scattering = res_scat_settings self._model.settings = settings def test_resonance_scattering(): - harness = ResonanceScatteringTestHarness('statepoint.10.h5', - model=openmc.Model()) + harness = ResonanceScatteringTestHarness("statepoint.10.h5", model=openmc.Model()) harness.main() diff --git a/tests/regression_tests/rotation/test.py b/tests/regression_tests/rotation/test.py index 31c3d8d655e..aff0502650e 100644 --- a/tests/regression_tests/rotation/test.py +++ b/tests/regression_tests/rotation/test.py @@ -2,5 +2,5 @@ def test_rotation(): - harness = TestHarness('statepoint.10.h5') + harness = TestHarness("statepoint.10.h5") harness.main() diff --git a/tests/regression_tests/salphabeta/test.py b/tests/regression_tests/salphabeta/test.py index 11bf3bc842a..b11042c1c55 100644 --- a/tests/regression_tests/salphabeta/test.py +++ b/tests/regression_tests/salphabeta/test.py @@ -9,46 +9,46 @@ def make_model(): # Materials m1 = openmc.Material() - m1.set_density('g/cc', 4.5) - m1.add_nuclide('U235', 1.0) - m1.add_nuclide('H1', 1.0) - m1.add_s_alpha_beta('c_H_in_H2O', fraction=0.5) + m1.set_density("g/cc", 4.5) + m1.add_nuclide("U235", 1.0) + m1.add_nuclide("H1", 1.0) + m1.add_s_alpha_beta("c_H_in_H2O", fraction=0.5) m2 = openmc.Material() - m2.set_density('g/cc', 4.5) - m2.add_nuclide('U235', 1.0) - m2.add_nuclide('C0', 1.0) - m2.add_s_alpha_beta('c_Graphite') + m2.set_density("g/cc", 4.5) + m2.add_nuclide("U235", 1.0) + m2.add_nuclide("C0", 1.0) + m2.add_s_alpha_beta("c_Graphite") m3 = openmc.Material() - m3.set_density('g/cc', 4.5) - m3.add_nuclide('U235', 1.0) - m3.add_nuclide('Be9', 1.0) - m3.add_nuclide('O16', 1.0) - m3.add_s_alpha_beta('c_Be_in_BeO') - m3.add_s_alpha_beta('c_O_in_BeO') + m3.set_density("g/cc", 4.5) + m3.add_nuclide("U235", 1.0) + m3.add_nuclide("Be9", 1.0) + m3.add_nuclide("O16", 1.0) + m3.add_s_alpha_beta("c_Be_in_BeO") + m3.add_s_alpha_beta("c_O_in_BeO") m4 = openmc.Material() - m4.set_density('g/cm3', 5.90168) - m4.add_nuclide('H1', 0.3) - m4.add_nuclide('Zr90', 0.15) - m4.add_nuclide('Zr91', 0.1) - m4.add_nuclide('Zr92', 0.1) - m4.add_nuclide('Zr94', 0.05) - m4.add_nuclide('Zr96', 0.05) - m4.add_nuclide('U235', 0.1) - m4.add_nuclide('U238', 0.15) - m4.add_s_alpha_beta('c_Zr_in_ZrH') - m4.add_s_alpha_beta('c_H_in_ZrH') + m4.set_density("g/cm3", 5.90168) + m4.add_nuclide("H1", 0.3) + m4.add_nuclide("Zr90", 0.15) + m4.add_nuclide("Zr91", 0.1) + m4.add_nuclide("Zr92", 0.1) + m4.add_nuclide("Zr94", 0.05) + m4.add_nuclide("Zr96", 0.05) + m4.add_nuclide("U235", 0.1) + m4.add_nuclide("U238", 0.15) + m4.add_s_alpha_beta("c_Zr_in_ZrH") + m4.add_s_alpha_beta("c_H_in_ZrH") model.materials += [m1, m2, m3, m4] # Geometry - x0 = openmc.XPlane(-10, boundary_type='vacuum') + x0 = openmc.XPlane(-10, boundary_type="vacuum") x1 = openmc.XPlane(-5) x2 = openmc.XPlane(0) x3 = openmc.XPlane(5) - x4 = openmc.XPlane(10, boundary_type='vacuum') + x4 = openmc.XPlane(10, boundary_type="vacuum") root_univ = openmc.Universe() @@ -57,7 +57,7 @@ def make_model(): cells = [] for i in range(4): cell = openmc.Cell() - cell.region = +surfs[i] & -surfs[i+1] + cell.region = +surfs[i] & -surfs[i + 1] cell.fill = mats[i] root_univ.add_cell(cell) @@ -67,13 +67,14 @@ def make_model(): model.settings.batches = 5 model.settings.inactive = 0 model.settings.particles = 400 - model.settings.source = openmc.IndependentSource(space=openmc.stats.Box( - [-4, -4, -4], [4, 4, 4])) + model.settings.source = openmc.IndependentSource( + space=openmc.stats.Box([-4, -4, -4], [4, 4, 4]) + ) return model def test_salphabeta(): model = make_model() - harness = PyAPITestHarness('statepoint.5.h5', model) + harness = PyAPITestHarness("statepoint.5.h5", model) harness.main() diff --git a/tests/regression_tests/score_current/test.py b/tests/regression_tests/score_current/test.py index a338a662669..5250165017b 100644 --- a/tests/regression_tests/score_current/test.py +++ b/tests/regression_tests/score_current/test.py @@ -9,17 +9,17 @@ def model(): model = openmc.model.Model() fuel = openmc.Material() - fuel.set_density('g/cm3', 10.0) - fuel.add_nuclide('U235', 1.0) + fuel.set_density("g/cm3", 10.0) + fuel.add_nuclide("U235", 1.0) zr = openmc.Material() - zr.set_density('g/cm3', 1.0) - zr.add_nuclide('Zr90', 1.0) + zr.set_density("g/cm3", 1.0) + zr.add_nuclide("Zr90", 1.0) model.materials.extend([fuel, zr]) box1 = openmc.model.RectangularPrism(10.0, 10.0) - box2 = openmc.model.RectangularPrism(20.0, 20.0, boundary_type='reflective') - top = openmc.ZPlane(z0=10.0, boundary_type='vacuum') - bottom = openmc.ZPlane(z0=-10.0, boundary_type='vacuum') + box2 = openmc.model.RectangularPrism(20.0, 20.0, boundary_type="reflective") + top = openmc.ZPlane(z0=10.0, boundary_type="vacuum") + bottom = openmc.ZPlane(z0=-10.0, boundary_type="vacuum") cell1 = openmc.Cell(fill=fuel, region=-box1 & +bottom & -top) cell2 = openmc.Cell(fill=zr, region=+box1 & -box2 & +bottom & -top) model.geometry = openmc.Geometry([cell1, cell2]) @@ -28,7 +28,6 @@ def model(): model.settings.inactive = 0 model.settings.particles = 1000 - mesh = openmc.Mesh() mesh.lower_left = (-10.0, -10.0, -10.0) mesh.upper_right = (10.0, 10.0, 10.0) @@ -39,15 +38,15 @@ def model(): tally1 = openmc.Tally() tally1.filters = [mesh_surface_filter] - tally1.scores = ['current'] + tally1.scores = ["current"] tally2 = openmc.Tally() tally2.filters = [mesh_surface_filter, energy_filter] - tally2.scores = ['current'] + tally2.scores = ["current"] model.tallies.extend([tally1, tally2]) return model def test_score_current(model): - harness = PyAPITestHarness('statepoint.5.h5', model) + harness = PyAPITestHarness("statepoint.5.h5", model) harness.main() diff --git a/tests/regression_tests/seed/test.py b/tests/regression_tests/seed/test.py index 8b2f031b3e0..ed37e794ebe 100644 --- a/tests/regression_tests/seed/test.py +++ b/tests/regression_tests/seed/test.py @@ -2,5 +2,5 @@ def test_seed(): - harness = TestHarness('statepoint.10.h5') + harness = TestHarness("statepoint.10.h5") harness.main() diff --git a/tests/regression_tests/source/test.py b/tests/regression_tests/source/test.py index c1f4dccff94..f03c1f9526a 100644 --- a/tests/regression_tests/source/test.py +++ b/tests/regression_tests/source/test.py @@ -10,11 +10,11 @@ class SourceTestHarness(PyAPITestHarness): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) mat1 = openmc.Material(material_id=1, temperature=294) - mat1.set_density('g/cm3', 4.5) - mat1.add_nuclide(openmc.Nuclide('U235'), 1.0) + mat1.set_density("g/cm3", 4.5) + mat1.add_nuclide(openmc.Nuclide("U235"), 1.0) self._model.materials = openmc.Materials([mat1]) - sphere = openmc.Sphere(surface_id=1, r=10.0, boundary_type='vacuum') + sphere = openmc.Sphere(surface_id=1, r=10.0, boundary_type="vacuum") inside_sphere = openmc.Cell(cell_id=1) inside_sphere.region = -sphere inside_sphere.fill = mat1 @@ -24,46 +24,47 @@ def __init__(self, *args, **kwargs): self._model.geometry = openmc.Geometry(root) # Create an array of different sources - x_dist = openmc.stats.Uniform(-3., 3.) - y_dist = openmc.stats.Discrete([-4., -1., 3.], [0.2, 0.3, 0.5]) - z_dist = openmc.stats.Tabular([-2., 0., 2.], [0.2, 0.3, 0.2]) - r_dist = openmc.stats.Uniform(2., 3.) - r_dist1 = openmc.stats.PowerLaw(2., 3., 1.) - r_dist2 = openmc.stats.PowerLaw(2., 3., 2.) - cos_theta_dist = openmc.stats.Discrete([cos(pi/4), 0.0, cos(3*pi/4)], - [0.3, 0.4, 0.3]) - phi_dist = openmc.stats.Uniform(0.0, 2*pi) + x_dist = openmc.stats.Uniform(-3.0, 3.0) + y_dist = openmc.stats.Discrete([-4.0, -1.0, 3.0], [0.2, 0.3, 0.5]) + z_dist = openmc.stats.Tabular([-2.0, 0.0, 2.0], [0.2, 0.3, 0.2]) + r_dist = openmc.stats.Uniform(2.0, 3.0) + r_dist1 = openmc.stats.PowerLaw(2.0, 3.0, 1.0) + r_dist2 = openmc.stats.PowerLaw(2.0, 3.0, 2.0) + cos_theta_dist = openmc.stats.Discrete( + [cos(pi / 4), 0.0, cos(3 * pi / 4)], [0.3, 0.4, 0.3] + ) + phi_dist = openmc.stats.Uniform(0.0, 2 * pi) spatial1 = openmc.stats.CartesianIndependent(x_dist, y_dist, z_dist) - spatial2 = openmc.stats.Box([-4., -4., -4.], [4., 4., 4.]) + spatial2 = openmc.stats.Box([-4.0, -4.0, -4.0], [4.0, 4.0, 4.0]) spatial3 = openmc.stats.Point([1.2, -2.3, 0.781]) - spatial4 = openmc.stats.SphericalIndependent(r_dist, cos_theta_dist, - phi_dist, - origin=(1., 1., 0.)) - spatial5 = openmc.stats.CylindricalIndependent(r_dist, phi_dist, - z_dist, - origin=(1., 1., 0.)) - spatial6 = openmc.stats.SphericalIndependent(r_dist2, cos_theta_dist, - phi_dist, - origin=(1., 1., 0.)) - spatial7 = openmc.stats.CylindricalIndependent(r_dist1, phi_dist, - z_dist, - origin=(1., 1., 0.)) + spatial4 = openmc.stats.SphericalIndependent( + r_dist, cos_theta_dist, phi_dist, origin=(1.0, 1.0, 0.0) + ) + spatial5 = openmc.stats.CylindricalIndependent( + r_dist, phi_dist, z_dist, origin=(1.0, 1.0, 0.0) + ) + spatial6 = openmc.stats.SphericalIndependent( + r_dist2, cos_theta_dist, phi_dist, origin=(1.0, 1.0, 0.0) + ) + spatial7 = openmc.stats.CylindricalIndependent( + r_dist1, phi_dist, z_dist, origin=(1.0, 1.0, 0.0) + ) - mu_dist = openmc.stats.Discrete([-1., 0., 1.], [0.5, 0.25, 0.25]) - phi_dist = openmc.stats.Uniform(0., 6.28318530718) + mu_dist = openmc.stats.Discrete([-1.0, 0.0, 1.0], [0.5, 0.25, 0.25]) + phi_dist = openmc.stats.Uniform(0.0, 6.28318530718) angle1 = openmc.stats.PolarAzimuthal(mu_dist, phi_dist) - angle2 = openmc.stats.Monodirectional(reference_uvw=[0., 1., 0.]) + angle2 = openmc.stats.Monodirectional(reference_uvw=[0.0, 1.0, 0.0]) angle3 = openmc.stats.Isotropic() # Note that the definition for E is equivalent to logspace(0, 7) but we # manually take powers because of last-digit differences that may cause # test failures with different versions of numpy E = np.array([10**x for x in np.linspace(0, 7)]) - p = np.sin(np.linspace(0., pi)) - p /= sum(np.diff(E)*p[:-1]) + p = np.sin(np.linspace(0.0, pi)) + p /= sum(np.diff(E) * p[:-1]) energy1 = openmc.stats.Maxwell(1.2895e6) energy2 = openmc.stats.Watt(0.988e6, 2.249e-6) - energy3 = openmc.stats.Tabular(E, p, interpolation='histogram') + energy3 = openmc.stats.Tabular(E, p, interpolation="histogram") energy4 = openmc.stats.Mixture([1, 2, 3], [energy1, energy2, energy3]) time1 = openmc.stats.Uniform(2, 5) @@ -74,17 +75,30 @@ def __init__(self, *args, **kwargs): source4 = openmc.IndependentSource(spatial4, angle3, energy3, strength=0.1) source5 = openmc.IndependentSource(spatial5, angle3, energy3, strength=0.1) source6 = openmc.IndependentSource(spatial5, angle3, energy4, strength=0.1) - source7 = openmc.IndependentSource(spatial6, angle3, energy4, time1, strength=0.1) - source8 = openmc.IndependentSource(spatial7, angle3, energy4, time1, strength=0.1) + source7 = openmc.IndependentSource( + spatial6, angle3, energy4, time1, strength=0.1 + ) + source8 = openmc.IndependentSource( + spatial7, angle3, energy4, time1, strength=0.1 + ) settings = openmc.Settings() settings.batches = 10 settings.inactive = 5 settings.particles = 1000 - settings.source = [source1, source2, source3, source4, source5, source6, source7, source8] + settings.source = [ + source1, + source2, + source3, + source4, + source5, + source6, + source7, + source8, + ] self._model.settings = settings def test_source(): - harness = SourceTestHarness('statepoint.10.h5', model=openmc.Model()) + harness = SourceTestHarness("statepoint.10.h5", model=openmc.Model()) harness.main() diff --git a/tests/regression_tests/source_dlopen/test.py b/tests/regression_tests/source_dlopen/test.py index 0581d6deec4..e71ef09948e 100644 --- a/tests/regression_tests/source_dlopen/test.py +++ b/tests/regression_tests/source_dlopen/test.py @@ -15,43 +15,49 @@ def compile_source(request): """Compile the external source""" # Get build directory and write CMakeLists.txt file - openmc_dir = Path(str(request.config.rootdir)) / 'build' - with open('CMakeLists.txt', 'w') as f: - f.write(textwrap.dedent(""" + openmc_dir = Path(str(request.config.rootdir)) / "build" + with open("CMakeLists.txt", "w") as f: + f.write( + textwrap.dedent( + """ cmake_minimum_required(VERSION 3.10 FATAL_ERROR) project(openmc_sources CXX) add_library(source SHARED source_sampling.cpp) find_package(OpenMC REQUIRED HINTS {}) target_link_libraries(source OpenMC::libopenmc) - """.format(openmc_dir))) + """.format( + openmc_dir + ) + ) + ) # Create temporary build directory and change to there - local_builddir = Path('build') + local_builddir = Path("build") local_builddir.mkdir(exist_ok=True) os.chdir(str(local_builddir)) # Run cmake/make to build the shared libary - subprocess.run(['cmake', os.path.pardir], check=True) - subprocess.run(['make'], check=True) + subprocess.run(["cmake", os.path.pardir], check=True) + subprocess.run(["make"], check=True) os.chdir(os.path.pardir) yield # Remove local build directory when test is complete - shutil.rmtree('build') - os.remove('CMakeLists.txt') + shutil.rmtree("build") + os.remove("CMakeLists.txt") @pytest.fixture def model(): model = openmc.model.Model() natural_lead = openmc.Material(name="natural_lead") - natural_lead.add_element('Pb', 1.0) - natural_lead.set_density('g/cm3', 11.34) + natural_lead.add_element("Pb", 1.0) + natural_lead.set_density("g/cm3", 11.34) model.materials.append(natural_lead) # geometry - surface_sph1 = openmc.Sphere(r=100, boundary_type='vacuum') + surface_sph1 = openmc.Sphere(r=100, boundary_type="vacuum") cell_1 = openmc.Cell(fill=natural_lead, region=-surface_sph1) model.geometry = openmc.Geometry([cell_1]) @@ -59,7 +65,7 @@ def model(): model.settings.batches = 10 model.settings.inactive = 0 model.settings.particles = 1000 - model.settings.run_mode = 'fixed source' + model.settings.run_mode = "fixed source" tally = openmc.Tally() mat_filter = openmc.MaterialFilter([natural_lead]) @@ -68,16 +74,16 @@ def model(): # zero energy_filter = openmc.EnergyFilter([0.0, 2e3, 1e6]) tally.filters = [mat_filter, energy_filter] - tally.scores = ['flux'] + tally.scores = ["flux"] model.tallies = openmc.Tallies([tally]) # custom source from shared library - source = openmc.CompiledSource('build/libsource.so') + source = openmc.CompiledSource("build/libsource.so") model.settings.source = source return model def test_dlopen_source(compile_source, model): - harness = PyAPITestHarness('statepoint.10.h5', model) + harness = PyAPITestHarness("statepoint.10.h5", model) harness.main() diff --git a/tests/regression_tests/source_file/test.py b/tests/regression_tests/source_file/test.py index b4d9c4a31df..28cd56531c7 100644 --- a/tests/regression_tests/source_file/test.py +++ b/tests/regression_tests/source_file/test.py @@ -6,7 +6,7 @@ from tests.testing_harness import * -settings1=""" +settings1 = """ @@ -65,37 +65,34 @@ def update_results(self): def _test_output_created(self): """Make sure statepoint and source files have been created.""" statepoint = glob.glob(os.path.join(os.getcwd(), self._sp_name)) - assert len(statepoint) == 1, 'Either multiple or no statepoint files ' \ - 'exist.' - assert statepoint[0].endswith('h5'), \ - 'Statepoint file is not a HDF5 file.' + assert len(statepoint) == 1, "Either multiple or no statepoint files " "exist." + assert statepoint[0].endswith("h5"), "Statepoint file is not a HDF5 file." - source = glob.glob(os.path.join(os.getcwd(), 'source.10.*')) - assert len(source) == 1, 'Either multiple or no source files exist.' - assert source[0].endswith('h5'), \ - 'Source file is not a HDF5 file.' + source = glob.glob(os.path.join(os.getcwd(), "source.10.*")) + assert len(source) == 1, "Either multiple or no source files exist." + assert source[0].endswith("h5"), "Source file is not a HDF5 file." def _run_openmc_restart(self): # Get the name of the source file. - source = glob.glob(os.path.join(os.getcwd(), 'source.10.*')) + source = glob.glob(os.path.join(os.getcwd(), "source.10.*")) # Write the new settings.xml file. - with open('settings.xml','w') as fh: - fh.write(settings2.format(source[0].split('.')[-1])) + with open("settings.xml", "w") as fh: + fh.write(settings2.format(source[0].split(".")[-1])) # Run OpenMC. self._run_openmc() def _cleanup(self): TestHarness._cleanup(self) - output = glob.glob(os.path.join(os.getcwd(), 'source.*')) + output = glob.glob(os.path.join(os.getcwd(), "source.*")) for f in output: if os.path.exists(f): os.remove(f) - with open('settings.xml','w') as fh: + with open("settings.xml", "w") as fh: fh.write(settings1) def test_source_file(): - harness = SourceFileTestHarness('statepoint.10.h5') + harness = SourceFileTestHarness("statepoint.10.h5") harness.main() diff --git a/tests/regression_tests/source_mcpl_file/test.py b/tests/regression_tests/source_mcpl_file/test.py index 84ec005ae16..f704b8ff875 100644 --- a/tests/regression_tests/source_mcpl_file/test.py +++ b/tests/regression_tests/source_mcpl_file/test.py @@ -5,11 +5,12 @@ import os from tests.testing_harness import * + pytestmark = pytest.mark.skipif( - not openmc.lib._mcpl_enabled(), - reason="MCPL is not enabled.") + not openmc.lib._mcpl_enabled(), reason="MCPL is not enabled." +) -settings1=""" +settings1 = """ @@ -68,34 +69,33 @@ def update_results(self): def _test_output_created(self): """Make sure statepoint and source files have been created.""" statepoint = glob.glob(os.path.join(os.getcwd(), self._sp_name)) - assert len(statepoint) == 1, 'Either multiple or no statepoint files ' \ - 'exist.' - assert statepoint[0].endswith('h5'), \ - 'Statepoint file is not a HDF5 file.' + assert len(statepoint) == 1, "Either multiple or no statepoint files " "exist." + assert statepoint[0].endswith("h5"), "Statepoint file is not a HDF5 file." - source = glob.glob(os.path.join(os.getcwd(), 'source.10.mcpl*')) - assert len(source) == 1, 'Either multiple or no source files exist.' - assert source[0].endswith('mcpl') or source[0].endswith('mcpl.gz'), \ - 'Source file is not a MCPL file.' + source = glob.glob(os.path.join(os.getcwd(), "source.10.mcpl*")) + assert len(source) == 1, "Either multiple or no source files exist." + assert source[0].endswith("mcpl") or source[0].endswith( + "mcpl.gz" + ), "Source file is not a MCPL file." def _run_openmc_restart(self): # Get the name of the source file. - source = glob.glob(os.path.join(os.getcwd(), 'source.10.*')) + source = glob.glob(os.path.join(os.getcwd(), "source.10.*")) # Write the new settings.xml file. - with open('settings.xml','w') as fh: - fh.write(settings2.format(source[0].split('.')[-1])) + with open("settings.xml", "w") as fh: + fh.write(settings2.format(source[0].split(".")[-1])) # Run OpenMC. self._run_openmc() def _cleanup(self): TestHarness._cleanup(self) - output = glob.glob(os.path.join(os.getcwd(), 'source.*')) - with open('settings.xml','w') as fh: + output = glob.glob(os.path.join(os.getcwd(), "source.*")) + with open("settings.xml", "w") as fh: fh.write(settings1) def test_source_file(): - harness = SourceFileTestHarness('statepoint.10.h5') + harness = SourceFileTestHarness("statepoint.10.h5") harness.main() diff --git a/tests/regression_tests/source_parameterized_dlopen/test.py b/tests/regression_tests/source_parameterized_dlopen/test.py index 151fb37356e..3bf77137ea7 100644 --- a/tests/regression_tests/source_parameterized_dlopen/test.py +++ b/tests/regression_tests/source_parameterized_dlopen/test.py @@ -15,43 +15,49 @@ def compile_source(request): """Compile the external source""" # Get build directory and write CMakeLists.txt file - openmc_dir = Path(str(request.config.rootdir)) / 'build' - with open('CMakeLists.txt', 'w') as f: - f.write(textwrap.dedent(""" + openmc_dir = Path(str(request.config.rootdir)) / "build" + with open("CMakeLists.txt", "w") as f: + f.write( + textwrap.dedent( + """ cmake_minimum_required(VERSION 3.10 FATAL_ERROR) project(openmc_sources CXX) add_library(source SHARED parameterized_source_sampling.cpp) find_package(OpenMC REQUIRED HINTS {}) target_link_libraries(source OpenMC::libopenmc) - """.format(openmc_dir))) + """.format( + openmc_dir + ) + ) + ) # Create temporary build directory and change to there - local_builddir = Path('build') + local_builddir = Path("build") local_builddir.mkdir(exist_ok=True) os.chdir(str(local_builddir)) # Run cmake/make to build the shared libary - subprocess.run(['cmake', os.path.pardir], check=True) - subprocess.run(['make'], check=True) + subprocess.run(["cmake", os.path.pardir], check=True) + subprocess.run(["make"], check=True) os.chdir(os.path.pardir) yield # Remove local build directory when test is complete - shutil.rmtree('build') - os.remove('CMakeLists.txt') + shutil.rmtree("build") + os.remove("CMakeLists.txt") @pytest.fixture def model(): model = openmc.model.Model() natural_lead = openmc.Material(name="natural_lead") - natural_lead.add_element('Pb', 1.0) - natural_lead.set_density('g/cm3', 11.34) + natural_lead.add_element("Pb", 1.0) + natural_lead.set_density("g/cm3", 11.34) model.materials.append(natural_lead) # geometry - surface_sph1 = openmc.Sphere(r=100, boundary_type='vacuum') + surface_sph1 = openmc.Sphere(r=100, boundary_type="vacuum") cell_1 = openmc.Cell(fill=natural_lead, region=-surface_sph1) model.geometry = openmc.Geometry([cell_1]) @@ -59,7 +65,7 @@ def model(): model.settings.batches = 10 model.settings.inactive = 0 model.settings.particles = 1000 - model.settings.run_mode = 'fixed source' + model.settings.run_mode = "fixed source" tally = openmc.Tally() mat_filter = openmc.MaterialFilter([natural_lead]) @@ -67,17 +73,17 @@ def model(): # the second bin shouldn't have any results energy_filter = openmc.EnergyFilter([0.0, 2e3, 1e6]) tally.filters = [mat_filter, energy_filter] - tally.scores = ['flux'] + tally.scores = ["flux"] model.tallies = openmc.Tallies([tally]) # custom source from shared library - source = openmc.CompiledSource('build/libsource.so') - source.parameters = '1e3' + source = openmc.CompiledSource("build/libsource.so") + source.parameters = "1e3" model.settings.source = source return model def test_dlopen_source(compile_source, model): - harness = PyAPITestHarness('statepoint.10.h5', model) + harness = PyAPITestHarness("statepoint.10.h5", model) harness.main() diff --git a/tests/regression_tests/sourcepoint_batch/test.py b/tests/regression_tests/sourcepoint_batch/test.py index fdc5ecca12a..6ac386dba19 100644 --- a/tests/regression_tests/sourcepoint_batch/test.py +++ b/tests/regression_tests/sourcepoint_batch/test.py @@ -8,8 +8,8 @@ class SourcepointTestHarness(TestHarness): def _test_output_created(self): """Make sure statepoint files have been created.""" - statepoint = glob.glob('statepoint.*.h5') - assert len(statepoint) == 5, 'Five statepoint files must exist.' + statepoint = glob.glob("statepoint.*.h5") + assert len(statepoint) == 5, "Five statepoint files must exist." def _get_results(self): """Digest info in the statepoint and return as a string.""" @@ -19,13 +19,13 @@ def _get_results(self): # Read the statepoint file. with StatePoint(self._sp_name) as sp: # Add the source information. - xyz = sp.source[0]['r'] - outstr += ' '.join(['{0:12.6E}'.format(x) for x in xyz]) + xyz = sp.source[0]["r"] + outstr += " ".join(["{0:12.6E}".format(x) for x in xyz]) outstr += "\n" return outstr def test_sourcepoint_batch(): - harness = SourcepointTestHarness('statepoint.08.h5') + harness = SourcepointTestHarness("statepoint.08.h5") harness.main() diff --git a/tests/regression_tests/sourcepoint_latest/test.py b/tests/regression_tests/sourcepoint_latest/test.py index d14b566ad86..d91f8557486 100644 --- a/tests/regression_tests/sourcepoint_latest/test.py +++ b/tests/regression_tests/sourcepoint_latest/test.py @@ -8,11 +8,10 @@ class SourcepointTestHarness(TestHarness): def _test_output_created(self): """Make sure statepoint.* and source* have been created.""" TestHarness._test_output_created(self) - source = glob.glob(os.path.join(os.getcwd(), 'source.*.h5')) - assert len(source) == 1, 'Either multiple or no source files ' \ - 'exist.' + source = glob.glob(os.path.join(os.getcwd(), "source.*.h5")) + assert len(source) == 1, "Either multiple or no source files " "exist." def test_sourcepoint_latest(): - harness = TestHarness('statepoint.10.h5') + harness = TestHarness("statepoint.10.h5") harness.main() diff --git a/tests/regression_tests/sourcepoint_restart/test.py b/tests/regression_tests/sourcepoint_restart/test.py index 32f53ec2384..7cce8cd165d 100644 --- a/tests/regression_tests/sourcepoint_restart/test.py +++ b/tests/regression_tests/sourcepoint_restart/test.py @@ -2,5 +2,5 @@ def test_sourcepoint_restart(): - harness = TestHarness('statepoint.10.h5') + harness = TestHarness("statepoint.10.h5") harness.main() diff --git a/tests/regression_tests/statepoint_batch/test.py b/tests/regression_tests/statepoint_batch/test.py index 323b28fc659..ab58b0e3bdc 100644 --- a/tests/regression_tests/statepoint_batch/test.py +++ b/tests/regression_tests/statepoint_batch/test.py @@ -7,7 +7,7 @@ def __init__(self): def _test_output_created(self): """Make sure statepoint files have been created.""" - sps = ('statepoint.03.h5', 'statepoint.06.h5', 'statepoint.09.h5') + sps = ("statepoint.03.h5", "statepoint.06.h5", "statepoint.09.h5") for sp in sps: self._sp_name = sp TestHarness._test_output_created(self) diff --git a/tests/regression_tests/statepoint_restart/test.py b/tests/regression_tests/statepoint_restart/test.py index 82e514da877..c6016b88a74 100644 --- a/tests/regression_tests/statepoint_restart/test.py +++ b/tests/regression_tests/statepoint_restart/test.py @@ -6,6 +6,7 @@ from tests.regression_tests import config from tests import cdtemp + class StatepointRestartTestHarness(TestHarness): def __init__(self, final_sp, restart_sp): super().__init__(final_sp) @@ -46,22 +47,22 @@ def _run_openmc_restart(self): statepoint = statepoint[0] # Run OpenMC - if config['mpi']: - mpi_args = [config['mpiexec'], '-n', config['mpi_np']] - openmc.run(restart_file=statepoint, openmc_exec=config['exe'], - mpi_args=mpi_args) + if config["mpi"]: + mpi_args = [config["mpiexec"], "-n", config["mpi_np"]] + openmc.run( + restart_file=statepoint, openmc_exec=config["exe"], mpi_args=mpi_args + ) else: - openmc.run(openmc_exec=config['exe'], restart_file=statepoint) + openmc.run(openmc_exec=config["exe"], restart_file=statepoint) def test_statepoint_restart(): - harness = StatepointRestartTestHarness('statepoint.10.h5', - 'statepoint.07.h5') + harness = StatepointRestartTestHarness("statepoint.10.h5", "statepoint.07.h5") harness.main() def test_batch_check(request, capsys): - xmls = list(request.path.parent.glob('*.xml')) + xmls = list(request.path.parent.glob("*.xml")) with cdtemp(xmls): model = openmc.Model.from_xml() diff --git a/tests/regression_tests/statepoint_sourcesep/test.py b/tests/regression_tests/statepoint_sourcesep/test.py index 3d63ca8ee53..994883ddd92 100644 --- a/tests/regression_tests/statepoint_sourcesep/test.py +++ b/tests/regression_tests/statepoint_sourcesep/test.py @@ -8,18 +8,17 @@ class SourcepointTestHarness(TestHarness): def _test_output_created(self): """Make sure statepoint.* and source* have been created.""" TestHarness._test_output_created(self) - source = glob.glob('source.*.h5') - assert len(source) == 1, 'Either multiple or no source files ' \ - 'exist.' + source = glob.glob("source.*.h5") + assert len(source) == 1, "Either multiple or no source files " "exist." def _cleanup(self): TestHarness._cleanup(self) - output = glob.glob('source.*.h5') + output = glob.glob("source.*.h5") for f in output: if os.path.exists(f): os.remove(f) def test_statepoint_sourcesep(): - harness = SourcepointTestHarness('statepoint.10.h5') + harness = SourcepointTestHarness("statepoint.10.h5") harness.main() diff --git a/tests/regression_tests/surface_source/test.py b/tests/regression_tests/surface_source/test.py index 251e9e9ad4a..a815ba75a69 100644 --- a/tests/regression_tests/surface_source/test.py +++ b/tests/regression_tests/surface_source/test.py @@ -29,35 +29,37 @@ def model(request): sph_1 = openmc.Sphere(r=1.0) # Surface to bank/write sources. sph_2 = openmc.Sphere(r=2.0) sph_3 = openmc.Sphere(r=2.5) - sph_4 = openmc.Sphere(r=4.0, boundary_type='vacuum') + sph_4 = openmc.Sphere(r=4.0, boundary_type="vacuum") cell_1 = openmc.Cell(region=-sph_1) - cell_2 = openmc.Cell(region=+sph_1&-sph_2) - cell_3 = openmc.Cell(region=+sph_2&-sph_3) # Cell to tally flux. - cell_4 = openmc.Cell(region=+sph_3&-sph_4) + cell_2 = openmc.Cell(region=+sph_1 & -sph_2) + cell_3 = openmc.Cell(region=+sph_2 & -sph_3) # Cell to tally flux. + cell_4 = openmc.Cell(region=+sph_3 & -sph_4) root = openmc.Universe(cells=[cell_1, cell_2, cell_3, cell_4]) openmc_model.geometry = openmc.Geometry(root) # Settings - openmc_model.settings.run_mode = 'fixed source' + openmc_model.settings.run_mode = "fixed source" openmc_model.settings.particles = 1000 openmc_model.settings.batches = 10 openmc_model.settings.seed = 1 - if surf_source_op == 'write': + if surf_source_op == "write": point = openmc.stats.Point((0, 0, 0)) pt_src = openmc.IndependentSource(space=point) openmc_model.settings.source = pt_src - openmc_model.settings.surf_source_write = {'surface_ids': [1], - 'max_particles': 1000} - elif surf_source_op == 'read': - openmc_model.settings.surf_source_read = {'path': 'surface_source_true.h5'} + openmc_model.settings.surf_source_write = { + "surface_ids": [1], + "max_particles": 1000, + } + elif surf_source_op == "read": + openmc_model.settings.surf_source_read = {"path": "surface_source_true.h5"} # Tallies tal = openmc.Tally() cell_filter = openmc.CellFilter(cell_3) tal.filters = [cell_filter] - tal.scores = ['flux'] + tal.scores = ["flux"] openmc_model.tallies.append(tal) return openmc_model @@ -69,23 +71,24 @@ def _test_output_created(self): super()._test_output_created() # Check if 'surface_source.h5' has been created. if self._model.settings.surf_source_write: - assert os.path.exists('surface_source.h5'), \ - 'Surface source file does not exist.' + assert os.path.exists( + "surface_source.h5" + ), "Surface source file does not exist." def _compare_output(self): """Make sure the current surface_source.h5 agree with the reference.""" if self._model.settings.surf_source_write: - with h5py.File("surface_source_true.h5", 'r') as f: - source_true = f['source_bank'][()] + with h5py.File("surface_source_true.h5", "r") as f: + source_true = f["source_bank"][()] # Convert dtye from mixed to a float for comparison assertion - source_true.dtype = 'float64' - with h5py.File("surface_source.h5", 'r') as f: - source_test = f['source_bank'][()] + source_true.dtype = "float64" + with h5py.File("surface_source.h5", "r") as f: + source_test = f["source_bank"][()] # Convert dtye from mixed to a float for comparison assertion - source_test.dtype = 'float64' - np.testing.assert_allclose(np.sort(source_true), - np.sort(source_test), - atol=1e-07) + source_test.dtype = "float64" + np.testing.assert_allclose( + np.sort(source_true), np.sort(source_test), atol=1e-07 + ) def execute_test(self): """Build input XMLs, run OpenMC, check output and results.""" @@ -105,31 +108,31 @@ def execute_test(self): def _overwrite_results(self): """Overwrite the results_true with the results_test.""" - shutil.copyfile('results_test.dat', 'results_true.dat') - if os.path.exists('surface_source.h5'): - shutil.copyfile('surface_source.h5', 'surface_source_true.h5') + shutil.copyfile("results_test.dat", "results_true.dat") + if os.path.exists("surface_source.h5"): + shutil.copyfile("surface_source.h5", "surface_source_true.h5") def _cleanup(self): """Delete statepoints, tally, and test files.""" super()._cleanup() - fs = 'surface_source.h5' + fs = "surface_source.h5" if os.path.exists(fs): os.remove(fs) -@pytest.mark.surf_source_op('write') +@pytest.mark.surf_source_op("write") def test_surface_source_write(model, monkeypatch): # Test result is based on 1 MPI process monkeypatch.setitem(config, "mpi_np", "1") - harness = SurfaceSourceTestHarness('statepoint.10.h5', - model, - 'inputs_true_write.dat') + harness = SurfaceSourceTestHarness( + "statepoint.10.h5", model, "inputs_true_write.dat" + ) harness.main() -@pytest.mark.surf_source_op('read') +@pytest.mark.surf_source_op("read") def test_surface_source_read(model): - harness = SurfaceSourceTestHarness('statepoint.10.h5', - model, - 'inputs_true_read.dat') - harness.main() \ No newline at end of file + harness = SurfaceSourceTestHarness( + "statepoint.10.h5", model, "inputs_true_read.dat" + ) + harness.main() diff --git a/tests/regression_tests/surface_source_write/_visualize.py b/tests/regression_tests/surface_source_write/_visualize.py index 73340cae06f..32f54780fdf 100644 --- a/tests/regression_tests/surface_source_write/_visualize.py +++ b/tests/regression_tests/surface_source_write/_visualize.py @@ -19,9 +19,9 @@ # Reading the surface source file with h5py.File(f"{folder}/surface_source_true.h5", "r") as fp: source_bank = fp["source_bank"][()] - r_xs = source_bank['r']['x'] - r_ys = source_bank['r']['y'] - r_zs = source_bank['r']['z'] + r_xs = source_bank["r"]["x"] + r_ys = source_bank["r"]["y"] + r_zs = source_bank["r"]["z"] print("Size of the source bank: ", len(source_bank)) diff --git a/tests/regression_tests/surface_source_write/test.py b/tests/regression_tests/surface_source_write/test.py index f144eb82a73..059fda6d78c 100644 --- a/tests/regression_tests/surface_source_write/test.py +++ b/tests/regression_tests/surface_source_write/test.py @@ -230,9 +230,12 @@ def model_1(): # Surfaces box1_rpp = openmc.model.RectangularParallelepiped( - -box1_size / 2.0, box1_size / 2.0, - -box1_size / 2.0, box1_size / 2.0, - -box1_size / 2.0, box1_size / 2.0, + -box1_size / 2.0, + box1_size / 2.0, + -box1_size / 2.0, + box1_size / 2.0, + -box1_size / 2.0, + box1_size / 2.0, ) # Cell @@ -247,10 +250,13 @@ def model_1(): # Surfaces box2_rpp = openmc.model.RectangularParallelepiped( - -box2_size / 2.0, box2_size / 2.0, - -box2_size / 2.0, box2_size / 2.0, - -box2_size / 2.0, box2_size / 2.0, - boundary_type="vacuum" + -box2_size / 2.0, + box2_size / 2.0, + -box2_size / 2.0, + box2_size / 2.0, + -box2_size / 2.0, + box2_size / 2.0, + boundary_type="vacuum", ) # Cell @@ -279,7 +285,8 @@ def model_1(): ] distribution = openmc.stats.Box(bounds[:3], bounds[3:]) model.settings.source = openmc.IndependentSource( - space=distribution, constraints={'fissionable': True}) + space=distribution, constraints={"fissionable": True} + ) return model @@ -350,10 +357,13 @@ def model_2(): # Surfaces box1_rpp = openmc.model.RectangularParallelepiped( - -box1_size / 2.0, box1_size / 2.0, - -box1_size / 2.0, box1_size / 2.0, - -box1_size / 2.0, box1_size / 2.0, - boundary_type="vacuum" + -box1_size / 2.0, + box1_size / 2.0, + -box1_size / 2.0, + box1_size / 2.0, + -box1_size / 2.0, + box1_size / 2.0, + boundary_type="vacuum", ) # Cell @@ -382,7 +392,8 @@ def model_2(): ] distribution = openmc.stats.Box(bounds[:3], bounds[3:]) model.settings.source = openmc.IndependentSource( - space=distribution, constraints={'fissionable': True}) + space=distribution, constraints={"fissionable": True} + ) return model @@ -453,10 +464,13 @@ def model_3(): # Surfaces box1_rpp = openmc.model.RectangularParallelepiped( - -box1_size / 2.0, box1_size / 2.0, - -box1_size / 2.0, box1_size / 2.0, - -box1_size / 2.0, box1_size / 2.0, - boundary_type="reflective" + -box1_size / 2.0, + box1_size / 2.0, + -box1_size / 2.0, + box1_size / 2.0, + -box1_size / 2.0, + box1_size / 2.0, + boundary_type="reflective", ) # Cell @@ -485,7 +499,8 @@ def model_3(): ] distribution = openmc.stats.Box(bounds[:3], bounds[3:]) model.settings.source = openmc.IndependentSource( - space=distribution, constraints={'fissionable': True}) + space=distribution, constraints={"fissionable": True} + ) return model @@ -599,7 +614,8 @@ def model_4(): ] distribution = openmc.stats.Box(bounds[:3], bounds[3:]) model.settings.source = openmc.IndependentSource( - space=distribution, constraints={'fissionable': True}) + space=distribution, constraints={"fissionable": True} + ) return model @@ -934,7 +950,8 @@ def model_dagmc_1(): source_box = openmc.stats.Box([-4, -4, -20], [4, 4, 20]) model.settings.source = openmc.IndependentSource( - space=source_box, constraints={'fissionable': True}) + space=source_box, constraints={"fissionable": True} + ) return model @@ -1069,7 +1086,8 @@ def model_dagmc_2(): source_box = openmc.stats.Box([-4, -4, -20], [4, 4, 20]) model.settings.source = openmc.IndependentSource( - space=source_box, constraints={'fissionable': True}) + space=source_box, constraints={"fissionable": True} + ) return model diff --git a/tests/regression_tests/surface_tally/test.py b/tests/regression_tests/surface_tally/test.py index e496ac0f65c..556715e2fef 100644 --- a/tests/regression_tests/surface_tally/test.py +++ b/tests/regression_tests/surface_tally/test.py @@ -9,37 +9,36 @@ class SurfaceTallyTestHarness(PyAPITestHarness): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Instantiate some Materials and register the appropriate Nuclides - uo2 = openmc.Material(name='UO2 fuel at 2.4% wt enrichment') - uo2.set_density('g/cc', 10.0) - uo2.add_nuclide('U238', 1.0) - uo2.add_nuclide('U235', 0.02) - uo2.add_nuclide('O16', 2.0) - - borated_water = openmc.Material(name='Borated water') - borated_water.set_density('g/cm3', 1) - borated_water.add_nuclide('B10', 10e-5) - borated_water.add_nuclide('H1', 2.0) - borated_water.add_nuclide('O16', 1.0) + uo2 = openmc.Material(name="UO2 fuel at 2.4% wt enrichment") + uo2.set_density("g/cc", 10.0) + uo2.add_nuclide("U238", 1.0) + uo2.add_nuclide("U235", 0.02) + uo2.add_nuclide("O16", 2.0) + + borated_water = openmc.Material(name="Borated water") + borated_water.set_density("g/cm3", 1) + borated_water.add_nuclide("B10", 10e-5) + borated_water.add_nuclide("H1", 2.0) + borated_water.add_nuclide("O16", 1.0) # Instantiate a Materials collection and export to XML self._model.materials = openmc.Materials([uo2, borated_water]) # Instantiate ZCylinder surfaces - fuel_or = openmc.ZCylinder(surface_id=1, x0=0, y0=0, r=1, - name='Fuel OR') - left = openmc.XPlane(surface_id=2, x0=-2, name='left') - right = openmc.XPlane(surface_id=3, x0=2, name='right') - bottom = openmc.YPlane(y0=-2, name='bottom') - top = openmc.YPlane(y0=2, name='top') - - left.boundary_type = 'vacuum' - right.boundary_type = 'reflective' - top.boundary_type = 'reflective' - bottom.boundary_type = 'reflective' + fuel_or = openmc.ZCylinder(surface_id=1, x0=0, y0=0, r=1, name="Fuel OR") + left = openmc.XPlane(surface_id=2, x0=-2, name="left") + right = openmc.XPlane(surface_id=3, x0=2, name="right") + bottom = openmc.YPlane(y0=-2, name="bottom") + top = openmc.YPlane(y0=2, name="top") + + left.boundary_type = "vacuum" + right.boundary_type = "reflective" + top.boundary_type = "reflective" + bottom.boundary_type = "reflective" # Instantiate Cells - fuel = openmc.Cell(name='fuel') - water = openmc.Cell(name='water') + fuel = openmc.Cell(name="fuel") + water = openmc.Cell(name="water") # Use surface half-spaces to define regions fuel.region = -fuel_or @@ -50,14 +49,14 @@ def __init__(self, *args, **kwargs): water.fill = borated_water # Instantiate pin cell Universe - pin_cell = openmc.Universe(name='pin cell') + pin_cell = openmc.Universe(name="pin cell") pin_cell.add_cells([fuel, water]) # Instantiate root Cell and Universe - root_cell = openmc.Cell(name='root cell') + root_cell = openmc.Cell(name="root cell") root_cell.region = +left & -right & +bottom & -top root_cell.fill = pin_cell - root_univ = openmc.Universe(universe_id=0, name='root universe') + root_univ = openmc.Universe(universe_id=0, name="root universe") root_univ.add_cell(root_cell) # Instantiate a Geometry, register the root Universe @@ -68,13 +67,17 @@ def __init__(self, *args, **kwargs): settings_file.batches = 10 settings_file.inactive = 0 settings_file.particles = 1000 - #settings_file.output = {'tallies': True} + # settings_file.output = {'tallies': True} # Create an initial uniform spatial source distribution bounds = [-0.62992, -0.62992, -1, 0.62992, 0.62992, 1] - uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:],) + uniform_dist = openmc.stats.Box( + bounds[:3], + bounds[3:], + ) settings_file.source = openmc.IndependentSource( - space=uniform_dist, constraints={'fissionable': True}) + space=uniform_dist, constraints={"fissionable": True} + ) self._model.settings = settings_file # Tallies file @@ -82,7 +85,7 @@ def __init__(self, *args, **kwargs): # Create partial current tallies from fuel to water # Filters - two_groups = [0., 4e6, 20e6] + two_groups = [0.0, 4e6, 20e6] energy_filter = openmc.EnergyFilter(two_groups) polar_filter = openmc.PolarFilter([0, np.pi / 4, np.pi]) azimuthal_filter = openmc.AzimuthalFilter([0, np.pi / 4, np.pi]) @@ -91,17 +94,27 @@ def __init__(self, *args, **kwargs): cell_filter = openmc.CellFilter(water) # Use Cell to cell filters for partial current - cell_to_cell_tally = openmc.Tally(name=str('fuel_to_water_1')) - cell_to_cell_tally.filters = [cell_from_filter, cell_filter, \ - energy_filter, polar_filter, azimuthal_filter] - cell_to_cell_tally.scores = ['current'] + cell_to_cell_tally = openmc.Tally(name=str("fuel_to_water_1")) + cell_to_cell_tally.filters = [ + cell_from_filter, + cell_filter, + energy_filter, + polar_filter, + azimuthal_filter, + ] + cell_to_cell_tally.scores = ["current"] tallies_file.append(cell_to_cell_tally) # Use a Cell from + surface filters for partial current - cell_to_cell_tally = openmc.Tally(name=str('fuel_to_water_2')) - cell_to_cell_tally.filters = [cell_from_filter, surface_filter, \ - energy_filter, polar_filter, azimuthal_filter] - cell_to_cell_tally.scores = ['current'] + cell_to_cell_tally = openmc.Tally(name=str("fuel_to_water_2")) + cell_to_cell_tally.filters = [ + cell_from_filter, + surface_filter, + energy_filter, + polar_filter, + azimuthal_filter, + ] + cell_to_cell_tally.scores = ["current"] tallies_file.append(cell_to_cell_tally) # Create partial current tallies from water to fuel @@ -110,49 +123,67 @@ def __init__(self, *args, **kwargs): cell_filter = openmc.CellFilter(fuel) # Cell to cell filters for partial current - cell_to_cell_tally = openmc.Tally(name=str('water_to_fuel_1')) - cell_to_cell_tally.filters = [mat_from_filter, cell_filter, \ - energy_filter, polar_filter, azimuthal_filter] - cell_to_cell_tally.scores = ['current'] + cell_to_cell_tally = openmc.Tally(name=str("water_to_fuel_1")) + cell_to_cell_tally.filters = [ + mat_from_filter, + cell_filter, + energy_filter, + polar_filter, + azimuthal_filter, + ] + cell_to_cell_tally.scores = ["current"] tallies_file.append(cell_to_cell_tally) # Cell from + surface filters for partial current - cell_to_cell_tally = openmc.Tally(name=str('water_to_fuel_2')) - cell_to_cell_tally.filters = [mat_from_filter, surface_filter, \ - energy_filter, polar_filter, azimuthal_filter] - cell_to_cell_tally.scores = ['current'] + cell_to_cell_tally = openmc.Tally(name=str("water_to_fuel_2")) + cell_to_cell_tally.filters = [ + mat_from_filter, + surface_filter, + energy_filter, + polar_filter, + azimuthal_filter, + ] + cell_to_cell_tally.scores = ["current"] tallies_file.append(cell_to_cell_tally) # Create a net current tally on inner surface using a surface filter surface_filter = openmc.SurfaceFilter([1]) - surf_tally1 = openmc.Tally(name='net_cylinder') - surf_tally1.filters = [surface_filter, energy_filter, polar_filter, \ - azimuthal_filter] - surf_tally1.scores = ['current'] + surf_tally1 = openmc.Tally(name="net_cylinder") + surf_tally1.filters = [ + surface_filter, + energy_filter, + polar_filter, + azimuthal_filter, + ] + surf_tally1.scores = ["current"] tallies_file.append(surf_tally1) # Create a net current tally on left surface using a surface filter # This surface has a vacuum boundary condition, so leakage is tallied surface_filter = openmc.SurfaceFilter([2]) - surf_tally2 = openmc.Tally(name='leakage_left') - surf_tally2.filters = [surface_filter, energy_filter, polar_filter, \ - azimuthal_filter] - surf_tally2.scores = ['current'] + surf_tally2 = openmc.Tally(name="leakage_left") + surf_tally2.filters = [ + surface_filter, + energy_filter, + polar_filter, + azimuthal_filter, + ] + surf_tally2.scores = ["current"] tallies_file.append(surf_tally2) # Create a net current tally on right surface using a surface filter # This surface has a reflective boundary condition, so the net current # should be zero. surface_filter = openmc.SurfaceFilter([3]) - surf_tally3 = openmc.Tally(name='net_right') + surf_tally3 = openmc.Tally(name="net_right") surf_tally3.filters = [surface_filter, energy_filter] - surf_tally3.scores = ['current'] + surf_tally3.scores = ["current"] tallies_file.append(surf_tally3) surface_filter = openmc.SurfaceFilter([3]) - surf_tally3 = openmc.Tally(name='net_right') + surf_tally3 = openmc.Tally(name="net_right") surf_tally3.filters = [surface_filter, energy_filter] - surf_tally3.scores = ['current'] + surf_tally3.scores = ["current"] tallies_file.append(surf_tally3) self._model.tallies = tallies_file @@ -167,11 +198,11 @@ def _get_results(self): df = pd.concat(tally_dfs, ignore_index=True) # Extract the relevant data as a CSV string. - cols = ('mean', 'std. dev.') - return df.to_csv(None, columns=cols, index=False, float_format='%.7e') + cols = ("mean", "std. dev.") + return df.to_csv(None, columns=cols, index=False, float_format="%.7e") return outstr def test_surface_tally(): - harness = SurfaceTallyTestHarness('statepoint.10.h5', model=openmc.Model()) + harness = SurfaceTallyTestHarness("statepoint.10.h5", model=openmc.Model()) harness.main() diff --git a/tests/regression_tests/survival_biasing/test.py b/tests/regression_tests/survival_biasing/test.py index 39e6faed8ef..14905aff085 100644 --- a/tests/regression_tests/survival_biasing/test.py +++ b/tests/regression_tests/survival_biasing/test.py @@ -2,5 +2,5 @@ def test_survival_biasing(): - harness = TestHarness('statepoint.10.h5') + harness = TestHarness("statepoint.10.h5") harness.main() diff --git a/tests/regression_tests/tallies/test.py b/tests/regression_tests/tallies/test.py index d20067ed33f..6ff618dba42 100644 --- a/tests/regression_tests/tallies/test.py +++ b/tests/regression_tests/tallies/test.py @@ -6,27 +6,28 @@ def test_tallies(): - harness = HashedPyAPITestHarness('statepoint.5.h5') + harness = HashedPyAPITestHarness("statepoint.5.h5") model = harness._model # Set settings explicitly model.settings.batches = 5 model.settings.inactive = 0 model.settings.particles = 400 - model.settings.source = openmc.IndependentSource(space=openmc.stats.Box( - [-160, -160, -183], [160, 160, 183])) + model.settings.source = openmc.IndependentSource( + space=openmc.stats.Box([-160, -160, -183], [160, 160, 183]) + ) azimuthal_bins = (-3.14159, -1.8850, -0.6283, 0.6283, 1.8850, 3.14159) azimuthal_filter = AzimuthalFilter(azimuthal_bins) azimuthal_tally1 = Tally() azimuthal_tally1.filters = [azimuthal_filter] - azimuthal_tally1.scores = ['flux'] - azimuthal_tally1.estimator = 'tracklength' + azimuthal_tally1.scores = ["flux"] + azimuthal_tally1.estimator = "tracklength" azimuthal_tally2 = Tally() azimuthal_tally2.filters = [azimuthal_filter] - azimuthal_tally2.scores = ['flux'] - azimuthal_tally2.estimator = 'analog' + azimuthal_tally2.scores = ["flux"] + azimuthal_tally2.estimator = "analog" mesh_2x2 = RegularMesh(mesh_id=1) mesh_2x2.lower_left = [-182.07, -182.07] @@ -35,142 +36,207 @@ def test_tallies(): mesh_filter = MeshFilter(mesh_2x2) azimuthal_tally3 = Tally() azimuthal_tally3.filters = [azimuthal_filter, mesh_filter] - azimuthal_tally3.scores = ['flux'] - azimuthal_tally3.estimator = 'tracklength' + azimuthal_tally3.scores = ["flux"] + azimuthal_tally3.estimator = "tracklength" cellborn_tally = Tally() cellborn_tally.filters = [ - CellBornFilter((model.geometry.get_all_cells()[10], - model.geometry.get_all_cells()[21], - 22, 23))] # Test both Cell objects and ids - cellborn_tally.scores = ['total'] + CellBornFilter( + ( + model.geometry.get_all_cells()[10], + model.geometry.get_all_cells()[21], + 22, + 23, + ) + ) + ] # Test both Cell objects and ids + cellborn_tally.scores = ["total"] dg_tally = Tally() dg_tally.filters = [DelayedGroupFilter((1, 2, 3, 4, 5, 6))] - dg_tally.scores = ['delayed-nu-fission', 'decay-rate'] - dg_tally.nuclides = ['U235', 'O16', 'total'] + dg_tally.scores = ["delayed-nu-fission", "decay-rate"] + dg_tally.nuclides = ["U235", "O16", "total"] four_groups = (0.0, 0.253, 1.0e3, 1.0e6, 20.0e6) energy_filter = EnergyFilter(four_groups) energy_tally = Tally() energy_tally.filters = [energy_filter] - energy_tally.scores = ['total'] + energy_tally.scores = ["total"] energyout_filter = EnergyoutFilter(four_groups) energyout_tally = Tally() energyout_tally.filters = [energyout_filter] - energyout_tally.scores = ['scatter'] + energyout_tally.scores = ["scatter"] transfer_tally = Tally() transfer_tally.filters = [energy_filter, energyout_filter] - transfer_tally.scores = ['scatter', 'nu-fission'] + transfer_tally.scores = ["scatter", "nu-fission"] material_tally = Tally() material_tally.filters = [ - MaterialFilter((model.geometry.get_materials_by_name('UOX fuel')[0], - model.geometry.get_materials_by_name('Zircaloy')[0], - 3, 4))] # Test both Material objects and ids - material_tally.scores = ['total'] + MaterialFilter( + ( + model.geometry.get_materials_by_name("UOX fuel")[0], + model.geometry.get_materials_by_name("Zircaloy")[0], + 3, + 4, + ) + ) + ] # Test both Material objects and ids + material_tally.scores = ["total"] mu_bins = (-1.0, -0.5, 0.0, 0.5, 1.0) mu_filter = MuFilter(mu_bins) mu_tally1 = Tally() mu_tally1.filters = [mu_filter] - mu_tally1.scores = ['scatter', 'nu-scatter'] + mu_tally1.scores = ["scatter", "nu-scatter"] mu_tally2 = Tally() mu_tally2.filters = [mu_filter, mesh_filter] - mu_tally2.scores = ['scatter', 'nu-scatter'] + mu_tally2.scores = ["scatter", "nu-scatter"] polar_bins = (0.0, 0.6283, 1.2566, 1.8850, 2.5132, 3.14159) polar_filter = PolarFilter(polar_bins) polar_tally1 = Tally() polar_tally1.filters = [polar_filter] - polar_tally1.scores = ['flux'] - polar_tally1.estimator = 'tracklength' + polar_tally1.scores = ["flux"] + polar_tally1.estimator = "tracklength" polar_tally2 = Tally() polar_tally2.filters = [polar_filter] - polar_tally2.scores = ['flux'] - polar_tally2.estimator = 'analog' + polar_tally2.scores = ["flux"] + polar_tally2.estimator = "analog" polar_tally3 = Tally() polar_tally3.filters = [polar_filter, mesh_filter] - polar_tally3.scores = ['flux'] - polar_tally3.estimator = 'tracklength' + polar_tally3.scores = ["flux"] + polar_tally3.estimator = "tracklength" legendre_filter = LegendreFilter(order=4) legendre_tally = Tally() legendre_tally.filters = [legendre_filter] - legendre_tally.scores = ['scatter', 'nu-scatter'] - legendre_tally.estimator = 'analog' + legendre_tally.scores = ["scatter", "nu-scatter"] + legendre_tally.estimator = "analog" harmonics_filter = SphericalHarmonicsFilter(order=4) harmonics_tally = Tally() harmonics_tally.filters = [harmonics_filter] - harmonics_tally.scores = ['scatter', 'nu-scatter', 'flux', 'total'] - harmonics_tally.estimator = 'analog' + harmonics_tally.scores = ["scatter", "nu-scatter", "flux", "total"] + harmonics_tally.estimator = "analog" harmonics_tally2 = Tally() harmonics_tally2.filters = [harmonics_filter] - harmonics_tally2.scores = ['flux', 'total'] - harmonics_tally2.estimator = 'collision' + harmonics_tally2.scores = ["flux", "total"] + harmonics_tally2.estimator = "collision" harmonics_tally3 = Tally() harmonics_tally3.filters = [harmonics_filter] - harmonics_tally3.scores = ['flux', 'total'] - harmonics_tally3.estimator = 'tracklength' + harmonics_tally3.scores = ["flux", "total"] + harmonics_tally3.estimator = "tracklength" universe_tally = Tally() universe_tally.filters = [ - UniverseFilter((model.geometry.get_all_universes()[1], - model.geometry.get_all_universes()[2], - 3, 4, 6, 8))] # Test both Universe objects and ids - universe_tally.scores = ['total'] - - cell_filter = CellFilter((model.geometry.get_all_cells()[10], - model.geometry.get_all_cells()[21], - 22, 23, 60)) # Test both Cell objects and ids + UniverseFilter( + ( + model.geometry.get_all_universes()[1], + model.geometry.get_all_universes()[2], + 3, + 4, + 6, + 8, + ) + ) + ] # Test both Universe objects and ids + universe_tally.scores = ["total"] + + cell_filter = CellFilter( + ( + model.geometry.get_all_cells()[10], + model.geometry.get_all_cells()[21], + 22, + 23, + 60, + ) + ) # Test both Cell objects and ids score_tallies = [Tally() for i in range(6)] for t in score_tallies: t.filters = [cell_filter] - t.scores = ['absorption', 'delayed-nu-fission', 'events', 'fission', - 'inverse-velocity', 'kappa-fission', '(n,2n)', '(n,n1)', - '(n,gamma)', 'nu-fission', 'scatter', 'elastic', - 'total', 'prompt-nu-fission', 'fission-q-prompt', - 'fission-q-recoverable', 'decay-rate'] - for t in score_tallies[0:2]: t.estimator = 'tracklength' - for t in score_tallies[2:4]: t.estimator = 'analog' - for t in score_tallies[4:6]: t.estimator = 'collision' + t.scores = [ + "absorption", + "delayed-nu-fission", + "events", + "fission", + "inverse-velocity", + "kappa-fission", + "(n,2n)", + "(n,n1)", + "(n,gamma)", + "nu-fission", + "scatter", + "elastic", + "total", + "prompt-nu-fission", + "fission-q-prompt", + "fission-q-recoverable", + "decay-rate", + ] + for t in score_tallies[0:2]: + t.estimator = "tracklength" + for t in score_tallies[2:4]: + t.estimator = "analog" + for t in score_tallies[4:6]: + t.estimator = "collision" for t in score_tallies[1::2]: - t.nuclides = ['U235', 'O16', 'total'] + t.nuclides = ["U235", "O16", "total"] cell_filter2 = CellFilter((21, 22, 23, 27, 28, 29, 60)) flux_tallies = [Tally() for i in range(3)] for t in flux_tallies: t.filters = [cell_filter2] - t.scores = ['flux'] - flux_tallies[0].estimator = 'tracklength' - flux_tallies[1].estimator = 'analog' - flux_tallies[2].estimator = 'collision' + t.scores = ["flux"] + flux_tallies[0].estimator = "tracklength" + flux_tallies[1].estimator = "analog" + flux_tallies[2].estimator = "collision" fusion_tally = Tally() - fusion_tally.scores = ['H1-production', 'H2-production', 'H3-production', - 'He3-production', 'He4-production', 'heating', 'damage-energy'] + fusion_tally.scores = [ + "H1-production", + "H2-production", + "H3-production", + "He3-production", + "He4-production", + "heating", + "damage-energy", + ] n_collision = (1, 2, 5, 3, 6) collision_filter = CollisionFilter(n_collision) collision_tally = Tally() collision_tally.filters = [collision_filter] - collision_tally.scores = ['scatter'] + collision_tally.scores = ["scatter"] model.tallies += [ - azimuthal_tally1, azimuthal_tally2, azimuthal_tally3, - cellborn_tally, dg_tally, energy_tally, energyout_tally, - transfer_tally, material_tally, mu_tally1, mu_tally2, - polar_tally1, polar_tally2, polar_tally3, legendre_tally, - harmonics_tally, harmonics_tally2, harmonics_tally3, - universe_tally, collision_tally] + azimuthal_tally1, + azimuthal_tally2, + azimuthal_tally3, + cellborn_tally, + dg_tally, + energy_tally, + energyout_tally, + transfer_tally, + material_tally, + mu_tally1, + mu_tally2, + polar_tally1, + polar_tally2, + polar_tally3, + legendre_tally, + harmonics_tally, + harmonics_tally2, + harmonics_tally3, + universe_tally, + collision_tally, + ] model.tallies += score_tallies model.tallies += flux_tallies model.tallies.append(fusion_tally) diff --git a/tests/regression_tests/tally_aggregation/test.py b/tests/regression_tests/tally_aggregation/test.py index 08d91166064..8550d102c4f 100644 --- a/tests/regression_tests/tally_aggregation/test.py +++ b/tests/regression_tests/tally_aggregation/test.py @@ -9,17 +9,17 @@ def model(): model = openmc.model.Model() - fuel = openmc.Material(name='UO2') - fuel.set_density('g/cm3', 10.29769) + fuel = openmc.Material(name="UO2") + fuel.set_density("g/cm3", 10.29769) fuel.add_nuclide("U234", 4.4843e-6) fuel.add_nuclide("U235", 5.5815e-4) fuel.add_nuclide("U238", 2.2408e-2) fuel.add_nuclide("O16", 4.5829e-2) - water = openmc.Material(name='light water') - water.add_nuclide('H1', 2.0) - water.add_nuclide('O16', 1.0) - water.set_density('g/cm3', 1.0) - water.add_s_alpha_beta('c_H_in_H2O') + water = openmc.Material(name="light water") + water.add_nuclide("H1", 2.0) + water.add_nuclide("O16", 1.0) + water.set_density("g/cm3", 1.0) + water.add_s_alpha_beta("c_H_in_H2O") model.materials.extend([fuel, water]) cyl = openmc.ZCylinder(r=0.4) @@ -33,7 +33,7 @@ def model(): [pin, pin], [pin, pin], ] - box = openmc.model.RectangularPrism(2*d, 2*d, boundary_type='reflective') + box = openmc.model.RectangularPrism(2 * d, 2 * d, boundary_type="reflective") main_cell = openmc.Cell(fill=lattice, region=-box) model.geometry = openmc.Geometry([main_cell]) @@ -43,16 +43,15 @@ def model(): energy_filter = openmc.EnergyFilter([0.0, 0.253, 1.0e3, 1.0e6, 20.0e6]) distrib_filter = openmc.DistribcellFilter(pin.cells[1]) - tally = openmc.Tally(name='distribcell tally') + tally = openmc.Tally(name="distribcell tally") tally.filters = [energy_filter, distrib_filter] - tally.scores = ['nu-fission', 'total'] - tally.nuclides = ['U234', 'U235', 'U238'] + tally.scores = ["nu-fission", "total"] + tally.nuclides = ["U234", "U235", "U238"] model.tallies.append(tally) return model - class TallyAggregationTestHarness(PyAPITestHarness): def _get_results(self, hash_output=False): """Digest info in the statepoint and return as a string.""" @@ -61,37 +60,37 @@ def _get_results(self, hash_output=False): sp = openmc.StatePoint(self._sp_name) # Extract the tally of interest - tally = sp.get_tally(name='distribcell tally') + tally = sp.get_tally(name="distribcell tally") # Perform tally aggregations across filter bins, nuclides and scores - outstr = '' + outstr = "" # This test occasionally fails in CI due to differences in the 8th # significant digit. Lowering precision to 7 digits prevents failures with np.printoptions(precision=7): # Sum across all energy filter bins tally_sum = tally.summation(filter_type=openmc.EnergyFilter) - outstr += ', '.join(map(str, tally_sum.mean)) - outstr += ', '.join(map(str, tally_sum.std_dev)) + outstr += ", ".join(map(str, tally_sum.mean)) + outstr += ", ".join(map(str, tally_sum.std_dev)) # Sum across all distribcell filter bins tally_sum = tally.summation(filter_type=openmc.DistribcellFilter) - outstr += ', '.join(map(str, tally_sum.mean)) - outstr += ', '.join(map(str, tally_sum.std_dev)) + outstr += ", ".join(map(str, tally_sum.mean)) + outstr += ", ".join(map(str, tally_sum.std_dev)) # Sum across all nuclides - tally_sum = tally.summation(nuclides=['U234', 'U235', 'U238']) - outstr += ', '.join(map(str, tally_sum.mean)) - outstr += ', '.join(map(str, tally_sum.std_dev)) + tally_sum = tally.summation(nuclides=["U234", "U235", "U238"]) + outstr += ", ".join(map(str, tally_sum.mean)) + outstr += ", ".join(map(str, tally_sum.std_dev)) # Sum across all scores - tally_sum = tally.summation(scores=['nu-fission', 'total']) - outstr += ', '.join(map(str, tally_sum.mean)) - outstr += ', '.join(map(str, tally_sum.std_dev)) + tally_sum = tally.summation(scores=["nu-fission", "total"]) + outstr += ", ".join(map(str, tally_sum.mean)) + outstr += ", ".join(map(str, tally_sum.std_dev)) return outstr def test_tally_aggregation(model): - harness = TallyAggregationTestHarness('statepoint.10.h5', model) + harness = TallyAggregationTestHarness("statepoint.10.h5", model) harness.main() diff --git a/tests/regression_tests/tally_arithmetic/test.py b/tests/regression_tests/tally_arithmetic/test.py index 532160a17ee..89165925997 100644 --- a/tests/regression_tests/tally_arithmetic/test.py +++ b/tests/regression_tests/tally_arithmetic/test.py @@ -12,19 +12,19 @@ def model(): model = openmc.model.Model() fuel = openmc.Material() - fuel.set_density('g/cm3', 10.0) - fuel.add_nuclide('U234', 1.0) - fuel.add_nuclide('U235', 4.0) - fuel.add_nuclide('U238', 95.0) - water = openmc.Material(name='light water') - water.add_nuclide('H1', 2.0) - water.add_nuclide('O16', 1.0) - water.set_density('g/cm3', 1.0) - water.add_s_alpha_beta('c_H_in_H2O') + fuel.set_density("g/cm3", 10.0) + fuel.add_nuclide("U234", 1.0) + fuel.add_nuclide("U235", 4.0) + fuel.add_nuclide("U238", 95.0) + water = openmc.Material(name="light water") + water.add_nuclide("H1", 2.0) + water.add_nuclide("O16", 1.0) + water.set_density("g/cm3", 1.0) + water.add_s_alpha_beta("c_H_in_H2O") model.materials.extend([fuel, water]) cyl1 = openmc.ZCylinder(r=5.0) - cyl2 = openmc.ZCylinder(r=10.0, boundary_type='vacuum') + cyl2 = openmc.ZCylinder(r=10.0, boundary_type="vacuum") cell1 = openmc.Cell(fill=fuel, region=-cyl1) cell2 = openmc.Cell(fill=water, region=+cyl1 & -cyl2) model.geometry = openmc.Geometry([cell1, cell2]) @@ -41,15 +41,15 @@ def model(): material_filter = openmc.MaterialFilter((fuel, water)) mesh_filter = openmc.MeshFilter(mesh) - tally = openmc.Tally(name='tally 1') + tally = openmc.Tally(name="tally 1") tally.filters = [material_filter, energy_filter] - tally.scores = ['nu-fission', 'total'] - tally.nuclides = ['U234', 'U235'] + tally.scores = ["nu-fission", "total"] + tally.nuclides = ["U234", "U235"] model.tallies.append(tally) - tally = openmc.Tally(name='tally 2') + tally = openmc.Tally(name="tally 2") tally.filters = [energy_filter, mesh_filter] - tally.scores = ['total', 'fission'] - tally.nuclides = ['U238', 'U235'] + tally.scores = ["total", "fission"] + tally.nuclides = ["U238", "U235"] model.tallies.append(tally) return model @@ -63,8 +63,8 @@ def _get_results(self, hash_output=False): sp = openmc.StatePoint(self._sp_name) # Load the tallies - tally_1 = sp.get_tally(name='tally 1') - tally_2 = sp.get_tally(name='tally 2') + tally_1 = sp.get_tally(name="tally 1") + tally_2 = sp.get_tally(name="tally 2") # Perform all the tally arithmetic operations and output results output = [] @@ -73,31 +73,35 @@ def _get_results(self, hash_output=False): output.append(str(mean[np.nonzero(mean)])) mean = tally_1.hybrid_product( - tally_2, '*', 'entrywise', 'tensor', 'tensor').mean + tally_2, "*", "entrywise", "tensor", "tensor" + ).mean output.append(str(mean[np.nonzero(mean)])) mean = tally_1.hybrid_product( - tally_2, '*', 'entrywise', 'entrywise', 'tensor').mean + tally_2, "*", "entrywise", "entrywise", "tensor" + ).mean output.append(str(mean[np.nonzero(mean)])) mean = tally_1.hybrid_product( - tally_2, '*', 'entrywise', 'tensor', 'entrywise').mean + tally_2, "*", "entrywise", "tensor", "entrywise" + ).mean output.append(str(mean[np.nonzero(mean)])) mean = tally_1.hybrid_product( - tally_2, '*', 'entrywise', 'entrywise', 'entrywise').mean + tally_2, "*", "entrywise", "entrywise", "entrywise" + ).mean output.append(str(mean[np.nonzero(mean)])) # Hash the results if necessary - outstr = ''.join(output) + outstr = "".join(output) if hash_output: sha512 = hashlib.sha512() - sha512.update(outstr.encode('utf-8')) + sha512.update(outstr.encode("utf-8")) outstr = sha512.hexdigest() return outstr def test_tally_arithmetic(model): - harness = TallyArithmeticTestHarness('statepoint.5.h5', model) + harness = TallyArithmeticTestHarness("statepoint.5.h5", model) harness.main() diff --git a/tests/regression_tests/tally_assumesep/test.py b/tests/regression_tests/tally_assumesep/test.py index 64595ced224..caab43d94c8 100644 --- a/tests/regression_tests/tally_assumesep/test.py +++ b/tests/regression_tests/tally_assumesep/test.py @@ -2,5 +2,5 @@ def test_tally_assumesep(): - harness = TestHarness('statepoint.10.h5') + harness = TestHarness("statepoint.10.h5") harness.main() diff --git a/tests/regression_tests/tally_nuclides/test.py b/tests/regression_tests/tally_nuclides/test.py index ee1f4b600e2..e1d9a906868 100644 --- a/tests/regression_tests/tally_nuclides/test.py +++ b/tests/regression_tests/tally_nuclides/test.py @@ -2,5 +2,5 @@ def test_tally_nuclides(): - harness = TestHarness('statepoint.10.h5') + harness = TestHarness("statepoint.10.h5") harness.main() diff --git a/tests/regression_tests/tally_slice_merge/test.py b/tests/regression_tests/tally_slice_merge/test.py index 090e3144838..fa99cfe7c5c 100644 --- a/tests/regression_tests/tally_slice_merge/test.py +++ b/tests/regression_tests/tally_slice_merge/test.py @@ -11,23 +11,23 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Define nuclides and scores to add to both tallies - self.nuclides = ['U235', 'U238'] - self.scores = ['fission', 'nu-fission'] + self.nuclides = ["U235", "U238"] + self.scores = ["fission", "nu-fission"] # Define filters for energy and spatial domain - low_energy = openmc.EnergyFilter([0., 0.625]) - high_energy = openmc.EnergyFilter([0.625, 20.e6]) + low_energy = openmc.EnergyFilter([0.0, 0.625]) + high_energy = openmc.EnergyFilter([0.625, 20.0e6]) merged_energies = low_energy.merge(high_energy) cell_21 = openmc.CellFilter(21) cell_27 = openmc.CellFilter(27) distribcell_filter = openmc.DistribcellFilter(21) - mesh = openmc.RegularMesh(name='mesh') + mesh = openmc.RegularMesh(name="mesh") mesh.dimension = [2, 2] - mesh.lower_left = [-50., -50.] - mesh.upper_right = [+50., +50.] + mesh.lower_left = [-50.0, -50.0] + mesh.upper_right = [+50.0, +50.0] mesh_filter = openmc.MeshFilter(mesh) self.cell_filters = [cell_21, cell_27] @@ -40,7 +40,7 @@ def __init__(self, *args, **kwargs): for nuclide in self.nuclides: for score in self.scores: tally = openmc.Tally() - tally.estimator = 'tracklength' + tally.estimator = "tracklength" tally.scores.append(score) tally.nuclides.append(nuclide) tally.filters.append(cell_filter) @@ -54,19 +54,19 @@ def __init__(self, *args, **kwargs): tallies = list(map(lambda xy: xy[0].merge(xy[1]), zip_split)) # Specify a name for the tally - tallies[0].name = 'cell tally' + tallies[0].name = "cell tally" # Initialize a distribcell tally - distribcell_tally = openmc.Tally(name='distribcell tally') - distribcell_tally.estimator = 'tracklength' + distribcell_tally = openmc.Tally(name="distribcell tally") + distribcell_tally.estimator = "tracklength" distribcell_tally.filters = [distribcell_filter, merged_energies] for score in self.scores: distribcell_tally.scores.append(score) for nuclide in self.nuclides: distribcell_tally.nuclides.append(nuclide) - mesh_tally = openmc.Tally(name='mesh tally') - mesh_tally.estimator = 'tracklength' + mesh_tally = openmc.Tally(name="mesh tally") + mesh_tally.estimator = "tracklength" mesh_tally.filters = [mesh_filter, merged_energies] mesh_tally.scores = self.scores mesh_tally.nuclides = self.nuclides @@ -81,19 +81,25 @@ def _get_results(self, hash_output=False): sp = openmc.StatePoint(self._sp_name) # Extract the cell tally - tallies = [sp.get_tally(name='cell tally')] + tallies = [sp.get_tally(name="cell tally")] # Slice the tallies by cell filter bins cell_filter_prod = itertools.product(tallies, self.cell_filters) - tallies = map(lambda tf: tf[0].get_slice(filters=[type(tf[1])], - filter_bins=[(tf[1].bins[0],)]), - cell_filter_prod) + tallies = map( + lambda tf: tf[0].get_slice( + filters=[type(tf[1])], filter_bins=[(tf[1].bins[0],)] + ), + cell_filter_prod, + ) # Slice the tallies by energy filter bins energy_filter_prod = itertools.product(tallies, self.energy_filters) - tallies = map(lambda tf: tf[0].get_slice(filters=[type(tf[1])], - filter_bins=[(tf[1].bins[0],)]), - energy_filter_prod) + tallies = map( + lambda tf: tf[0].get_slice( + filters=[type(tf[1])], filter_bins=[(tf[1].bins[0],)] + ), + energy_filter_prod, + ) # Slice the tallies by nuclide nuclide_prod = itertools.product(tallies, self.nuclides) @@ -105,7 +111,7 @@ def _get_results(self, hash_output=False): tallies = list(tallies) # Initialize an output string - outstr = '' + outstr = "" # Append sliced Tally Pandas DataFrames to output string for tally in tallies: @@ -120,51 +126,55 @@ def _get_results(self, hash_output=False): # Append merged Tally Pandas DataFrame to output string df = tallies[0].get_pandas_dataframe() - outstr += df.to_string() + '\n' + outstr += df.to_string() + "\n" # Extract the distribcell tally - distribcell_tally = sp.get_tally(name='distribcell tally') + distribcell_tally = sp.get_tally(name="distribcell tally") # Sum up a few subdomains from the distribcell tally - sum1 = distribcell_tally.summation(filter_type=openmc.DistribcellFilter, - filter_bins=[0, 100, 2000, 30000]) + sum1 = distribcell_tally.summation( + filter_type=openmc.DistribcellFilter, filter_bins=[0, 100, 2000, 30000] + ) # Sum up a few subdomains from the distribcell tally - sum2 = distribcell_tally.summation(filter_type=openmc.DistribcellFilter, - filter_bins=[500, 5000, 50000]) + sum2 = distribcell_tally.summation( + filter_type=openmc.DistribcellFilter, filter_bins=[500, 5000, 50000] + ) # Merge the distribcell tally slices merge_tally = sum1.merge(sum2) # Append merged Tally Pandas DataFrame to output string df = merge_tally.get_pandas_dataframe() - outstr += df.to_string() + '\n' + outstr += df.to_string() + "\n" # Extract the mesh tally - mesh_tally = sp.get_tally(name='mesh tally') + mesh_tally = sp.get_tally(name="mesh tally") # Sum up a few subdomains from the mesh tally - sum1 = mesh_tally.summation(filter_type=openmc.MeshFilter, - filter_bins=[(1, 1), (1, 2)]) + sum1 = mesh_tally.summation( + filter_type=openmc.MeshFilter, filter_bins=[(1, 1), (1, 2)] + ) # Sum up a few subdomains from the mesh tally - sum2 = mesh_tally.summation(filter_type=openmc.MeshFilter, - filter_bins=[(2, 1), (2, 2)]) + sum2 = mesh_tally.summation( + filter_type=openmc.MeshFilter, filter_bins=[(2, 1), (2, 2)] + ) # Merge the mesh tally slices merge_tally = sum1.merge(sum2) # Append merged Tally Pandas DataFrame to output string df = merge_tally.get_pandas_dataframe() - outstr += df.to_string() + '\n' + outstr += df.to_string() + "\n" # Hash the results if necessary if hash_output: sha512 = hashlib.sha512() - sha512.update(outstr.encode('utf-8')) + sha512.update(outstr.encode("utf-8")) outstr = sha512.hexdigest() return outstr def test_tally_slice_merge(): - harness = TallySliceMergeTestHarness('statepoint.10.h5') + harness = TallySliceMergeTestHarness("statepoint.10.h5") harness.main() diff --git a/tests/regression_tests/time_cutoff/test.py b/tests/regression_tests/time_cutoff/test.py index 9554a6e2a00..a3c00f83f86 100755 --- a/tests/regression_tests/time_cutoff/test.py +++ b/tests/regression_tests/time_cutoff/test.py @@ -10,26 +10,27 @@ def time_model(): time_cutoff = 1e-7 # A single sphere - s1 = openmc.Sphere(r=200, boundary_type='vacuum') + s1 = openmc.Sphere(r=200, boundary_type="vacuum") sphere = openmc.Cell() sphere.region = -s1 model.geometry = openmc.Geometry([sphere]) # Set the running parameters settings_file = openmc.Settings() - settings_file.run_mode = 'fixed source' + settings_file.run_mode = "fixed source" settings_file.batches = 10 settings_file.particles = 100 - settings_file.cutoff = {'time_neutron': time_cutoff} + settings_file.cutoff = {"time_neutron": time_cutoff} settings_file.source = openmc.IndependentSource( - space=openmc.stats.Point(), energy=openmc.stats.Discrete([1e4], [1])) + space=openmc.stats.Point(), energy=openmc.stats.Discrete([1e4], [1]) + ) model.settings = settings_file # Tally flux under time cutoff tallies = openmc.Tallies() tally = openmc.Tally() - tally.scores = ['flux'] - time_filter = openmc.TimeFilter([0, time_cutoff, 2*time_cutoff]) + tally.scores = ["flux"] + time_filter = openmc.TimeFilter([0, time_cutoff, 2 * time_cutoff]) tally.filters = [time_filter] tallies.append(tally) model.tallies = tallies @@ -38,5 +39,5 @@ def time_model(): def test_time_cutoff(time_model): - harness = PyAPITestHarness('statepoint.10.h5', time_model) + harness = PyAPITestHarness("statepoint.10.h5", time_model) harness.main() diff --git a/tests/regression_tests/torus/large_major/test.py b/tests/regression_tests/torus/large_major/test.py index 1ce1ab2cac8..97e6446482c 100644 --- a/tests/regression_tests/torus/large_major/test.py +++ b/tests/regression_tests/torus/large_major/test.py @@ -8,34 +8,42 @@ def model(): model = openmc.Model() tungsten = openmc.Material() - tungsten.set_density('g/cm3', 1.0) - tungsten.add_nuclide('W184', 1.0) + tungsten.set_density("g/cm3", 1.0) + tungsten.add_nuclide("W184", 1.0) ss = openmc.Material() - ss.set_density('g/cm3', 5.0) - ss.add_nuclide('Fe56', 1.0) + ss.set_density("g/cm3", 5.0) + ss.add_nuclide("Fe56", 1.0) model.materials.extend([tungsten, ss]) # Create nested torii with very large major radii R = 1000.0 vacuum = openmc.ZTorus(a=R, b=30.0, c=30.0) first_wall = openmc.ZTorus(a=R, b=35.0, c=35.0) - vessel = openmc.ZTorus(a=R, b=40.0, c=40.0, boundary_type='vacuum') + vessel = openmc.ZTorus(a=R, b=40.0, c=40.0, boundary_type="vacuum") cell1 = openmc.Cell(region=-vacuum) cell2 = openmc.Cell(fill=tungsten, region=+vacuum & -first_wall) cell3 = openmc.Cell(fill=ss, region=+first_wall & -vessel) model.geometry = openmc.Geometry([cell1, cell2, cell3]) - model.settings.run_mode ='fixed source' + model.settings.run_mode = "fixed source" model.settings.particles = 1000 model.settings.batches = 10 - model.settings.source = openmc.IndependentSource(space=openmc.stats.Point((-R, 0, 0,))) + model.settings.source = openmc.IndependentSource( + space=openmc.stats.Point( + ( + -R, + 0, + 0, + ) + ) + ) tally = openmc.Tally() - tally.scores = ['flux'] + tally.scores = ["flux"] model.tallies.append(tally) return model def test_torus_large_major(model): - harness = PyAPITestHarness('statepoint.10.h5', model) + harness = PyAPITestHarness("statepoint.10.h5", model) harness.main() diff --git a/tests/regression_tests/torus/test.py b/tests/regression_tests/torus/test.py index 8970a5cc77d..d69dd5728ff 100644 --- a/tests/regression_tests/torus/test.py +++ b/tests/regression_tests/torus/test.py @@ -8,19 +8,20 @@ def model(): model = openmc.Model() fuel = openmc.Material() - fuel.set_density('g/cm3', 12.0) - fuel.add_nuclide('U235', 1.0) + fuel.set_density("g/cm3", 12.0) + fuel.add_nuclide("U235", 1.0) al = openmc.Material() - al.set_density('g/cm3', 1.0) - al.add_nuclide('H1', 1.0) + al.set_density("g/cm3", 1.0) + al.add_nuclide("H1", 1.0) model.materials.extend([fuel, al]) # šŸ©šŸ©šŸ© zt = openmc.ZTorus(a=3, b=1.5, c=1) xt = openmc.XTorus(x0=6, a=3, b=1.5, c=1) yt = openmc.YTorus(x0=6, a=6, b=1, c=0.75) - box = openmc.model.RectangularParallelepiped(-5, 14, -5, 5, -8, 8, - boundary_type='vacuum') + box = openmc.model.RectangularParallelepiped( + -5, 14, -5, 5, -8, 8, boundary_type="vacuum" + ) xt_cell = openmc.Cell(fill=fuel, region=-xt) yt_cell = openmc.Cell(fill=fuel, region=-yt) @@ -35,5 +36,5 @@ def model(): def test_torus(model): - harness = PyAPITestHarness('statepoint.10.h5', model) + harness = PyAPITestHarness("statepoint.10.h5", model) harness.main() diff --git a/tests/regression_tests/trace/test.py b/tests/regression_tests/trace/test.py index 79dcaa1060a..a297ffed3d8 100644 --- a/tests/regression_tests/trace/test.py +++ b/tests/regression_tests/trace/test.py @@ -2,5 +2,5 @@ def test_trace(): - harness = TestHarness('statepoint.10.h5') + harness = TestHarness("statepoint.10.h5") harness.main() diff --git a/tests/regression_tests/track_output/test.py b/tests/regression_tests/track_output/test.py index 62526117dfb..5d76b22320e 100644 --- a/tests/regression_tests/track_output/test.py +++ b/tests/regression_tests/track_output/test.py @@ -15,25 +15,27 @@ def _test_output_created(self): """Make sure statepoint.* and track* have been created.""" TestHarness._test_output_created(self) - if config['mpi'] and int(config['mpi_np']) > 1: - outputs = Path.cwd().glob('tracks_p*.h5') - assert len(list(outputs)) == int(config['mpi_np']) + if config["mpi"] and int(config["mpi_np"]) > 1: + outputs = Path.cwd().glob("tracks_p*.h5") + assert len(list(outputs)) == int(config["mpi_np"]) else: - assert Path('tracks.h5').is_file() + assert Path("tracks.h5").is_file() def _get_results(self): """Get data from track file and return as a string.""" # For MPI mode, combine track files - if config['mpi']: - call(['../../../scripts/openmc-track-combine', '-o', 'tracks.h5'] + - glob.glob('tracks_p*.h5')) + if config["mpi"]: + call( + ["../../../scripts/openmc-track-combine", "-o", "tracks.h5"] + + glob.glob("tracks_p*.h5") + ) # Get string of track file information - outstr = '' - tracks = openmc.Tracks('tracks.h5') + outstr = "" + tracks = openmc.Tracks("tracks.h5") for track in tracks: - with np.printoptions(formatter={'float_kind': '{:.6e}'.format}): + with np.printoptions(formatter={"float_kind": "{:.6e}".format}): for ptrack in track: outstr += f"{ptrack.particle} {ptrack.states}\n" @@ -41,7 +43,7 @@ def _get_results(self): def _cleanup(self): TestHarness._cleanup(self) - output = glob.glob('tracks*') + glob.glob('poly*') + output = glob.glob("tracks*") + glob.glob("poly*") for f in output: if os.path.exists(f): os.remove(f) @@ -50,6 +52,6 @@ def _cleanup(self): def test_track_output(): # If vtk python module is not available, we can't run track.py so skip this # test. - vtk = pytest.importorskip('vtk') - harness = TrackTestHarness('statepoint.2.h5') + vtk = pytest.importorskip("vtk") + harness = TrackTestHarness("statepoint.2.h5") harness.main() diff --git a/tests/regression_tests/translation/test.py b/tests/regression_tests/translation/test.py index 876db736b93..32db53a3254 100644 --- a/tests/regression_tests/translation/test.py +++ b/tests/regression_tests/translation/test.py @@ -2,5 +2,5 @@ def test_translation(): - harness = TestHarness('statepoint.10.h5') + harness = TestHarness("statepoint.10.h5") harness.main() diff --git a/tests/regression_tests/trigger_batch_interval/test.py b/tests/regression_tests/trigger_batch_interval/test.py index e1617450227..525cc50e0ae 100644 --- a/tests/regression_tests/trigger_batch_interval/test.py +++ b/tests/regression_tests/trigger_batch_interval/test.py @@ -2,5 +2,5 @@ def test_trigger_batch_interval(): - harness = TestHarness('statepoint.15.h5') + harness = TestHarness("statepoint.15.h5") harness.main() diff --git a/tests/regression_tests/trigger_no_batch_interval/test.py b/tests/regression_tests/trigger_no_batch_interval/test.py index 4a40def5aa7..58cd98ee544 100644 --- a/tests/regression_tests/trigger_no_batch_interval/test.py +++ b/tests/regression_tests/trigger_no_batch_interval/test.py @@ -2,5 +2,5 @@ def test_trigger_no_batch_interval(): - harness = TestHarness('statepoint.15.h5') + harness = TestHarness("statepoint.15.h5") harness.main() diff --git a/tests/regression_tests/trigger_no_status/test.py b/tests/regression_tests/trigger_no_status/test.py index 75ae9a8cc9c..c7f41cebf5b 100644 --- a/tests/regression_tests/trigger_no_status/test.py +++ b/tests/regression_tests/trigger_no_status/test.py @@ -2,5 +2,5 @@ def test_trigger_no_status(): - harness = TestHarness('statepoint.10.h5') + harness = TestHarness("statepoint.10.h5") harness.main() diff --git a/tests/regression_tests/trigger_statepoint_restart/test.py b/tests/regression_tests/trigger_statepoint_restart/test.py index 8144c1d0b0a..8b42e471d4a 100644 --- a/tests/regression_tests/trigger_statepoint_restart/test.py +++ b/tests/regression_tests/trigger_statepoint_restart/test.py @@ -13,38 +13,37 @@ def model(): # Materials mat = openmc.Material() - mat.set_density('g/cm3', 4.5) - mat.add_nuclide('U235', 1.0) + mat.set_density("g/cm3", 4.5) + mat.add_nuclide("U235", 1.0) materials = openmc.Materials([mat]) # Geometry - sph = openmc.Sphere(r=10.0, boundary_type='vacuum') + sph = openmc.Sphere(r=10.0, boundary_type="vacuum") cell = openmc.Cell(fill=mat, region=-sph) geometry = openmc.Geometry([cell]) # Settings settings = openmc.Settings() - settings.run_mode = 'eigenvalue' + settings.run_mode = "eigenvalue" settings.batches = 15 settings.inactive = 10 settings.particles = 400 # Choose a sufficiently low threshold to enable use of trigger - settings.keff_trigger = {'type': 'std_dev', 'threshold': 0.003} + settings.keff_trigger = {"type": "std_dev", "threshold": 0.003} settings.trigger_max_batches = 1000 settings.trigger_batch_interval = 1 settings.trigger_active = True - settings.verbosity = 1 # to test that this works even with no output + settings.verbosity = 1 # to test that this works even with no output # Tallies t = openmc.Tally() - t.scores = ['flux'] + t.scores = ["flux"] tallies = openmc.Tallies([t]) # Put it all together - model = openmc.model.Model(materials=materials, - geometry=geometry, - settings=settings, - tallies=tallies) + model = openmc.model.Model( + materials=materials, geometry=geometry, settings=settings, tallies=tallies + ) return model @@ -59,16 +58,14 @@ def __init__(self, statepoint, model=None): def _test_output_created(self): """Make sure statepoint files have been created.""" spfiles = sorted(glob.glob(self._sp_pattern)) - assert len(spfiles) == 2, \ - 'Two statepoint files should have been created' + assert len(spfiles) == 2, "Two statepoint files should have been created" if not self._final_sp: # First non-restart run self._restart_sp = spfiles[0] self._final_sp = spfiles[1] else: # Second restart run - assert spfiles[1] == self._final_sp, \ - 'Final statepoint names were different' + assert spfiles[1] == self._final_sp, "Final statepoint names were different" # Use the final_sp as the sp_name for the 'standard' results tests self._sp_name = self._final_sp @@ -79,20 +76,20 @@ def execute_test(self): generated using the normal PyAPITestHarness update methods. """ try: - args = {'openmc_exec': config['exe'], 'event_based': config['event']} - if config['mpi']: - args['mpi_args'] = [config['mpiexec'], '-n', config['mpi_np']] + args = {"openmc_exec": config["exe"], "event_based": config["event"]} + if config["mpi"]: + args["mpi_args"] = [config["mpiexec"], "-n", config["mpi_np"]] # First non-restart run spfile = self._model.run(**args) sp_batchno_1 = 0 - print('Last sp file: %s' % spfile) + print("Last sp file: %s" % spfile) assert spfile with openmc.StatePoint(spfile) as sp: - sp_batchno_1 = sp.current_batch - keff_1 = sp.keff + sp_batchno_1 = sp.current_batch + keff_1 = sp.keff assert sp_batchno_1 > 5 - print('Last batch no = %d' % sp_batchno_1) + print("Last batch no = %d" % sp_batchno_1) self._write_inputs(self._get_inputs()) self._compare_inputs() self._test_output_created() @@ -102,19 +99,19 @@ def execute_test(self): # Second restart run restart_spfile = glob.glob(os.path.join(os.getcwd(), self._restart_sp)) assert len(restart_spfile) == 1 - args['restart_file'] = restart_spfile[0] + args["restart_file"] = restart_spfile[0] spfile = self._model.run(**args) sp_batchno_2 = 0 assert spfile with openmc.StatePoint(spfile) as sp: - sp_batchno_2 = sp.current_batch - keff_2 = sp.keff + sp_batchno_2 = sp.current_batch + keff_2 = sp.keff assert sp_batchno_2 > 5 - assert sp_batchno_1 == sp_batchno_2, \ - 'Different final batch number after restart' + assert ( + sp_batchno_1 == sp_batchno_2 + ), "Different final batch number after restart" # need str() here as uncertainties.ufloat instances are always different - assert str(keff_1) == str(keff_2), \ - 'Different final keff after restart' + assert str(keff_1) == str(keff_2), "Different final keff after restart" self._write_inputs(self._get_inputs()) self._compare_inputs() self._test_output_created() @@ -127,5 +124,5 @@ def execute_test(self): def test_trigger_statepoint_restart(model): # Assuming we converge within 1000 batches, the statepoint filename # should include the batch number padded by at least one '0'. - harness = TriggerStatepointRestartTestHarness('statepoint.0*.h5', model) + harness = TriggerStatepointRestartTestHarness("statepoint.0*.h5", model) harness.main() diff --git a/tests/regression_tests/trigger_tallies/test.py b/tests/regression_tests/trigger_tallies/test.py index f8493c0c68c..d81ed554406 100644 --- a/tests/regression_tests/trigger_tallies/test.py +++ b/tests/regression_tests/trigger_tallies/test.py @@ -2,5 +2,5 @@ def test_trigger_tallies(): - harness = TestHarness('statepoint.15.h5') + harness = TestHarness("statepoint.15.h5") harness.main() diff --git a/tests/regression_tests/triso/test.py b/tests/regression_tests/triso/test.py index 3fa5e3c60d2..7e3eb1e7b85 100644 --- a/tests/regression_tests/triso/test.py +++ b/tests/regression_tests/triso/test.py @@ -9,40 +9,39 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Define TRISO matrials fuel = openmc.Material() - fuel.set_density('g/cm3', 10.5) - fuel.add_nuclide('U235', 0.14154) - fuel.add_nuclide('U238', 0.85846) - fuel.add_nuclide('C0', 0.5) - fuel.add_nuclide('O16', 1.5) + fuel.set_density("g/cm3", 10.5) + fuel.add_nuclide("U235", 0.14154) + fuel.add_nuclide("U238", 0.85846) + fuel.add_nuclide("C0", 0.5) + fuel.add_nuclide("O16", 1.5) porous_carbon = openmc.Material() - porous_carbon.set_density('g/cm3', 1.0) - porous_carbon.add_nuclide('C0', 1.0) - porous_carbon.add_s_alpha_beta('c_Graphite') + porous_carbon.set_density("g/cm3", 1.0) + porous_carbon.add_nuclide("C0", 1.0) + porous_carbon.add_s_alpha_beta("c_Graphite") ipyc = openmc.Material() - ipyc.set_density('g/cm3', 1.90) - ipyc.add_nuclide('C0', 1.0) - ipyc.add_s_alpha_beta('c_Graphite') + ipyc.set_density("g/cm3", 1.90) + ipyc.add_nuclide("C0", 1.0) + ipyc.add_s_alpha_beta("c_Graphite") sic = openmc.Material() - sic.set_density('g/cm3', 3.20) - sic.add_nuclide('C0', 1.0) - sic.add_element('Si', 1.0) + sic.set_density("g/cm3", 3.20) + sic.add_nuclide("C0", 1.0) + sic.add_element("Si", 1.0) opyc = openmc.Material() - opyc.set_density('g/cm3', 1.87) - opyc.add_nuclide('C0', 1.0) - opyc.add_s_alpha_beta('c_Graphite') + opyc.set_density("g/cm3", 1.87) + opyc.add_nuclide("C0", 1.0) + opyc.add_s_alpha_beta("c_Graphite") graphite = openmc.Material() - graphite.set_density('g/cm3', 1.1995) - graphite.add_nuclide('C0', 1.0) - graphite.add_s_alpha_beta('c_Graphite') + graphite.set_density("g/cm3", 1.1995) + graphite.add_nuclide("C0", 1.0) + graphite.add_s_alpha_beta("c_Graphite") # Create TRISO particles - spheres = [openmc.Sphere(r=r*1e-4) - for r in [212.5, 312.5, 347.5, 382.5]] + spheres = [openmc.Sphere(r=r * 1e-4) for r in [212.5, 312.5, 347.5, 382.5]] c1 = openmc.Cell(fill=fuel, region=-spheres[0]) c2 = openmc.Cell(fill=porous_carbon, region=+spheres[0] & -spheres[1]) c3 = openmc.Cell(fill=ipyc, region=+spheres[1] & -spheres[2]) @@ -51,27 +50,26 @@ def __init__(self, *args, **kwargs): inner_univ = openmc.Universe(cells=[c1, c2, c3, c4, c5]) # Define box to contain lattice and to pack TRISO particles in - min_x = openmc.XPlane(-0.5, boundary_type='reflective') - max_x = openmc.XPlane(0.5, boundary_type='reflective') - min_y = openmc.YPlane(-0.5, boundary_type='reflective') - max_y = openmc.YPlane(0.5, boundary_type='reflective') - min_z = openmc.ZPlane(-0.5, boundary_type='reflective') - max_z = openmc.ZPlane(0.5, boundary_type='reflective') + min_x = openmc.XPlane(-0.5, boundary_type="reflective") + max_x = openmc.XPlane(0.5, boundary_type="reflective") + min_y = openmc.YPlane(-0.5, boundary_type="reflective") + max_y = openmc.YPlane(0.5, boundary_type="reflective") + min_z = openmc.ZPlane(-0.5, boundary_type="reflective") + max_z = openmc.ZPlane(0.5, boundary_type="reflective") box_region = +min_x & -max_x & +min_y & -max_y & +min_z & -max_z box = openmc.Cell(region=box_region) - outer_radius = 422.5*1e-4 + outer_radius = 422.5 * 1e-4 centers = openmc.model.pack_spheres( - radius=outer_radius, region=box_region, num_spheres=100, seed=1) - trisos = [openmc.model.TRISO(outer_radius, inner_univ, c) - for c in centers] + radius=outer_radius, region=box_region, num_spheres=100, seed=1 + ) + trisos = [openmc.model.TRISO(outer_radius, inner_univ, c) for c in centers] # Create lattice ll, ur = box.region.bounding_box shape = (3, 3, 3) pitch = (ur - ll) / shape - lattice = openmc.model.create_triso_lattice( - trisos, ll, pitch, shape, graphite) + lattice = openmc.model.create_triso_lattice(trisos, ll, pitch, shape, graphite) box.fill = lattice root = openmc.Universe(0, cells=[box]) @@ -84,10 +82,11 @@ def __init__(self, *args, **kwargs): settings.source = openmc.IndependentSource(space=openmc.stats.Point()) self._model.settings = settings - self._model.materials = openmc.Materials([fuel, porous_carbon, ipyc, - sic, opyc, graphite]) + self._model.materials = openmc.Materials( + [fuel, porous_carbon, ipyc, sic, opyc, graphite] + ) def test_triso(): - harness = TRISOTestHarness('statepoint.4.h5', model=openmc.Model()) + harness = TRISOTestHarness("statepoint.4.h5", model=openmc.Model()) harness.main() diff --git a/tests/regression_tests/uniform_fs/test.py b/tests/regression_tests/uniform_fs/test.py index 64d997909ca..95f9ea97e1e 100644 --- a/tests/regression_tests/uniform_fs/test.py +++ b/tests/regression_tests/uniform_fs/test.py @@ -2,5 +2,5 @@ def test_uniform_fs(): - harness = TestHarness('statepoint.10.h5') + harness = TestHarness("statepoint.10.h5") harness.main() diff --git a/tests/regression_tests/universe/test.py b/tests/regression_tests/universe/test.py index d5ed9645bd0..228f3a17b48 100644 --- a/tests/regression_tests/universe/test.py +++ b/tests/regression_tests/universe/test.py @@ -2,5 +2,5 @@ def test_universe(): - harness = TestHarness('statepoint.10.h5') + harness = TestHarness("statepoint.10.h5") harness.main() diff --git a/tests/regression_tests/unstructured_mesh/test.py b/tests/regression_tests/unstructured_mesh/test.py index 0082198ddec..ab24addfa1c 100644 --- a/tests/regression_tests/unstructured_mesh/test.py +++ b/tests/regression_tests/unstructured_mesh/test.py @@ -16,21 +16,23 @@ class UnstructuredMeshTest(PyAPITestHarness): ELEM_PER_VOXEL = 12 - def __init__(self, - statepoint_name, - model, - inputs_true='inputs_true.dat', - holes=False, - scale_factor=10.0): + def __init__( + self, + statepoint_name, + model, + inputs_true="inputs_true.dat", + holes=False, + scale_factor=10.0, + ): super().__init__(statepoint_name, model, inputs_true) - self.holes = holes # holes in the test mesh + self.holes = holes # holes in the test mesh self.scale_bounding_cell(scale_factor) def scale_bounding_cell(self, scale_factor): geometry = self._model.geometry for surface in geometry.get_all_surfaces().values(): - if surface.boundary_type != 'vacuum': + if surface.boundary_type != "vacuum": continue for coeff in surface._coefficients: surface._coefficients[coeff] *= scale_factor @@ -73,15 +75,15 @@ def _compare_results(self): # we expect these results to be the same to within at least ten # decimal places - decimals = 10 if umesh_tally.estimator == 'collision' else 8 - np.testing.assert_array_almost_equal(np.sort(unstructured_data), - np.sort(reg_mesh_data), - decimals) + decimals = 10 if umesh_tally.estimator == "collision" else 8 + np.testing.assert_array_almost_equal( + np.sort(unstructured_data), np.sort(reg_mesh_data), decimals + ) def get_mesh_tally_data(self, tally, structured=False): - data = tally.get_reshaped_data(value='mean') + data = tally.get_reshaped_data(value="mean") if structured: - data = data.reshape((-1, self.ELEM_PER_VOXEL)) + data = data.reshape((-1, self.ELEM_PER_VOXEL)) else: data.shape = (data.size, 1) return np.sum(data, axis=1) @@ -100,8 +102,8 @@ def update_results(self): def _cleanup(self): super()._cleanup() - output = glob.glob('tally*.vtk') - output += glob.glob('tally*.e') + output = glob.glob("tally*.vtk") + output += glob.glob("tally*.e") for f in output: if os.path.exists(f): os.remove(f) @@ -118,7 +120,7 @@ def model(): fuel_mat = openmc.Material(name="fuel") fuel_mat.add_nuclide("U235", 1.0) - fuel_mat.set_density('g/cc', 4.5) + fuel_mat.set_density("g/cc", 4.5) materials.append(fuel_mat) zirc_mat = openmc.Material(name="zircaloy") @@ -145,9 +147,14 @@ def model(): fuel_max_z = openmc.ZPlane(5.0, name="maximum z") fuel_cell = openmc.Cell(name="fuel") - fuel_cell.region = +fuel_min_x & -fuel_max_x & \ - +fuel_min_y & -fuel_max_y & \ - +fuel_min_z & -fuel_max_z + fuel_cell.region = ( + +fuel_min_x + & -fuel_max_x + & +fuel_min_y + & -fuel_max_y + & +fuel_min_z + & -fuel_max_z + ) fuel_cell.fill = fuel_mat clad_min_x = openmc.XPlane(-6.0, name="minimum x") @@ -160,44 +167,50 @@ def model(): clad_max_z = openmc.ZPlane(6.0, name="maximum z") clad_cell = openmc.Cell(name="clad") - clad_cell.region = (-fuel_min_x | +fuel_max_x | - -fuel_min_y | +fuel_max_y | - -fuel_min_z | +fuel_max_z) & \ - (+clad_min_x & -clad_max_x & - +clad_min_y & -clad_max_y & - +clad_min_z & -clad_max_z) + clad_cell.region = ( + -fuel_min_x + | +fuel_max_x + | -fuel_min_y + | +fuel_max_y + | -fuel_min_z + | +fuel_max_z + ) & ( + +clad_min_x + & -clad_max_x + & +clad_min_y + & -clad_max_y + & +clad_min_z + & -clad_max_z + ) clad_cell.fill = zirc_mat # set bounding cell dimension to one # this will be updated later according to the test case parameters - water_min_x = openmc.XPlane(x0=-1.0, - name="minimum x", - boundary_type='vacuum') - water_max_x = openmc.XPlane(x0=1.0, - name="maximum x", - boundary_type='vacuum') - - water_min_y = openmc.YPlane(y0=-1.0, - name="minimum y", - boundary_type='vacuum') - water_max_y = openmc.YPlane(y0=1.0, - name="maximum y", - boundary_type='vacuum') - - water_min_z = openmc.ZPlane(z0=-1.0, - name="minimum z", - boundary_type='vacuum') - water_max_z = openmc.ZPlane(z0=1.0, - name="maximum z", - boundary_type='vacuum') + water_min_x = openmc.XPlane(x0=-1.0, name="minimum x", boundary_type="vacuum") + water_max_x = openmc.XPlane(x0=1.0, name="maximum x", boundary_type="vacuum") + + water_min_y = openmc.YPlane(y0=-1.0, name="minimum y", boundary_type="vacuum") + water_max_y = openmc.YPlane(y0=1.0, name="maximum y", boundary_type="vacuum") + + water_min_z = openmc.ZPlane(z0=-1.0, name="minimum z", boundary_type="vacuum") + water_max_z = openmc.ZPlane(z0=1.0, name="maximum z", boundary_type="vacuum") water_cell = openmc.Cell(name="water") - water_cell.region = (-clad_min_x | +clad_max_x | - -clad_min_y | +clad_max_y | - -clad_min_z | +clad_max_z) & \ - (+water_min_x & -water_max_x & - +water_min_y & -water_max_y & - +water_min_z & -water_max_z) + water_cell.region = ( + -clad_min_x + | +clad_max_x + | -clad_min_y + | +clad_max_y + | -clad_min_z + | +clad_max_z + ) & ( + +water_min_x + & -water_max_x + & +water_min_y + & -water_max_y + & +water_min_z + & -water_max_z + ) water_cell.fill = water_mat # create a containing universe @@ -214,13 +227,13 @@ def model(): regular_mesh_filter = openmc.MeshFilter(mesh=regular_mesh) regular_mesh_tally = openmc.Tally(name="regular mesh tally") regular_mesh_tally.filters = [regular_mesh_filter] - regular_mesh_tally.scores = ['flux'] + regular_mesh_tally.scores = ["flux"] model.tallies = openmc.Tallies([regular_mesh_tally]) ### Settings ### settings = openmc.Settings() - settings.run_mode = 'fixed source' + settings.run_mode = "fixed source" settings.particles = 1000 settings.batches = 10 @@ -230,7 +243,7 @@ def model(): phi = openmc.stats.Discrete(x=[0.0], p=[1.0]) space = openmc.stats.SphericalIndependent(r, cos_theta, phi) - energy = openmc.stats.Discrete(x=[15.e+06], p=[1.0]) + energy = openmc.stats.Discrete(x=[15.0e06], p=[1.0]) source = openmc.IndependentSource(space=space, energy=energy) settings.source = source @@ -239,88 +252,95 @@ def model(): return model -param_values = (['libmesh', 'moab'], # mesh libraries - ['collision', 'tracklength'], # estimators - [True, False], # geometry outside of the mesh - [(333, 90, 77), None]) # location of holes in the mesh +param_values = ( + ["libmesh", "moab"], # mesh libraries + ["collision", "tracklength"], # estimators + [True, False], # geometry outside of the mesh + [(333, 90, 77), None], +) # location of holes in the mesh test_cases = [] for i, (lib, estimator, ext_geom, holes) in enumerate(product(*param_values)): - test_cases.append({'library' : lib, - 'estimator' : estimator, - 'external_geom' : ext_geom, - 'holes' : holes, - 'inputs_true' : 'inputs_true{}.dat'.format(i)}) + test_cases.append( + { + "library": lib, + "estimator": estimator, + "external_geom": ext_geom, + "holes": holes, + "inputs_true": "inputs_true{}.dat".format(i), + } + ) @pytest.mark.parametrize("test_opts", test_cases) def test_unstructured_mesh_tets(model, test_opts): # skip the test if the library is not enabled - if test_opts['library'] == 'moab' and not openmc.lib._dagmc_enabled(): + if test_opts["library"] == "moab" and not openmc.lib._dagmc_enabled(): pytest.skip("DAGMC (and MOAB) mesh not enbaled in this build.") - if test_opts['library'] == 'libmesh' and not openmc.lib._libmesh_enabled(): + if test_opts["library"] == "libmesh" and not openmc.lib._libmesh_enabled(): pytest.skip("LibMesh is not enabled in this build.") # skip the tracklength test for libmesh - if test_opts['library'] == 'libmesh' and \ - test_opts['estimator'] == 'tracklength': - pytest.skip("Tracklength tallies are not supported using libmesh.") + if test_opts["library"] == "libmesh" and test_opts["estimator"] == "tracklength": + pytest.skip("Tracklength tallies are not supported using libmesh.") - if test_opts['holes']: + if test_opts["holes"]: mesh_filename = "test_mesh_tets_w_holes.e" else: mesh_filename = "test_mesh_tets.e" # add reference mesh tally regular_mesh_tally = model.tallies[0] - regular_mesh_tally.estimator = test_opts['estimator'] + regular_mesh_tally.estimator = test_opts["estimator"] # add analagous unstructured mesh tally - uscd_mesh = openmc.UnstructuredMesh(mesh_filename, test_opts['library']) - if test_opts['library'] == 'moab': - uscd_mesh.options = 'MAX_DEPTH=15;PLANE_SET=2' + uscd_mesh = openmc.UnstructuredMesh(mesh_filename, test_opts["library"]) + if test_opts["library"] == "moab": + uscd_mesh.options = "MAX_DEPTH=15;PLANE_SET=2" uscd_filter = openmc.MeshFilter(mesh=uscd_mesh) # create tallies uscd_tally = openmc.Tally(name="unstructured mesh tally") uscd_tally.filters = [uscd_filter] - uscd_tally.scores = ['flux'] - uscd_tally.estimator = test_opts['estimator'] + uscd_tally.scores = ["flux"] + uscd_tally.estimator = test_opts["estimator"] model.tallies.append(uscd_tally) # modify model geometry according to test opts - if test_opts['external_geom']: + if test_opts["external_geom"]: scale_factor = 15.0 else: scale_factor = 10.0 - harness = UnstructuredMeshTest('statepoint.10.h5', - model, - test_opts['inputs_true'], - test_opts['holes'], - scale_factor) + harness = UnstructuredMeshTest( + "statepoint.10.h5", + model, + test_opts["inputs_true"], + test_opts["holes"], + scale_factor, + ) harness.main() -@pytest.mark.skipif(not openmc.lib._libmesh_enabled(), - reason='LibMesh is not enabled in this build.') +@pytest.mark.skipif( + not openmc.lib._libmesh_enabled(), reason="LibMesh is not enabled in this build." +) def test_unstructured_mesh_hexes(model): regular_mesh_tally = model.tallies[0] - regular_mesh_tally.estimator = 'collision' + regular_mesh_tally.estimator = "collision" # add analagous unstructured mesh tally - uscd_mesh = openmc.UnstructuredMesh('test_mesh_hexes.e', 'libmesh') + uscd_mesh = openmc.UnstructuredMesh("test_mesh_hexes.e", "libmesh") uscd_filter = openmc.MeshFilter(mesh=uscd_mesh) # create tallies uscd_tally = openmc.Tally(name="unstructured mesh tally") uscd_tally.filters = [uscd_filter] - uscd_tally.scores = ['flux'] - uscd_tally.estimator = 'collision' + uscd_tally.scores = ["flux"] + uscd_tally.estimator = "collision" model.tallies.append(uscd_tally) - harness = UnstructuredMeshTest('statepoint.10.h5', - model) + harness = UnstructuredMeshTest("statepoint.10.h5", model) harness.ELEM_PER_VOXEL = 1 harness.main() diff --git a/tests/regression_tests/void/test.py b/tests/regression_tests/void/test.py index 76c15eb39f6..ee2b9512bfa 100644 --- a/tests/regression_tests/void/test.py +++ b/tests/regression_tests/void/test.py @@ -4,23 +4,26 @@ from tests.testing_harness import PyAPITestHarness + @pytest.fixture def model(): model = openmc.model.Model() zn = openmc.Material() - zn.set_density('g/cm3', 7.14) - zn.add_nuclide('Zn64', 1.0) + zn.set_density("g/cm3", 7.14) + zn.add_nuclide("Zn64", 1.0) model.materials.append(zn) radii = np.linspace(1.0, 100.0) surfs = [openmc.Sphere(r=r) for r in radii] - surfs[-1].boundary_type = 'vacuum' - cells = [openmc.Cell(fill=(zn if i % 2 == 0 else None), region=region) - for i, region in enumerate(openmc.model.subdivide(surfs))] + surfs[-1].boundary_type = "vacuum" + cells = [ + openmc.Cell(fill=(zn if i % 2 == 0 else None), region=region) + for i, region in enumerate(openmc.model.subdivide(surfs)) + ] model.geometry = openmc.Geometry(cells) - model.settings.run_mode = 'fixed source' + model.settings.run_mode = "fixed source" model.settings.batches = 3 model.settings.particles = 1000 model.settings.source = openmc.IndependentSource(space=openmc.stats.Point()) @@ -28,12 +31,12 @@ def model(): cell_filter = openmc.CellFilter(cells) tally = openmc.Tally() tally.filters = [cell_filter] - tally.scores = ['total'] + tally.scores = ["total"] model.tallies.append(tally) return model def test_void(model): - harness = PyAPITestHarness('statepoint.3.h5', model) + harness = PyAPITestHarness("statepoint.3.h5", model) harness.main() diff --git a/tests/regression_tests/volume_calc/test.py b/tests/regression_tests/volume_calc/test.py index c94d16c7975..47b0326e70d 100644 --- a/tests/regression_tests/volume_calc/test.py +++ b/tests/regression_tests/volume_calc/test.py @@ -19,37 +19,41 @@ def __init__(self, is_ce, *args, **kwargs): self.exp_variance = 5e-02 self.is_ce = is_ce if not is_ce: - self.inputs_true = 'inputs_true_mg.dat' + self.inputs_true = "inputs_true_mg.dat" # Define materials water = openmc.Material(1) - water.add_nuclide('H1', 2.0) - water.add_nuclide('O16', 1.0) - water.add_nuclide('B10', 0.0001) + water.add_nuclide("H1", 2.0) + water.add_nuclide("O16", 1.0) + water.add_nuclide("B10", 0.0001) if self.is_ce: - water.add_s_alpha_beta('c_H_in_H2O') - water.set_density('g/cc', 1.0) + water.add_s_alpha_beta("c_H_in_H2O") + water.set_density("g/cc", 1.0) fuel = openmc.Material(2) - fuel.add_nuclide('U235', 1.0) - fuel.add_nuclide('Mo99', 0.1) - fuel.set_density('g/cc', 4.5) + fuel.add_nuclide("U235", 1.0) + fuel.add_nuclide("Mo99", 0.1) + fuel.set_density("g/cc", 4.5) materials = openmc.Materials((water, fuel)) if not self.is_ce: - materials.cross_sections = 'mg_lib.h5' + materials.cross_sections = "mg_lib.h5" self._model.materials = materials - cyl = openmc.ZCylinder(surface_id=1, r=1.0, boundary_type='vacuum') - top_sphere = openmc.Sphere(surface_id=2, z0=5., r=1., boundary_type='vacuum') - top_plane = openmc.ZPlane(surface_id=3, z0=5.) - bottom_sphere = openmc.Sphere(surface_id=4, z0=-5., r=1., boundary_type='vacuum') - bottom_plane = openmc.ZPlane(surface_id=5, z0=-5.) + cyl = openmc.ZCylinder(surface_id=1, r=1.0, boundary_type="vacuum") + top_sphere = openmc.Sphere(surface_id=2, z0=5.0, r=1.0, boundary_type="vacuum") + top_plane = openmc.ZPlane(surface_id=3, z0=5.0) + bottom_sphere = openmc.Sphere( + surface_id=4, z0=-5.0, r=1.0, boundary_type="vacuum" + ) + bottom_plane = openmc.ZPlane(surface_id=5, z0=-5.0) # Define geometry inside_cyl = openmc.Cell(1, fill=fuel, region=-cyl & -top_plane & +bottom_plane) top_hemisphere = openmc.Cell(2, fill=water, region=-top_sphere & +top_plane) - bottom_hemisphere = openmc.Cell(3, fill=water, region=-bottom_sphere & -top_plane) + bottom_hemisphere = openmc.Cell( + 3, fill=water, region=-bottom_sphere & -top_plane + ) root = openmc.Universe(0, cells=(inside_cyl, top_hemisphere, bottom_hemisphere)) self._model.geometry = openmc.Geometry(root) @@ -62,47 +66,46 @@ def __init__(self, is_ce, *args, **kwargs): openmc.VolumeCalculation([root], 100000, ll, ur), openmc.VolumeCalculation(list(root.cells.values()), 100), openmc.VolumeCalculation([water, fuel], 100, ll, ur), - openmc.VolumeCalculation(list(root.cells.values()), 100) + openmc.VolumeCalculation(list(root.cells.values()), 100), ] - vol_calcs[3].set_trigger(self.exp_std_dev, 'std_dev') + vol_calcs[3].set_trigger(self.exp_std_dev, "std_dev") - vol_calcs[4].set_trigger(self.exp_rel_err, 'rel_err') + vol_calcs[4].set_trigger(self.exp_rel_err, "rel_err") - vol_calcs[5].set_trigger(self.exp_variance, 'variance') + vol_calcs[5].set_trigger(self.exp_variance, "variance") # Define settings settings = openmc.Settings() - settings.run_mode = 'volume' + settings.run_mode = "volume" if not self.is_ce: - settings.energy_mode = 'multi-group' + settings.energy_mode = "multi-group" settings.volume_calculations = vol_calcs self._model.settings = settings # Create the MGXS file if necessary if not self.is_ce: - groups = openmc.mgxs.EnergyGroups(group_edges=[0., 20.e6]) + groups = openmc.mgxs.EnergyGroups(group_edges=[0.0, 20.0e6]) mg_xs_file = openmc.MGXSLibrary(groups) - nu = [2.] - fiss = [1.] - capture = [1.] + nu = [2.0] + fiss = [1.0] + capture = [1.0] absorption_fissile = np.add(fiss, capture) absorption_other = capture - scatter = np.array([[[1.]]]) - total_fissile = np.add(absorption_fissile, - np.sum(scatter[:, :, 0], axis=1)) - total_other = np.add(absorption_other, - np.sum(scatter[:, :, 0], axis=1)) - chi = [1.] - - for iso in ['H1', 'O16', 'B10', 'Mo99', 'U235']: + scatter = np.array([[[1.0]]]) + total_fissile = np.add(absorption_fissile, np.sum(scatter[:, :, 0], axis=1)) + total_other = np.add(absorption_other, np.sum(scatter[:, :, 0], axis=1)) + chi = [1.0] + + for iso in ["H1", "O16", "B10", "Mo99", "U235"]: mat = openmc.XSdata(iso, groups) mat.order = 0 - mat.atomic_weight_ratio = \ + mat.atomic_weight_ratio = ( openmc.data.atomic_mass(iso) / openmc.data.NEUTRON_MASS + ) mat.set_scatter_matrix(scatter) - if iso == 'U235': + if iso == "U235": mat.set_nu_fission(np.multiply(nu, fiss)) mat.set_absorption(absorption_fissile) mat.set_total(total_fissile) @@ -111,35 +114,35 @@ def __init__(self, is_ce, *args, **kwargs): mat.set_absorption(absorption_other) mat.set_total(total_other) mg_xs_file.add_xsdata(mat) - mg_xs_file.export_to_hdf5('mg_lib.h5') + mg_xs_file.export_to_hdf5("mg_lib.h5") def _cleanup(self): super()._cleanup() - output = ['mg_lib.h5'] + output = ["mg_lib.h5"] for f in output: if os.path.exists(f): os.remove(f) def _get_results(self): - outstr = '' - for i, filename in enumerate(sorted(glob.glob('volume_*.h5'))): - outstr += 'Volume calculation {}\n'.format(i) + outstr = "" + for i, filename in enumerate(sorted(glob.glob("volume_*.h5"))): + outstr += "Volume calculation {}\n".format(i) # Read volume calculation results volume_calc = openmc.VolumeCalculation.from_hdf5(filename) - outstr += 'Trigger Type: {}\n'.format(volume_calc.trigger_type) - outstr += 'Trigger threshold: {}\n'.format(volume_calc.threshold) - outstr += 'Iterations: {}\n'.format(volume_calc.iterations) + outstr += "Trigger Type: {}\n".format(volume_calc.trigger_type) + outstr += "Trigger threshold: {}\n".format(volume_calc.threshold) + outstr += "Iterations: {}\n".format(volume_calc.iterations) if i == 3: - assert volume_calc.trigger_type == 'std_dev' + assert volume_calc.trigger_type == "std_dev" assert volume_calc.threshold == self.exp_std_dev elif i == 4: - assert volume_calc.trigger_type == 'rel_err' + assert volume_calc.trigger_type == "rel_err" assert volume_calc.threshold == self.exp_rel_err elif i == 5: - assert volume_calc.trigger_type == 'variance' + assert volume_calc.trigger_type == "variance" assert volume_calc.threshold == self.exp_variance else: assert volume_calc.trigger_type is None @@ -148,17 +151,17 @@ def _get_results(self): # if a trigger is applied, make sure the calculation satisfies the trigger for vol in volume_calc.volumes.values(): - if volume_calc.trigger_type == 'std_dev': + if volume_calc.trigger_type == "std_dev": assert vol.std_dev <= self.exp_std_dev - if volume_calc.trigger_type == 'rel_err': - assert vol.std_dev/vol.nominal_value <= self.exp_rel_err - if volume_calc.trigger_type == 'variance': + if volume_calc.trigger_type == "rel_err": + assert vol.std_dev / vol.nominal_value <= self.exp_rel_err + if volume_calc.trigger_type == "variance": assert vol.std_dev * vol.std_dev <= self.exp_variance # Write cell volumes and total # of atoms for each nuclide for uid, volume in sorted(volume_calc.volumes.items()): - outstr += 'Domain {}: {} cm^3\n'.format(uid, volume) - outstr += str(volume_calc.atoms_dataframe) + '\n' + outstr += "Domain {}: {} cm^3\n".format(uid, volume) + outstr += str(volume_calc.atoms_dataframe) + "\n" return outstr @@ -166,7 +169,7 @@ def _test_output_created(self): pass -@pytest.mark.parametrize('is_ce', [True, False]) +@pytest.mark.parametrize("is_ce", [True, False]) def test_volume_calc(is_ce): - harness = VolumeTest(is_ce, '', model=openmc.Model()) + harness = VolumeTest(is_ce, "", model=openmc.Model()) harness.main() diff --git a/tests/regression_tests/weightwindows/generators/test.py b/tests/regression_tests/weightwindows/generators/test.py index d6a40b44f01..fb1eeec1b95 100644 --- a/tests/regression_tests/weightwindows/generators/test.py +++ b/tests/regression_tests/weightwindows/generators/test.py @@ -10,47 +10,47 @@ def test_ww_generator(run_in_tmpdir): model = openmc.Model() water = openmc.Material() - water.set_density('g/cc', 1.0) - water.add_nuclide('H1', 0.66) - water.add_nuclide('O16', 0.34) + water.set_density("g/cc", 1.0) + water.add_nuclide("H1", 0.66) + water.add_nuclide("O16", 0.34) - s = openmc.Sphere(r=50, boundary_type='vacuum') + s = openmc.Sphere(r=50, boundary_type="vacuum") c = openmc.Cell(fill=water, region=-s) model.geometry = openmc.Geometry([c]) model.settings.particles = 500 model.settings.batches = 5 - model.settings.run_mode = 'fixed source' + model.settings.run_mode = "fixed source" model.settings.max_history_splits = 100 mesh = openmc.RegularMesh.from_domain(model.geometry.root_universe) energy_bounds = np.linspace(0.0, 1e6, 70) - particle = 'neutron' + particle = "neutron" # include another tally to make sure user-specified tallies and those automaticaly # created by weight window generators can coexist tally = openmc.Tally() ef = openmc.EnergyFilter(energy_bounds) tally.filters = [ef] - tally.scores = ['flux'] + tally.scores = ["flux"] model.tallies = [tally] wwg = openmc.WeightWindowGenerator(mesh, energy_bounds, particle) - wwg.update_parameters = {'ratio': 5.0, 'threshold': 0.8, 'value': 'mean'} + wwg.update_parameters = {"ratio": 5.0, "threshold": 0.8, "value": "mean"} model.settings.weight_window_generators = wwg model.run() # we test the effectiveness of the update method elsewhere, so # just test that the generation happens successfully here - assert os.path.exists('weight_windows.h5') + assert os.path.exists("weight_windows.h5") wws_mean = openmc.hdf5_to_wws() assert len(wws_mean) == 1 # check that generation using the relative error works too - wwg.update_parameters['value'] = 'rel_err' + wwg.update_parameters["value"] = "rel_err" model.run() wws_rel_err = openmc.hdf5_to_wws() diff --git a/tests/regression_tests/weightwindows/test.py b/tests/regression_tests/weightwindows/test.py index 864f4f062dc..b34ad50cd88 100644 --- a/tests/regression_tests/weightwindows/test.py +++ b/tests/regression_tests/weightwindows/test.py @@ -14,8 +14,8 @@ def model(): # materials (M4 steel alloy) m4 = openmc.Material() - m4.set_density('g/cc', 2.3) - m4.add_nuclide('H1', 0.168018676) + m4.set_density("g/cc", 2.3) + m4.add_nuclide("H1", 0.168018676) m4.add_nuclide("H2", 1.93244e-05) m4.add_nuclide("O16", 0.561814465) m4.add_nuclide("O17", 0.00021401) @@ -36,7 +36,7 @@ def model(): m4.add_nuclide("Fe58", 1.19737e-05) s0 = openmc.Sphere(r=240) - s1 = openmc.Sphere(r=250, boundary_type='vacuum') + s1 = openmc.Sphere(r=250, boundary_type="vacuum") c0 = openmc.Cell(fill=m4, region=-s0) c1 = openmc.Cell(region=+s0 & -s1) @@ -45,13 +45,13 @@ def model(): # settings settings = model.settings - settings.run_mode = 'fixed source' + settings.run_mode = "fixed source" settings.particles = 200 settings.batches = 2 settings.max_history_splits = 200 settings.photon_transport = True space = Point((0.001, 0.001, 0.001)) - energy = Discrete([14E6], [1.0]) + energy = Discrete([14e6], [1.0]) settings.source = openmc.IndependentSource(space=space, energy=energy) @@ -63,14 +63,14 @@ def model(): mesh_filter = openmc.MeshFilter(mesh) - e_bnds = [0.0, 0.5, 2E7] + e_bnds = [0.0, 0.5, 2e7] energy_filter = openmc.EnergyFilter(e_bnds) - particle_filter = openmc.ParticleFilter(['neutron', 'photon']) + particle_filter = openmc.ParticleFilter(["neutron", "photon"]) tally = openmc.Tally() tally.filters = [mesh_filter, energy_filter, particle_filter] - tally.scores = ['flux'] + tally.scores = ["flux"] model.tallies.append(tally) @@ -78,8 +78,8 @@ def model(): # load pre-generated weight windows # (created using the same tally as above) - ww_n_lower_bnds = np.loadtxt('ww_n.txt') - ww_p_lower_bnds = np.loadtxt('ww_p.txt') + ww_n_lower_bnds = np.loadtxt("ww_n.txt") + ww_p_lower_bnds = np.loadtxt("ww_p.txt") # create a mesh matching the one used # to generate the weight windows @@ -88,19 +88,13 @@ def model(): ww_mesh.upper_right = (240, 240, 240) ww_mesh.dimension = (5, 6, 7) - ww_n = openmc.WeightWindows(ww_mesh, - ww_n_lower_bnds, - None, - 10.0, - e_bnds, - max_lower_bound_ratio=1.5) + ww_n = openmc.WeightWindows( + ww_mesh, ww_n_lower_bnds, None, 10.0, e_bnds, max_lower_bound_ratio=1.5 + ) - ww_p = openmc.WeightWindows(ww_mesh, - ww_p_lower_bnds, - None, - 10.0, - e_bnds, - max_lower_bound_ratio=1.5) + ww_p = openmc.WeightWindows( + ww_mesh, ww_p_lower_bnds, None, 10.0, e_bnds, max_lower_bound_ratio=1.5 + ) model.settings.weight_windows = [ww_n, ww_p] @@ -108,29 +102,35 @@ def model(): def test_weightwindows(model): - test = HashedPyAPITestHarness('statepoint.2.h5', model) + test = HashedPyAPITestHarness("statepoint.2.h5", model) test.main() def test_wwinp_cylindrical(): - ww = openmc.wwinp_to_wws('ww_n_cyl.txt')[0] + ww = openmc.wwinp_to_wws("ww_n_cyl.txt")[0] mesh = ww.mesh assert mesh.dimension == (8, 8, 7) # make sure that the mesh grids are correct - exp_r_grid = np.hstack((np.linspace(0.0, 3.02, 3, endpoint=False), - np.linspace(3.02, 6.0001, 6))).flatten() - - exp_phi_grid = np.hstack((np.linspace(0.0, 0.25, 2, endpoint=False), - np.linspace(0.25, 1.5707, 1, endpoint=False), - np.linspace(1.5707, 3.1415, 2, endpoint=False), - np.linspace(3.1415, 4.7124, 4))).flatten() - - exp_z_grid = np.hstack((np.linspace(0.0, 8.008, 4, endpoint=False), - np.linspace(8.008, 14.002, 4))).flatten() + exp_r_grid = np.hstack( + (np.linspace(0.0, 3.02, 3, endpoint=False), np.linspace(3.02, 6.0001, 6)) + ).flatten() + + exp_phi_grid = np.hstack( + ( + np.linspace(0.0, 0.25, 2, endpoint=False), + np.linspace(0.25, 1.5707, 1, endpoint=False), + np.linspace(1.5707, 3.1415, 2, endpoint=False), + np.linspace(3.1415, 4.7124, 4), + ) + ).flatten() + + exp_z_grid = np.hstack( + (np.linspace(0.0, 8.008, 4, endpoint=False), np.linspace(8.008, 14.002, 4)) + ).flatten() assert isinstance(mesh, openmc.CylindricalMesh) @@ -144,25 +144,34 @@ def test_wwinp_cylindrical(): def test_wwinp_spherical(): - ww = openmc.wwinp_to_wws('ww_n_sph.txt')[0] + ww = openmc.wwinp_to_wws("ww_n_sph.txt")[0] mesh = ww.mesh assert mesh.dimension == (8, 7, 8) # make sure that the mesh grids are correct - exp_r_grid = np.hstack((np.linspace(0.0, 3.02, 3, endpoint=False), - np.linspace(3.02, 6.0001, 6))).flatten() - - exp_theta_grid = np.hstack((np.linspace(0.0, 0.25, 2, endpoint=False), - np.linspace(0.25, 0.5, 1, endpoint=False), - np.linspace(0.5, 0.75, 2, endpoint=False), - np.linspace(0.75, 1.5707, 3))).flatten() - - exp_phi_grid = np.hstack((np.linspace(0.0, 0.25, 2, endpoint=False), - np.linspace(0.25, 0.5, 1, endpoint=False), - np.linspace(0.5, 1.5707, 2, endpoint=False), - np.linspace(1.5707, 3.1415, 4))).flatten() + exp_r_grid = np.hstack( + (np.linspace(0.0, 3.02, 3, endpoint=False), np.linspace(3.02, 6.0001, 6)) + ).flatten() + + exp_theta_grid = np.hstack( + ( + np.linspace(0.0, 0.25, 2, endpoint=False), + np.linspace(0.25, 0.5, 1, endpoint=False), + np.linspace(0.5, 0.75, 2, endpoint=False), + np.linspace(0.75, 1.5707, 3), + ) + ).flatten() + + exp_phi_grid = np.hstack( + ( + np.linspace(0.0, 0.25, 2, endpoint=False), + np.linspace(0.25, 0.5, 1, endpoint=False), + np.linspace(0.5, 1.5707, 2, endpoint=False), + np.linspace(1.5707, 3.1415, 4), + ) + ).flatten() assert isinstance(mesh, openmc.SphericalMesh) diff --git a/tests/regression_tests/white_plane/test.py b/tests/regression_tests/white_plane/test.py index 223f4e5e6a9..f430b0bd336 100644 --- a/tests/regression_tests/white_plane/test.py +++ b/tests/regression_tests/white_plane/test.py @@ -2,5 +2,5 @@ def test_white_plane(): - harness = TestHarness('statepoint.10.h5') + harness = TestHarness("statepoint.10.h5") harness.main() diff --git a/tests/test_matplotlib_import.py b/tests/test_matplotlib_import.py index d319976c453..a784bc85e69 100644 --- a/tests/test_matplotlib_import.py +++ b/tests/test_matplotlib_import.py @@ -1,6 +1,7 @@ import sys import openmc + def test_matplotlib_presence(): """Checks that matplotlib remains a deferred import""" - assert 'matplotlib' not in sys.modules + assert "matplotlib" not in sys.modules diff --git a/tests/testing_harness.py b/tests/testing_harness.py index 81527452b84..bff08761463 100644 --- a/tests/testing_harness.py +++ b/tests/testing_harness.py @@ -20,11 +20,11 @@ def colorize(diff): """Produce colored diff for test results""" for line in diff: - if line.startswith('+'): + if line.startswith("+"): yield Fore.RED + line + Fore.RESET - elif line.startswith('-'): + elif line.startswith("-"): yield Fore.GREEN + line + Fore.RESET - elif line.startswith('^'): + elif line.startswith("^"): yield Fore.BLUE + line + Fore.RESET else: yield line @@ -38,7 +38,7 @@ def __init__(self, statepoint_name): def main(self): """Accept commandline arguments and either run or update tests.""" - if config['update']: + if config["update"]: self.update_results() else: self.execute_test() @@ -66,51 +66,51 @@ def update_results(self): self._cleanup() def _run_openmc(self): - if config['mpi']: - mpi_args = [config['mpiexec'], '-n', config['mpi_np']] - openmc.run(openmc_exec=config['exe'], mpi_args=mpi_args, - event_based=config['event']) + if config["mpi"]: + mpi_args = [config["mpiexec"], "-n", config["mpi_np"]] + openmc.run( + openmc_exec=config["exe"], + mpi_args=mpi_args, + event_based=config["event"], + ) else: - openmc.run(openmc_exec=config['exe'], event_based=config['event']) + openmc.run(openmc_exec=config["exe"], event_based=config["event"]) def _test_output_created(self): """Make sure statepoint.* and tallies.out have been created.""" statepoint = glob.glob(self._sp_name) - assert len(statepoint) == 1, 'Either multiple or no statepoint files' \ - ' exist.' - assert statepoint[0].endswith('h5'), \ - 'Statepoint file is not a HDF5 file.' - if os.path.exists('tallies.xml'): - assert os.path.exists('tallies.out'), \ - 'Tally output file does not exist.' + assert len(statepoint) == 1, "Either multiple or no statepoint files" " exist." + assert statepoint[0].endswith("h5"), "Statepoint file is not a HDF5 file." + if os.path.exists("tallies.xml"): + assert os.path.exists("tallies.out"), "Tally output file does not exist." def _get_results(self, hash_output=False): """Digest info in the statepoint and return as a string.""" # Read the statepoint file. statepoint = glob.glob(self._sp_name)[0] with openmc.StatePoint(statepoint) as sp: - outstr = '' - if sp.run_mode == 'eigenvalue': + outstr = "" + if sp.run_mode == "eigenvalue": # Write out k-combined. - outstr += 'k-combined:\n' - form = '{0:12.6E} {1:12.6E}\n' + outstr += "k-combined:\n" + form = "{0:12.6E} {1:12.6E}\n" outstr += form.format(sp.keff.n, sp.keff.s) # Write out tally data. for i, tally_ind in enumerate(sp.tallies): tally = sp.tallies[tally_ind] - results = np.zeros((tally.sum.size * 2, )) + results = np.zeros((tally.sum.size * 2,)) results[0::2] = tally.sum.ravel() results[1::2] = tally.sum_sq.ravel() - results = ['{0:12.6E}'.format(x) for x in results] + results = ["{0:12.6E}".format(x) for x in results] - outstr += 'tally {}:\n'.format(i + 1) - outstr += '\n'.join(results) + '\n' + outstr += "tally {}:\n".format(i + 1) + outstr += "\n".join(results) + "\n" # Hash the results if necessary. if hash_output: sha512 = hashlib.sha512() - sha512.update(outstr.encode('utf-8')) + sha512.update(outstr.encode("utf-8")) outstr = sha512.hexdigest() return outstr @@ -121,31 +121,32 @@ def statepoint_name(self): def _write_results(self, results_string): """Write the results to an ASCII file.""" - with open('results_test.dat', 'w') as fh: + with open("results_test.dat", "w") as fh: fh.write(results_string) def _overwrite_results(self): """Overwrite the results_true with the results_test.""" - shutil.copyfile('results_test.dat', 'results_true.dat') + shutil.copyfile("results_test.dat", "results_true.dat") def _compare_results(self): """Make sure the current results agree with the reference.""" - compare = filecmp.cmp('results_test.dat', 'results_true.dat') + compare = filecmp.cmp("results_test.dat", "results_true.dat") if not compare: - expected = open('results_true.dat').readlines() - actual = open('results_test.dat').readlines() - diff = unified_diff(expected, actual, 'results_true.dat', - 'results_test.dat') - print('Result differences:') - print(''.join(colorize(diff))) - os.rename('results_test.dat', 'results_error.dat') - assert compare, 'Results do not agree' + expected = open("results_true.dat").readlines() + actual = open("results_test.dat").readlines() + diff = unified_diff( + expected, actual, "results_true.dat", "results_test.dat" + ) + print("Result differences:") + print("".join(colorize(diff))) + os.rename("results_test.dat", "results_error.dat") + assert compare, "Results do not agree" def _cleanup(self): """Delete statepoints, tally, and test files.""" - output = glob.glob('statepoint.*.h5') - output += ['tallies.out', 'results_test.dat', 'summary.h5'] - output += glob.glob('volume_*.h5') + output = glob.glob("statepoint.*.h5") + output += ["tallies.out", "results_test.dat", "summary.h5"] + output += glob.glob("volume_*.h5") for f in output: if os.path.exists(f): os.remove(f) @@ -168,23 +169,22 @@ def __init__(self, statepoint_name, cmfd_run): def _create_cmfd_result_str(self, cmfd_run): """Create CMFD result string from variables of CMFDRun instance""" - outstr = 'cmfd indices\n' - outstr += '\n'.join(['{:.6E}'.format(x) for x in cmfd_run.indices]) - outstr += '\nk cmfd\n' - outstr += '\n'.join(['{:.6E}'.format(x) for x in cmfd_run.k_cmfd]) - outstr += '\ncmfd entropy\n' - outstr += '\n'.join(['{:.6E}'.format(x) for x in cmfd_run.entropy]) - outstr += '\ncmfd balance\n' - outstr += '\n'.join(['{:.5E}'.format(x) for x in cmfd_run.balance]) - outstr += '\ncmfd dominance ratio\n' - outstr += '\n'.join(['{:.3E}'.format(x) for x in cmfd_run.dom]) - outstr += '\ncmfd openmc source comparison\n' - outstr += '\n'.join(['{:.6E}'.format(x) for x in cmfd_run.src_cmp]) - outstr += '\ncmfd source\n' - cmfdsrc = np.reshape(cmfd_run.cmfd_src, np.prod(cmfd_run.indices), - order='F') - outstr += '\n'.join(['{:.6E}'.format(x) for x in cmfdsrc]) - outstr += '\n' + outstr = "cmfd indices\n" + outstr += "\n".join(["{:.6E}".format(x) for x in cmfd_run.indices]) + outstr += "\nk cmfd\n" + outstr += "\n".join(["{:.6E}".format(x) for x in cmfd_run.k_cmfd]) + outstr += "\ncmfd entropy\n" + outstr += "\n".join(["{:.6E}".format(x) for x in cmfd_run.entropy]) + outstr += "\ncmfd balance\n" + outstr += "\n".join(["{:.5E}".format(x) for x in cmfd_run.balance]) + outstr += "\ncmfd dominance ratio\n" + outstr += "\n".join(["{:.3E}".format(x) for x in cmfd_run.dom]) + outstr += "\ncmfd openmc source comparison\n" + outstr += "\n".join(["{:.6E}".format(x) for x in cmfd_run.src_cmp]) + outstr += "\ncmfd source\n" + cmfdsrc = np.reshape(cmfd_run.cmfd_src, np.prod(cmfd_run.indices), order="F") + outstr += "\n".join(["{:.6E}".format(x) for x in cmfdsrc]) + outstr += "\n" self._cmfdrun_results = outstr def execute_test(self): @@ -218,8 +218,14 @@ def update_results(self): def _cleanup(self): """Delete output files for numpy matrices and flux vectors.""" super()._cleanup() - output = ['loss.npz', 'loss.dat', 'prod.npz', 'prod.dat', - 'fluxvec.npy', 'fluxvec.dat'] + output = [ + "loss.npz", + "loss.dat", + "prod.npz", + "prod.dat", + "fluxvec.npy", + "fluxvec.dat", + ] for f in output: if os.path.exists(f): os.remove(f) @@ -230,24 +236,24 @@ class ParticleRestartTestHarness(TestHarness): def _run_openmc(self): # Set arguments - args = {'openmc_exec': config['exe']} - if config['mpi']: - args['mpi_args'] = [config['mpiexec'], '-n', config['mpi_np']] + args = {"openmc_exec": config["exe"]} + if config["mpi"]: + args["mpi_args"] = [config["mpiexec"], "-n", config["mpi_np"]] # Initial run openmc.run(**args) # Run particle restart - args.update({'restart_file': self._sp_name}) + args.update({"restart_file": self._sp_name}) openmc.run(**args) def _test_output_created(self): """Make sure the restart file has been created.""" particle = glob.glob(self._sp_name) - assert len(particle) == 1, 'Either multiple or no particle restart ' \ - 'files exist.' - assert particle[0].endswith('h5'), \ - 'Particle restart file is not a HDF5 file.' + assert len(particle) == 1, ( + "Either multiple or no particle restart " "files exist." + ) + assert particle[0].endswith("h5"), "Particle restart file is not a HDF5 file." def _get_results(self): """Digest info in the statepoint and return as a string.""" @@ -256,32 +262,30 @@ def _get_results(self): p = openmc.Particle(particle) # Write out the properties. - outstr = '' - outstr += 'current batch:\n' + outstr = "" + outstr += "current batch:\n" outstr += "{0:12.6E}\n".format(p.current_batch) - outstr += 'current generation:\n' + outstr += "current generation:\n" outstr += "{0:12.6E}\n".format(p.current_generation) - outstr += 'particle id:\n' + outstr += "particle id:\n" outstr += "{0:12.6E}\n".format(p.id) - outstr += 'run mode:\n' + outstr += "run mode:\n" outstr += "{0}\n".format(p.run_mode) - outstr += 'particle weight:\n' + outstr += "particle weight:\n" outstr += "{0:12.6E}\n".format(p.weight) - outstr += 'particle energy:\n' + outstr += "particle energy:\n" outstr += "{0:12.6E}\n".format(p.energy) - outstr += 'particle xyz:\n' - outstr += "{0:12.6E} {1:12.6E} {2:12.6E}\n".format(p.xyz[0], p.xyz[1], - p.xyz[2]) - outstr += 'particle uvw:\n' - outstr += "{0:12.6E} {1:12.6E} {2:12.6E}\n".format(p.uvw[0], p.uvw[1], - p.uvw[2]) + outstr += "particle xyz:\n" + outstr += "{0:12.6E} {1:12.6E} {2:12.6E}\n".format(p.xyz[0], p.xyz[1], p.xyz[2]) + outstr += "particle uvw:\n" + outstr += "{0:12.6E} {1:12.6E} {2:12.6E}\n".format(p.uvw[0], p.uvw[1], p.uvw[2]) return outstr def _cleanup(self): """Delete particle restart files.""" super()._cleanup() - output = glob.glob('particle*.h5') + output = glob.glob("particle*.h5") for f in output: os.remove(f) @@ -299,9 +303,9 @@ def __init__(self, statepoint_name, model=None, inputs_true=None): def main(self): """Accept commandline arguments and either run or update tests.""" - if config['build_inputs']: + if config["build_inputs"]: self._build_inputs() - elif config['update']: + elif config["update"]: self.update_results() else: self.execute_test() @@ -342,37 +346,42 @@ def _build_inputs(self): def _get_inputs(self): """Return a hash digest of the input XML files.""" - xmls = ['model.xml', 'plots.xml'] - return ''.join([open(fname).read() for fname in xmls - if os.path.exists(fname)]) + xmls = ["model.xml", "plots.xml"] + return "".join([open(fname).read() for fname in xmls if os.path.exists(fname)]) def _write_inputs(self, input_digest): """Write the digest of the input XMLs to an ASCII file.""" - with open('inputs_test.dat', 'w') as fh: + with open("inputs_test.dat", "w") as fh: fh.write(input_digest) def _overwrite_inputs(self): """Overwrite inputs_true.dat with inputs_test.dat""" - shutil.copyfile('inputs_test.dat', self.inputs_true) + shutil.copyfile("inputs_test.dat", self.inputs_true) def _compare_inputs(self): """Make sure the current inputs agree with the _true standard.""" - compare = filecmp.cmp('inputs_test.dat', self.inputs_true) + compare = filecmp.cmp("inputs_test.dat", self.inputs_true) if not compare: - expected = open(self.inputs_true, 'r').readlines() - actual = open('inputs_test.dat', 'r').readlines() - diff = unified_diff(expected, actual, self.inputs_true, - 'inputs_test.dat') - print('Input differences:') - print(''.join(colorize(diff))) - os.rename('inputs_test.dat', 'inputs_error.dat') - assert compare, 'Input files are broken.' + expected = open(self.inputs_true, "r").readlines() + actual = open("inputs_test.dat", "r").readlines() + diff = unified_diff(expected, actual, self.inputs_true, "inputs_test.dat") + print("Input differences:") + print("".join(colorize(diff))) + os.rename("inputs_test.dat", "inputs_error.dat") + assert compare, "Input files are broken." def _cleanup(self): """Delete XMLs, statepoints, tally, and test files.""" super()._cleanup() - output = ['materials.xml', 'geometry.xml', 'settings.xml', - 'tallies.xml', 'plots.xml', 'inputs_test.dat', 'model.xml'] + output = [ + "materials.xml", + "geometry.xml", + "settings.xml", + "tallies.xml", + "plots.xml", + "inputs_test.dat", + "model.xml", + ] for f in output: if os.path.exists(f): os.remove(f) @@ -390,6 +399,7 @@ class TolerantPyAPITestHarness(PyAPITestHarness): due to single precision usage (e.g., as in the random ray solver). """ + def _are_files_equal(self, actual_path, expected_path, tolerance): def isfloat(value): try: @@ -429,37 +439,40 @@ def compare_tokens(token1, token2): def _compare_results(self): """Make sure the current results agree with the reference.""" - compare = self._are_files_equal('results_test.dat', 'results_true.dat', 1e-6) + compare = self._are_files_equal("results_test.dat", "results_true.dat", 1e-6) if not compare: - expected = open('results_true.dat').readlines() - actual = open('results_test.dat').readlines() - diff = unified_diff(expected, actual, 'results_true.dat', - 'results_test.dat') - print('Result differences:') - print(''.join(colorize(diff))) - os.rename('results_test.dat', 'results_error.dat') - assert compare, 'Results do not agree' + expected = open("results_true.dat").readlines() + actual = open("results_test.dat").readlines() + diff = unified_diff( + expected, actual, "results_true.dat", "results_test.dat" + ) + print("Result differences:") + print("".join(colorize(diff))) + os.rename("results_test.dat", "results_error.dat") + assert compare, "Results do not agree" class PlotTestHarness(TestHarness): """Specialized TestHarness for running OpenMC plotting tests.""" + def __init__(self, plot_names, voxel_convert_checks=[]): super().__init__(None) self._plot_names = plot_names self._voxel_convert_checks = voxel_convert_checks def _run_openmc(self): - openmc.plot_geometry(openmc_exec=config['exe']) + openmc.plot_geometry(openmc_exec=config["exe"]) # Check that voxel h5 can be converted to vtk for voxel_h5_filename in self._voxel_convert_checks: - check_call(['../../../scripts/openmc-voxel-to-vtk'] + - glob.glob(voxel_h5_filename)) + check_call( + ["../../../scripts/openmc-voxel-to-vtk"] + glob.glob(voxel_h5_filename) + ) def _test_output_created(self): """Make sure *.png has been created.""" for fname in self._plot_names: - assert os.path.exists(fname), 'Plot output file does not exist.' + assert os.path.exists(fname), "Plot output file does not exist." def _cleanup(self): super()._cleanup() @@ -472,18 +485,18 @@ def _get_results(self): outstr = bytes() for fname in self._plot_names: - if fname.endswith('.png'): + if fname.endswith(".png"): # Add PNG output to results - with open(fname, 'rb') as fh: + with open(fname, "rb") as fh: outstr += fh.read() - elif fname.endswith('.h5'): + elif fname.endswith(".h5"): # Add voxel data to results - with h5py.File(fname, 'r') as fh: - outstr += fh.attrs['filetype'] - outstr += fh.attrs['num_voxels'].tobytes() - outstr += fh.attrs['lower_left'].tobytes() - outstr += fh.attrs['voxel_width'].tobytes() - outstr += fh['data'][()].tobytes() + with h5py.File(fname, "r") as fh: + outstr += fh.attrs["filetype"] + outstr += fh.attrs["num_voxels"].tobytes() + outstr += fh.attrs["lower_left"].tobytes() + outstr += fh.attrs["voxel_width"].tobytes() + outstr += fh["data"][()].tobytes() # Hash the information and return. sha512 = hashlib.sha512() diff --git a/tests/unit_tests/__init__.py b/tests/unit_tests/__init__.py index e97ab13e2a7..21bf530f040 100644 --- a/tests/unit_tests/__init__.py +++ b/tests/unit_tests/__init__.py @@ -5,8 +5,9 @@ # Check if NJOY is available -needs_njoy = pytest.mark.skipif(shutil.which('njoy') is None, - reason="NJOY not installed") +needs_njoy = pytest.mark.skipif( + shutil.which("njoy") is None, reason="NJOY not installed" +) def assert_unbounded(obj): diff --git a/tests/unit_tests/cell_instances/test_hex_multilattice.py b/tests/unit_tests/cell_instances/test_hex_multilattice.py index 3f503fe9b84..d67acfbc446 100644 --- a/tests/unit_tests/cell_instances/test_hex_multilattice.py +++ b/tests/unit_tests/cell_instances/test_hex_multilattice.py @@ -8,7 +8,7 @@ from tests import cdtemp -@pytest.fixture(scope='module', autouse=True) +@pytest.fixture(scope="module", autouse=True) def double_hex_lattice_model(): openmc.reset_auto_ids() radius = 0.9 @@ -21,19 +21,20 @@ def double_hex_lattice_model(): # materials nat_u = openmc.Material() - nat_u.set_density('g/cm3', 12.0) - nat_u.add_element('U', 1.0) + nat_u.set_density("g/cm3", 12.0) + nat_u.add_element("U", 1.0) graphite = openmc.Material() - graphite.set_density('g/cm3', 1.1995) - graphite.add_element('C', 1.0) + graphite.set_density("g/cm3", 1.1995) + graphite.add_element("C", 1.0) # zplanes to define lower and upper region - z_low = openmc.ZPlane(-10, boundary_type='vacuum') + z_low = openmc.ZPlane(-10, boundary_type="vacuum") z_mid = openmc.ZPlane(0) - z_high = openmc.ZPlane(10, boundary_type='vacuum') + z_high = openmc.ZPlane(10, boundary_type="vacuum") hex_prism = openmc.model.HexagonalPrism( - edge_length=hex_prism_edge, boundary_type='reflective') + edge_length=hex_prism_edge, boundary_type="reflective" + ) # geometry cyl = openmc.ZCylinder(r=radius) @@ -41,9 +42,9 @@ def double_hex_lattice_model(): # create a hexagonal lattice of compacts hex_lattice = openmc.HexLattice() - hex_lattice.orientation = 'y' + hex_lattice.orientation = "y" hex_lattice.pitch = (pin_lattice_pitch,) - hex_lattice.center = (0., 0.) + hex_lattice.center = (0.0, 0.0) center = [univ] ring = [univ, univ, univ, univ, univ, univ] hex_lattice.universes = [ring, center] @@ -57,13 +58,13 @@ def double_hex_lattice_model(): tally = openmc.Tally(tally_id=1) filter = openmc.DistribcellFilter(cell) tally.filters = [filter] - tally.scores = ['flux'] + tally.scores = ["flux"] model.tallies = [tally] # settings # source definition. fission source given bounding box of graphite active region - system_LL = (-pin_lattice_pitch*sqrt(3)/2, -pin_lattice_pitch, -5) - system_UR = (pin_lattice_pitch*sqrt(3)/2, pin_lattice_pitch, 5) + system_LL = (-pin_lattice_pitch * sqrt(3) / 2, -pin_lattice_pitch, -5) + system_UR = (pin_lattice_pitch * sqrt(3) / 2, pin_lattice_pitch, 5) source_dist = openmc.stats.Box(system_LL, system_UR) model.settings.source = openmc.IndependentSource(space=source_dist) model.settings.particles = 100 diff --git a/tests/unit_tests/cell_instances/test_rect_multilattice.py b/tests/unit_tests/cell_instances/test_rect_multilattice.py index aaecb3bdac5..06c7b929e0b 100644 --- a/tests/unit_tests/cell_instances/test_rect_multilattice.py +++ b/tests/unit_tests/cell_instances/test_rect_multilattice.py @@ -7,15 +7,15 @@ from tests import cdtemp -@pytest.fixture(scope='module', autouse=True) +@pytest.fixture(scope="module", autouse=True) def double_rect_lattice_model(): openmc.reset_auto_ids() model = openmc.Model() # Create a single material m = openmc.Material() - m.add_nuclide('U235', 1.0) - m.set_density('g/cm3', 10.0) + m.add_nuclide("U235", 1.0) + m.set_density("g/cm3", 10.0) model.materials.append(m) # Create a universe with a single infinite cell @@ -30,20 +30,20 @@ def double_rect_lattice_model(): # Create two cells each filled with the same lattice, one from x=0..2 and # y=0..2 and the other from x=2..4 and y=0..2 - x0 = openmc.XPlane(0.0, boundary_type='vacuum') + x0 = openmc.XPlane(0.0, boundary_type="vacuum") x2 = openmc.XPlane(2.0) - x4 = openmc.XPlane(4.0, boundary_type='vacuum') - y0 = openmc.YPlane(0.0, boundary_type='vacuum') - y2 = openmc.YPlane(2.0, boundary_type='vacuum') + x4 = openmc.XPlane(4.0, boundary_type="vacuum") + y0 = openmc.YPlane(0.0, boundary_type="vacuum") + y2 = openmc.YPlane(2.0, boundary_type="vacuum") cell_with_lattice1 = openmc.Cell(fill=lattice, region=+x0 & -x2 & +y0 & -y2) cell_with_lattice2 = openmc.Cell(fill=lattice, region=+x2 & -x4 & +y0 & -y2) - cell_with_lattice2.translation = (2., 0., 0.) + cell_with_lattice2.translation = (2.0, 0.0, 0.0) model.geometry = openmc.Geometry([cell_with_lattice1, cell_with_lattice2]) tally = openmc.Tally(tally_id=1) dcell_filter = openmc.DistribcellFilter(c) tally.filters = [dcell_filter] - tally.scores = ['flux'] + tally.scores = ["flux"] model.tallies = [tally] # Add box source that covers the model space well @@ -65,6 +65,7 @@ def double_rect_lattice_model(): yield openmc.lib.finalize() + # This shows the expected cell instance numbers for each lattice position: # ā”Œā”€ā”¬ā”€ā”¬ā”€ā”¬ā”€ā” # │2│3│6│7│ @@ -83,7 +84,9 @@ def double_rect_lattice_model(): ] -@pytest.mark.parametrize("r,expected_cell_instance", rect_expected_results, ids=lambda p : f'{p}') +@pytest.mark.parametrize( + "r,expected_cell_instance", rect_expected_results, ids=lambda p: f"{p}" +) def test_cell_instance_rect_multilattice(r, expected_cell_instance): _, cell_instance = openmc.lib.find_cell(r) assert cell_instance == expected_cell_instance diff --git a/tests/unit_tests/conftest.py b/tests/unit_tests/conftest.py index 6041d898201..1da60cd69ea 100644 --- a/tests/unit_tests/conftest.py +++ b/tests/unit_tests/conftest.py @@ -4,50 +4,51 @@ from tests.regression_tests import config -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def mpi_intracomm(): - if config['mpi']: + if config["mpi"]: from mpi4py import MPI + return MPI.COMM_WORLD else: return None -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def uo2(): - m = openmc.Material(material_id=100, name='UO2') - m.add_nuclide('U235', 1.0) - m.add_nuclide('O16', 2.0) - m.set_density('g/cm3', 10.0) + m = openmc.Material(material_id=100, name="UO2") + m.add_nuclide("U235", 1.0) + m.add_nuclide("O16", 2.0) + m.set_density("g/cm3", 10.0) m.depletable = True return m -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def water(): - m = openmc.Material(name='light water') - m.add_nuclide('H1', 2.0) - m.add_nuclide('O16', 1.0) - m.set_density('g/cm3', 1.0) - m.add_s_alpha_beta('c_H_in_H2O') + m = openmc.Material(name="light water") + m.add_nuclide("H1", 2.0) + m.add_nuclide("O16", 1.0) + m.set_density("g/cm3", 1.0) + m.add_s_alpha_beta("c_H_in_H2O") return m -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def sphere_model(): model = openmc.model.Model() m = openmc.Material() - m.add_nuclide('U235', 1.0) - m.set_density('g/cm3', 1.0) + m.add_nuclide("U235", 1.0) + m.set_density("g/cm3", 1.0) model.materials.append(m) - sph = openmc.Sphere(boundary_type='vacuum') + sph = openmc.Sphere(boundary_type="vacuum") c = openmc.Cell(fill=m, region=-sph) model.geometry.root_universe = openmc.Universe(cells=[c]) model.settings.particles = 100 model.settings.batches = 10 - model.settings.run_mode = 'fixed source' + model.settings.run_mode = "fixed source" model.settings.source = openmc.IndependentSource(space=openmc.stats.Point()) return model @@ -62,15 +63,19 @@ def cell_with_lattice(): outside_cyl = openmc.Cell(fill=m_outside, region=+cyl) univ = openmc.Universe(cells=[inside_cyl, outside_cyl]) - lattice = openmc.RectLattice(name='My Lattice') + lattice = openmc.RectLattice(name="My Lattice") lattice.lower_left = (-4.0, -4.0) lattice.pitch = (4.0, 4.0) lattice.universes = [[univ, univ], [univ, univ]] main_cell = openmc.Cell(fill=lattice) - return ([inside_cyl, outside_cyl, main_cell], - [m_inside[0], m_inside[1], m_inside[3], m_outside], - univ, lattice) + return ( + [inside_cyl, outside_cyl, main_cell], + [m_inside[0], m_inside[1], m_inside[3], m_outside], + univ, + lattice, + ) + @pytest.fixture def mixed_lattice_model(uo2, water): @@ -86,16 +91,16 @@ def mixed_lattice_model(uo2, water): hex_lattice = openmc.HexLattice() hex_lattice.center = (0.0, 0.0) hex_lattice.pitch = (1.2, 10.0) - outer_ring = [pin]*6 + outer_ring = [pin] * 6 inner_ring = [empty_univ] axial_level = [outer_ring, inner_ring] - hex_lattice.universes = [axial_level]*3 + hex_lattice.universes = [axial_level] * 3 hex_lattice.outer = empty_univ cell_hex = openmc.Cell(fill=hex_lattice) u = openmc.Universe(cells=[cell_hex]) rotated_cell_hex = openmc.Cell(fill=u) - rotated_cell_hex.rotation = (0., 0., 30.) + rotated_cell_hex.rotation = (0.0, 0.0, 30.0) ur = openmc.Universe(cells=[rotated_cell_hex]) d = 6.0 @@ -103,18 +108,14 @@ def mixed_lattice_model(uo2, water): rect_lattice.lower_left = (-d, -d) rect_lattice.pitch = (d, d) rect_lattice.outer = empty_univ - rect_lattice.universes = [ - [ur, empty_univ], - [empty_univ, u] - ] + rect_lattice.universes = [[ur, empty_univ], [empty_univ, u]] - xmin = openmc.XPlane(-d, boundary_type='periodic') - xmax = openmc.XPlane(d, boundary_type='periodic') + xmin = openmc.XPlane(-d, boundary_type="periodic") + xmax = openmc.XPlane(d, boundary_type="periodic") xmin.periodic_surface = xmax - ymin = openmc.YPlane(-d, boundary_type='periodic') - ymax = openmc.YPlane(d, boundary_type='periodic') - main_cell = openmc.Cell(fill=rect_lattice, - region=+xmin & -xmax & +ymin & -ymax) + ymin = openmc.YPlane(-d, boundary_type="periodic") + ymax = openmc.YPlane(d, boundary_type="periodic") + main_cell = openmc.Cell(fill=rect_lattice, region=+xmin & -xmax & +ymin & -ymax) # Create geometry and use unique material in each fuel cell geometry = openmc.Geometry([main_cell]) diff --git a/tests/unit_tests/dagmc/test.py b/tests/unit_tests/dagmc/test.py index e84b5317ede..66c1c739137 100644 --- a/tests/unit_tests/dagmc/test.py +++ b/tests/unit_tests/dagmc/test.py @@ -10,8 +10,8 @@ from tests import cdtemp pytestmark = pytest.mark.skipif( - not openmc.lib._dagmc_enabled(), - reason="DAGMC CAD geometry is not enabled.") + not openmc.lib._dagmc_enabled(), reason="DAGMC CAD geometry is not enabled." +) @pytest.fixture(scope="module", autouse=True) @@ -23,15 +23,14 @@ def dagmc_model(request): model.settings.batches = 5 model.settings.inactive = 0 model.settings.particles = 100 - model.settings.temperature = {'tolerance': 50.0} + model.settings.temperature = {"tolerance": 50.0} model.settings.verbosity = 1 - source_box = openmc.stats.Box([ -4, -4, -4 ], - [ 4, 4, 4 ]) + source_box = openmc.stats.Box([-4, -4, -4], [4, 4, 4]) source = openmc.IndependentSource(space=source_box) model.settings.source = source # geometry - dagmc_file = Path(request.fspath).parent / 'dagmc.h5m' + dagmc_file = Path(request.fspath).parent / "dagmc.h5m" dagmc_universe = openmc.DAGMCUniverse(dagmc_file) model.geometry = openmc.Geometry(dagmc_universe) @@ -45,22 +44,22 @@ def dagmc_model(request): # tally tally = openmc.Tally() - tally.scores = ['total'] + tally.scores = ["total"] tally.filters = [openmc.CellFilter(1)] model.tallies = [tally] # materials u235 = openmc.Material(name="no-void fuel") - u235.add_nuclide('U235', 1.0, 'ao') - u235.set_density('g/cc', 11) + u235.add_nuclide("U235", 1.0, "ao") + u235.set_density("g/cc", 11) u235.id = 40 u235.temperature = 320 water = openmc.Material(name="water") - water.add_nuclide('H1', 2.0, 'ao') - water.add_nuclide('O16', 1.0, 'ao') - water.set_density('g/cc', 1.0) - water.add_s_alpha_beta('c_H_in_H2O') + water.add_nuclide("H1", 2.0, "ao") + water.add_nuclide("O16", 1.0, "ao") + water.set_density("g/cc", 1.0) + water.add_s_alpha_beta("c_H_in_H2O") water.id = 41 mats = openmc.Materials([u235, water]) @@ -78,9 +77,14 @@ def dagmc_model(request): openmc.lib.finalize() -@pytest.mark.parametrize("cell_id,exp_temp", ((1, 320.0), # assigned by material - (2, 300.0), # assigned in dagmc file - (3, 293.6))) # assigned by default +@pytest.mark.parametrize( + "cell_id,exp_temp", + ( + (1, 320.0), # assigned by material + (2, 300.0), # assigned in dagmc file + (3, 293.6), + ), +) # assigned by default def test_dagmc_temperatures(cell_id, exp_temp): cell = openmc.lib.cells[cell_id] assert np.isclose(cell.get_temperature(), exp_temp) diff --git a/tests/unit_tests/dagmc/test_bounds.py b/tests/unit_tests/dagmc/test_bounds.py index 35e1dd3403f..7f41057e343 100644 --- a/tests/unit_tests/dagmc/test_bounds.py +++ b/tests/unit_tests/dagmc/test_bounds.py @@ -23,17 +23,17 @@ def test_bounding_region(request): assert isinstance(region, openmc.Region) assert len(region) == 6 assert region[0].surface.type == "x-plane" - assert region[0].surface.x0 == -25. + assert region[0].surface.x0 == -25.0 assert region[1].surface.type == "x-plane" - assert region[1].surface.x0 == 25. + assert region[1].surface.x0 == 25.0 assert region[2].surface.type == "y-plane" - assert region[2].surface.y0 == -25. + assert region[2].surface.y0 == -25.0 assert region[3].surface.type == "y-plane" - assert region[3].surface.y0 == 25. + assert region[3].surface.y0 == 25.0 assert region[4].surface.type == "z-plane" - assert region[4].surface.z0 == -25. + assert region[4].surface.z0 == -25.0 assert region[5].surface.type == "z-plane" - assert region[5].surface.z0 == 25. + assert region[5].surface.z0 == 25.0 assert region[0].surface.boundary_type == "vacuum" assert region[1].surface.boundary_type == "vacuum" assert region[2].surface.boundary_type == "vacuum" @@ -41,12 +41,12 @@ def test_bounding_region(request): assert region[4].surface.boundary_type == "vacuum" assert region[5].surface.boundary_type == "vacuum" region = u.bounding_region(padding_distance=5) - assert region[0].surface.x0 == -30. - assert region[1].surface.x0 == 30. - assert region[2].surface.y0 == -30. - assert region[3].surface.y0 == 30. - assert region[4].surface.z0 == -30. - assert region[5].surface.z0 == 30. + assert region[0].surface.x0 == -30.0 + assert region[1].surface.x0 == 30.0 + assert region[2].surface.y0 == -30.0 + assert region[3].surface.y0 == 30.0 + assert region[4].surface.z0 == -30.0 + assert region[5].surface.z0 == 30.0 region = u.bounding_region(bounded_type="sphere", boundary_type="reflective") assert isinstance(region, openmc.Region) @@ -94,4 +94,4 @@ def test_material_names(request): u = openmc.DAGMCUniverse(Path(request.fspath).parent / "dagmc.h5m") - assert u.material_names == ['41', 'Graveyard', 'no-void fuel'] + assert u.material_names == ["41", "Graveyard", "no-void fuel"] diff --git a/tests/unit_tests/dagmc/test_lost_particles.py b/tests/unit_tests/dagmc/test_lost_particles.py index 502bd795e85..99bef34018d 100644 --- a/tests/unit_tests/dagmc/test_lost_particles.py +++ b/tests/unit_tests/dagmc/test_lost_particles.py @@ -7,8 +7,8 @@ import pytest pytestmark = pytest.mark.skipif( - not openmc.lib._dagmc_enabled(), - reason="DAGMC CAD geometry is not enabled.") + not openmc.lib._dagmc_enabled(), reason="DAGMC CAD geometry is not enabled." +) @pytest.fixture @@ -17,17 +17,17 @@ def broken_dagmc_model(request): model = openmc.Model() ### MATERIALS ### - fuel = openmc.Material(name='no-void fuel') - fuel.set_density('g/cc', 10.29769) - fuel.add_nuclide('U233', 1.0) + fuel = openmc.Material(name="no-void fuel") + fuel.set_density("g/cc", 10.29769) + fuel.add_nuclide("U233", 1.0) - cladding = openmc.Material(name='clad') - cladding.set_density('g/cc', 6.55) - cladding.add_nuclide('Zr90', 1.0) + cladding = openmc.Material(name="clad") + cladding.set_density("g/cc", 6.55) + cladding.add_nuclide("Zr90", 1.0) - h1 = openmc.Material(name='water') - h1.set_density('g/cc', 0.75) - h1.add_nuclide('H1', 1.0) + h1 = openmc.Material(name="water") + h1.set_density("g/cc", 0.75) + h1.add_nuclide("H1", 1.0) model.materials = openmc.Materials([fuel, cladding, h1]) @@ -46,7 +46,14 @@ def broken_dagmc_model(request): # clip the DAGMC geometry at +/- 10 cm w/ CSG planes rpp = openmc.model.RectangularParallelepiped( - -pitch[0], pitch[0], -pitch[1], pitch[1], -10.0, 10.0, boundary_type='reflective') + -pitch[0], + pitch[0], + -pitch[1], + pitch[1], + -10.0, + 10.0, + boundary_type="reflective", + ) bounding_cell = openmc.Cell(fill=lattice, region=-rpp) model.geometry = openmc.Geometry(root=[bounding_cell]) @@ -55,7 +62,7 @@ def broken_dagmc_model(request): model.settings.particles = 100 model.settings.batches = 10 model.settings.inactive = 2 - model.settings.output = {'summary': False} + model.settings.output = {"summary": False} model.export_to_xml() @@ -66,7 +73,9 @@ def test_lost_particles(run_in_tmpdir, broken_dagmc_model): broken_dagmc_model.export_to_xml() # ensure that particles will be lost when cell intersections can't be found # due to the removed triangles in this model - with pytest.raises(RuntimeError, match='Maximum number of lost particles has been reached.'): + with pytest.raises( + RuntimeError, match="Maximum number of lost particles has been reached." + ): openmc.run() # run this again, but with the dagmc universe as the root unvierse @@ -76,6 +85,7 @@ def test_lost_particles(run_in_tmpdir, broken_dagmc_model): break broken_dagmc_model.export_to_xml() - with pytest.raises(RuntimeError, match='Maximum number of lost particles has been reached.'): + with pytest.raises( + RuntimeError, match="Maximum number of lost particles has been reached." + ): openmc.run() - diff --git a/tests/unit_tests/mesh_to_vtk/test.py b/tests/unit_tests/mesh_to_vtk/test.py index 3ee6c0298ce..aac12bfb084 100644 --- a/tests/unit_tests/mesh_to_vtk/test.py +++ b/tests/unit_tests/mesh_to_vtk/test.py @@ -8,18 +8,21 @@ from tests.regression_tests import config -pytest.importorskip('vtk') +pytest.importorskip("vtk") + def full_path(f): return Path(__file__).parent.absolute() / f + def diff_file(file1, file2): with open(file1) as fh: f1_text = fh.readlines() with open(file2) as fh: f2_text = fh.readlines() diff_lines = difflib.unified_diff(f1_text, f2_text) - return ''.join(diff_lines) + return "".join(diff_lines) + # test meshes reg_mesh = openmc.RegularMesh() @@ -29,7 +32,7 @@ def diff_file(file1, file2): rect_mesh = openmc.RectilinearMesh() rect_mesh.x_grid = np.linspace(0, 10, 5) -rect_mesh.y_grid = np.geomspace(5., 20., 10) +rect_mesh.y_grid = np.geomspace(5.0, 20.0, 10) rect_mesh.z_grid = np.linspace(1, 100, 20) cyl_mesh = openmc.CylindricalMesh( @@ -43,7 +46,7 @@ def diff_file(file1, file2): origin=(10, 10, -10), r_grid=np.linspace(0, 5, 3), theta_grid=np.linspace(0, 0.5 * np.pi, 4), - phi_grid=np.linspace(0, 2*np.pi, 8), + phi_grid=np.linspace(0, 2 * np.pi, 8), ) @@ -51,26 +54,28 @@ def mesh_data(mesh_dims): data = 100 * np.arange(np.prod(mesh_dims), dtype=float) return data.reshape(*mesh_dims) -test_data = ((reg_mesh, False, 'regular'), - (rect_mesh, False, 'rectilinear'), - (cyl_mesh, False, 'cylindrical-linear'), - (cyl_mesh, True, 'cylindrical-curvilinear'), - (sphere_mesh, False, 'spherical-linear'), - (sphere_mesh, True, 'spherical-curvilinear')) -@pytest.mark.parametrize('mesh_params', - test_data, - ids=lambda params: params[2]) +test_data = ( + (reg_mesh, False, "regular"), + (rect_mesh, False, "rectilinear"), + (cyl_mesh, False, "cylindrical-linear"), + (cyl_mesh, True, "cylindrical-curvilinear"), + (sphere_mesh, False, "spherical-linear"), + (sphere_mesh, True, "spherical-curvilinear"), +) + + +@pytest.mark.parametrize("mesh_params", test_data, ids=lambda params: params[2]) def test_mesh_write_vtk(mesh_params, run_in_tmpdir): mesh, curvilinear, filename = mesh_params test_data = full_path(filename + ".vtk") kwargs = {} if curvilinear: - kwargs['curvilinear'] = curvilinear + kwargs["curvilinear"] = curvilinear # set output filename based on test configuration - filename = test_data if config['update'] else filename + "-actual.vtk" + filename = test_data if config["update"] else filename + "-actual.vtk" # write the mesh file and compare to the expected version mesh.write_data_to_vtk(filename, **kwargs) @@ -81,14 +86,15 @@ def test_mesh_write_vtk(mesh_params, run_in_tmpdir): diff = diff_file(test_data, filename) raise AssertionError(diff) from e + # check data writing def test_mesh_write_vtk_data(run_in_tmpdir): - data = {'ascending_data': mesh_data(cyl_mesh.dimension)} - filename_expected = full_path('cyl-data.vtk') - filename_actual = full_path('cyl-data-actual.vtk') + data = {"ascending_data": mesh_data(cyl_mesh.dimension)} + filename_expected = full_path("cyl-data.vtk") + filename_actual = full_path("cyl-data-actual.vtk") # update the test file if requested - filename = filename_expected if config['update'] else filename_actual + filename = filename_expected if config["update"] else filename_actual cyl_mesh.write_data_to_vtk(filename, datasets=data, volume_normalization=False) try: @@ -96,6 +102,3 @@ def test_mesh_write_vtk_data(run_in_tmpdir): except AssertionError as e: diff = diff_file(filename_expected, filename) raise AssertionError(diff) from e - - - diff --git a/tests/unit_tests/mesh_to_vtk/test_vtk_dims.py b/tests/unit_tests/mesh_to_vtk/test_vtk_dims.py index 0b6acf5aabf..7f2f1b1d1e6 100644 --- a/tests/unit_tests/mesh_to_vtk/test_vtk_dims.py +++ b/tests/unit_tests/mesh_to_vtk/test_vtk_dims.py @@ -9,12 +9,13 @@ import openmc + @pytest.fixture def model(): openmc.reset_auto_ids() - surf1 = openmc.Sphere(r=10, boundary_type='vacuum') - surf2 = openmc.XPlane(x0=-0.001, boundary_type='vacuum') + surf1 = openmc.Sphere(r=10, boundary_type="vacuum") + surf2 = openmc.XPlane(x0=-0.001, boundary_type="vacuum") cell = openmc.Cell(region=-surf1 & -surf2) @@ -23,7 +24,7 @@ def model(): settings = openmc.Settings() settings.batches = 2 settings.particles = 100 - settings.run_mode = 'fixed source' + settings.run_mode = "fixed source" source = openmc.IndependentSource() source.angle = openmc.stats.Isotropic() @@ -45,40 +46,40 @@ def model(): rectilinear_mesh = openmc.RectilinearMesh() rectilinear_mesh.x_grid = np.linspace(-10, 10, 6) rectilinear_mesh.y_grid = np.logspace(0, 1, 7) -rectilinear_mesh.y_grid = \ - np.concatenate((-rectilinear_mesh.y_grid[::-1], rectilinear_mesh.y_grid)) +rectilinear_mesh.y_grid = np.concatenate( + (-rectilinear_mesh.y_grid[::-1], rectilinear_mesh.y_grid) +) rectilinear_mesh.z_grid = np.linspace(-10, 10, 11) cylinder_mesh = openmc.CylindricalMesh( - r_grid=np.linspace(0, 10, 23), - z_grid=np.linspace(0, 1, 15) + r_grid=np.linspace(0, 10, 23), z_grid=np.linspace(0, 1, 15) ) cylinder_mesh.phi_grid = np.linspace(0, np.pi, 21) spherical_mesh = openmc.SphericalMesh( r_grid=np.linspace(1, 10, 30), - phi_grid=np.linspace(0, 0.8*np.pi, 25), + phi_grid=np.linspace(0, 0.8 * np.pi, 25), theta_grid=np.linspace(0, np.pi / 2, 15), ) MESHES = [cylinder_mesh, regular_mesh, rectilinear_mesh, spherical_mesh] -x_plane = openmc.XPlane(x0=-0.001, boundary_type='vacuum') -y_plane = openmc.YPlane(y0=-0.001, boundary_type='vacuum') -z_plane = openmc.ZPlane(z0=-0.001, boundary_type='vacuum') +x_plane = openmc.XPlane(x0=-0.001, boundary_type="vacuum") +y_plane = openmc.YPlane(y0=-0.001, boundary_type="vacuum") +z_plane = openmc.ZPlane(z0=-0.001, boundary_type="vacuum") SURFS = [x_plane, y_plane, z_plane] def ids(mesh): if isinstance(mesh, openmc.CylindricalMesh): - return 'cylindrical_mesh' + return "cylindrical_mesh" elif isinstance(mesh, openmc.RegularMesh): - return 'regular_mesh' + return "regular_mesh" elif isinstance(mesh, openmc.RectilinearMesh): - return 'rectilinear_mesh' + return "rectilinear_mesh" elif isinstance(mesh, openmc.SphericalMesh): - return 'spherical_mesh' + return "spherical_mesh" @pytest.mark.parametrize("mesh", MESHES, ids=ids) @@ -138,12 +139,13 @@ def test_write_data_to_vtk_size_mismatch(mesh): # by regex. These are needed to make the test string match the error message # string when using the match argument as that uses regular expression expected_error_msg = ( - fr"The size of the dataset 'label' \({len(data)}\) should be equal to " - fr"the number of mesh cells \({mesh.num_mesh_cells}\)" + rf"The size of the dataset 'label' \({len(data)}\) should be equal to " + rf"the number of mesh cells \({mesh.num_mesh_cells}\)" ) with pytest.raises(ValueError, match=expected_error_msg): mesh.write_data_to_vtk(filename="out.vtk", datasets={"label": data}) + def test_write_data_to_vtk_round_trip(run_in_tmpdir): cmesh = openmc.CylindricalMesh( r_grid=(0.0, 1.0, 2.0), @@ -166,9 +168,7 @@ def test_write_data_to_vtk_round_trip(run_in_tmpdir): filename = "mesh.vtk" data = np.array([1.0] * 12) # there are 12 voxels in each mesh mesh.write_data_to_vtk( - filename=filename, - datasets={"normalized": data}, - volume_normalization=True + filename=filename, datasets={"normalized": data}, volume_normalization=True ) reader = vtk.vtkStructuredGridReader() @@ -203,22 +203,23 @@ def test_write_data_to_vtk_round_trip(run_in_tmpdir): # checks that the vtk cell values are equal to the data assert np.array_equal(vtk_values, data) + def mesh_surf_id(param): if isinstance(param, openmc.MeshBase): return ids(param) elif isinstance(param, openmc.XPlane): - return 'XPlane' + return "XPlane" elif isinstance(param, openmc.YPlane): - return 'YPlane' + return "YPlane" elif isinstance(param, openmc.ZPlane): - return 'ZPlane' + return "ZPlane" @pytest.mark.parametrize("mesh,surface", product(MESHES, SURFS), ids=mesh_surf_id) def test_vtk_write_ordering(run_in_tmpdir, model, mesh, surface): tally = openmc.Tally() - tally.scores = ['flux'] + tally.scores = ["flux"] # use the mesh on the specified tally mesh_filter = openmc.MeshFilter(mesh) tally.filters = [mesh_filter] @@ -232,10 +233,10 @@ def test_vtk_write_ordering(run_in_tmpdir, model, mesh, surface): mean = sp.tallies[tally.id].mean # write the data to a VTK file - vtk_filename = 'test.vtk' - mesh.write_data_to_vtk(vtk_filename, datasets={'mean': mean}) + vtk_filename = "test.vtk" + mesh.write_data_to_vtk(vtk_filename, datasets={"mean": mean}) - # read file + # read file reader = vtk.vtkStructuredGridReader() reader.SetFileName(str(vtk_filename)) reader.Update() @@ -270,7 +271,7 @@ def in_geom(cell): cell = vtk_grid.GetCell(*ijk) if not in_geom(cell): cell.GetCentroid(centroid) - err_msg = f'IJK: {ijk} should be zero but is not. Centroid: {centroid}' + err_msg = f"IJK: {ijk} should be zero but is not. Centroid: {centroid}" assert mean[ijk] == 0.0, err_msg # need to get flat index with axes reversed due to ordering passed into the VTK file @@ -281,11 +282,11 @@ def in_geom(cell): def test_sphere_mesh_coordinates(run_in_tmpdir): mesh = openmc.SphericalMesh( r_grid=np.linspace(0.1, 10, 30), - phi_grid=np.linspace(0, 1.5*np.pi, 25), + phi_grid=np.linspace(0, 1.5 * np.pi, 25), theta_grid=np.linspace(0, np.pi / 2, 15), ) # write the data to a VTK file (no data) - vtk_filename = 'test.vtk' + vtk_filename = "test.vtk" mesh.write_data_to_vtk(vtk_filename, {}) # read file @@ -314,7 +315,7 @@ def test_sphere_mesh_coordinates(run_in_tmpdir): # if the coordinate conversion is happening correctly, # every one of the cell centroids should be in the CSG region - assert centroid in region, \ - f'Cell centroid {centroid} not in equivalent ' \ - f'CSG region for spherical mesh {mesh}' - + assert centroid in region, ( + f"Cell centroid {centroid} not in equivalent " + f"CSG region for spherical mesh {mesh}" + ) diff --git a/tests/unit_tests/test_cell.py b/tests/unit_tests/test_cell.py index 95c8249bb33..694de1c5dd3 100644 --- a/tests/unit_tests/test_cell.py +++ b/tests/unit_tests/test_cell.py @@ -17,7 +17,7 @@ def test_contains(): # Cell with no region c = openmc.Cell() - assert (10.0, -4., 2.0) in c + assert (10.0, -4.0, 2.0) in c def test_repr(cell_with_lattice): @@ -43,8 +43,8 @@ def test_bounding_box(): zcyl = openmc.ZCylinder() c = openmc.Cell(region=-zcyl) ll, ur = c.bounding_box - assert ll == pytest.approx((-1., -1., -np.inf)) - assert ur == pytest.approx((1., 1., np.inf)) + assert ll == pytest.approx((-1.0, -1.0, -np.inf)) + assert ur == pytest.approx((1.0, 1.0, np.inf)) # Cell with no region specified c = openmc.Cell() @@ -73,9 +73,9 @@ def test_clone(): assert c4.region == c.region # Add optional properties to the original cell to ensure they're cloned successfully - c.temperature = 650. - c.translation = (1., 2., 3.) - c.rotation = (4., 5., 6.) + c.temperature = 650.0 + c.translation = (1.0, 2.0, 3.0) + c.rotation = (4.0, 5.0, 6.0) c.volume = 100 c5 = c.clone(clone_materials=False, clone_regions=False) @@ -90,8 +90,8 @@ def test_clone(): # Mutate the original to ensure the changes are not seen in the clones c.fill = openmc.Material() c.region = +openmc.ZCylinder() - c.translation = (-1., -2., -3.) - c.rotation = (-4., -5., -6.) + c.translation = (-1.0, -2.0, -3.0) + c.rotation = (-4.0, -5.0, -6.0) c.temperature = 1 c.volume = 1 assert c5.fill != c.fill @@ -115,7 +115,7 @@ def test_temperature(cell_with_lattice): assert c1.temperature == 400.0 assert c2.temperature == 400.0 with pytest.raises(ValueError): - c.temperature = -100. + c.temperature = -100.0 c.temperature = None assert c1.temperature == None assert c2.temperature == None @@ -123,31 +123,27 @@ def test_temperature(cell_with_lattice): # distributed temperature cells, _, _, _ = cell_with_lattice c = cells[0] - c.temperature = (300., 600., 900.) + c.temperature = (300.0, 600.0, 900.0) def test_rotation(): u = openmc.Universe() c = openmc.Cell(fill=u) c.rotation = (180.0, 0.0, 0.0) - assert np.allclose(c.rotation_matrix, [ - [1., 0., 0.], - [0., -1., 0.], - [0., 0., -1.] - ]) + assert np.allclose( + c.rotation_matrix, [[1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, -1.0]] + ) c.rotation = (0.0, 90.0, 0.0) - assert np.allclose(c.rotation_matrix, [ - [0., 0., -1.], - [0., 1., 0.], - [1., 0., 0.] - ]) + assert np.allclose( + c.rotation_matrix, [[0.0, 0.0, -1.0], [0.0, 1.0, 0.0], [1.0, 0.0, 0.0]] + ) def test_get_nuclides(uo2): c = openmc.Cell(fill=uo2) nucs = c.get_nuclides() - assert nucs == ['U235', 'O16'] + assert nucs == ["U235", "O16"] def test_volume_setting(): @@ -169,18 +165,18 @@ def test_volume_setting(): def test_atoms_material_cell(uo2, water): - """ Test if correct number of atoms is returned. + """Test if correct number of atoms is returned. Also check if Cell.atoms still works after volume/material was changed """ c = openmc.Cell(fill=uo2) c.volume = 2.0 - expected_nucs = ['U235', 'O16'] + expected_nucs = ["U235", "O16"] # Precalculate the expected number of atoms - M = (atomic_mass('U235') + 2 * atomic_mass('O16')) / 3 + M = (atomic_mass("U235") + 2 * atomic_mass("O16")) / 3 expected_atoms = [ - 1/3 * uo2.density/M * AVOGADRO * 2.0, # U235 - 2/3 * uo2.density/M * AVOGADRO * 2.0 # O16 + 1 / 3 * uo2.density / M * AVOGADRO * 2.0, # U235 + 2 / 3 * uo2.density / M * AVOGADRO * 2.0, # O16 ] tuples = c.atoms.items() @@ -191,8 +187,8 @@ def test_atoms_material_cell(uo2, water): # Change volume and check if OK c.volume = 3.0 expected_atoms = [ - 1/3 * uo2.density/M * AVOGADRO * 3.0, # U235 - 2/3 * uo2.density/M * AVOGADRO * 3.0 # O16 + 1 / 3 * uo2.density / M * AVOGADRO * 3.0, # U235 + 2 / 3 * uo2.density / M * AVOGADRO * 3.0, # O16 ] tuples = c.atoms.items() @@ -202,11 +198,11 @@ def test_atoms_material_cell(uo2, water): # Change material and check if OK c.fill = water - expected_nucs = ['H1', 'O16'] - M = (2 * atomic_mass('H1') + atomic_mass('O16')) / 3 + expected_nucs = ["H1", "O16"] + M = (2 * atomic_mass("H1") + atomic_mass("O16")) / 3 expected_atoms = [ - 2/3 * water.density/M * AVOGADRO * 3.0, # H1 - 1/3 * water.density/M * AVOGADRO * 3.0 # O16 + 2 / 3 * water.density / M * AVOGADRO * 3.0, # H1 + 1 / 3 * water.density / M * AVOGADRO * 3.0, # O16 ] tuples = c.atoms.items() @@ -216,21 +212,23 @@ def test_atoms_material_cell(uo2, water): def test_atoms_distribmat_cell(uo2, water): - """ Test if correct number of atoms is returned for a cell with + """Test if correct number of atoms is returned for a cell with 'distribmat' fill """ c = openmc.Cell(fill=[uo2, water]) c.volume = 6.0 # Calculate the expected number of atoms - expected_nucs = ['U235', 'O16', 'H1'] - M_uo2 = (atomic_mass('U235') + 2 * atomic_mass('O16')) / 3 - M_water = (2 * atomic_mass('H1') + atomic_mass('O16')) / 3 + expected_nucs = ["U235", "O16", "H1"] + M_uo2 = (atomic_mass("U235") + 2 * atomic_mass("O16")) / 3 + M_water = (2 * atomic_mass("H1") + atomic_mass("O16")) / 3 expected_atoms = [ - 1/3 * uo2.density/M_uo2 * AVOGADRO * 3.0, # U235 - (2/3 * uo2.density/M_uo2 * AVOGADRO * 3.0 + - 1/3 * water.density/M_water * AVOGADRO * 3.0), # O16 - 2/3 * water.density/M_water * AVOGADRO * 3.0 # H1 + 1 / 3 * uo2.density / M_uo2 * AVOGADRO * 3.0, # U235 + ( + 2 / 3 * uo2.density / M_uo2 * AVOGADRO * 3.0 + + 1 / 3 * water.density / M_water * AVOGADRO * 3.0 + ), # O16 + 2 / 3 * water.density / M_water * AVOGADRO * 3.0, # H1 ] tuples = c.atoms.items() @@ -260,7 +258,7 @@ def test_atoms_errors(cell_with_lattice): def test_nuclide_densities(uo2): c = openmc.Cell(fill=uo2) - expected_nucs = ['U235', 'O16'] + expected_nucs = ["U235", "O16"] expected_density = [1.0, 2.0] tuples = list(c.get_nuclide_densities().values()) for nuc, density, t in zip(expected_nucs, expected_density, tuples): @@ -293,7 +291,7 @@ def test_get_all_materials(cell_with_lattice): m = openmc.Material() c = openmc.Cell(fill=m) test_mats = set(c.get_all_materials().values()) - assert not(test_mats ^ {m}) + assert not (test_mats ^ {m}) # Cell filled with distributed materials cells, mats, univ, lattice = cell_with_lattice @@ -311,36 +309,35 @@ def test_to_xml_element(cell_with_lattice): cells, mats, univ, lattice = cell_with_lattice c = cells[-1] - root = ET.Element('geometry') + root = ET.Element("geometry") elem = c.create_xml_subelement(root) - assert elem.tag == 'cell' - assert elem.get('id') == str(c.id) - assert elem.get('region') is None - surf_elem = root.find('surface') - assert surf_elem.get('id') == str(cells[0].region.surface.id) + assert elem.tag == "cell" + assert elem.get("id") == str(c.id) + assert elem.get("region") is None + surf_elem = root.find("surface") + assert surf_elem.get("id") == str(cells[0].region.surface.id) c = cells[0] c.temperature = 900.0 c.volume = 1.0 elem = c.create_xml_subelement(root) - assert elem.get('region') == str(c.region) - assert elem.get('temperature') == str(c.temperature) - assert elem.get('volume') == str(c.volume) + assert elem.get("region") == str(c.region) + assert elem.get("temperature") == str(c.temperature) + assert elem.get("volume") == str(c.volume) -@pytest.mark.parametrize("rotation", [ - (90, 45, 0), - [[1.0, 0.0, 0.0], [0.0, 0.0, 1.0], [0.0, -1.0, 0.0]] -]) +@pytest.mark.parametrize( + "rotation", [(90, 45, 0), [[1.0, 0.0, 0.0], [0.0, 0.0, 1.0], [0.0, -1.0, 0.0]]] +) def test_rotation_from_xml(rotation): # Make sure rotation attribute (matrix) round trips through XML correctly s = openmc.ZCylinder(r=10.0) cell = openmc.Cell(region=-s) cell.rotation = rotation - root = ET.Element('geometry') + root = ET.Element("geometry") elem = cell.create_xml_subelement(root) new_cell = openmc.Cell.from_xml_element( - elem, {s.id: s}, {'void': None}, openmc.Universe + elem, {s.id: s}, {"void": None}, openmc.Universe ) np.testing.assert_allclose(new_cell.rotation, cell.rotation) diff --git a/tests/unit_tests/test_complex_cell_bb.py b/tests/unit_tests/test_complex_cell_bb.py index 8a94d9ddcfd..3f47bff13a6 100644 --- a/tests/unit_tests/test_complex_cell_bb.py +++ b/tests/unit_tests/test_complex_cell_bb.py @@ -2,6 +2,7 @@ import openmc.lib import pytest + @pytest.fixture(autouse=True) def complex_cell(run_in_tmpdir, mpi_intracomm): @@ -10,52 +11,54 @@ def complex_cell(run_in_tmpdir, mpi_intracomm): model = openmc.model.Model() u235 = openmc.Material() - u235.set_density('g/cc', 4.5) + u235.set_density("g/cc", 4.5) u235.add_nuclide("U235", 1.0) u238 = openmc.Material() - u238.set_density('g/cc', 4.5) + u238.set_density("g/cc", 4.5) u238.add_nuclide("U238", 1.0) zr90 = openmc.Material() - zr90.set_density('g/cc', 2.0) + zr90.set_density("g/cc", 2.0) zr90.add_nuclide("Zr90", 1.0) n14 = openmc.Material() - n14.set_density('g/cc', 0.1) + n14.set_density("g/cc", 0.1) n14.add_nuclide("N14", 1.0) model.materials = (u235, u238, zr90, n14) - s1 = openmc.XPlane(-10.0, boundary_type='vacuum') + s1 = openmc.XPlane(-10.0, boundary_type="vacuum") s2 = openmc.XPlane(-7.0) s3 = openmc.XPlane(-4.0) s4 = openmc.XPlane(4.0) s5 = openmc.XPlane(7.0) - s6 = openmc.XPlane(10.0, boundary_type='vacuum') + s6 = openmc.XPlane(10.0, boundary_type="vacuum") s7 = openmc.XPlane(0.0) - s11 = openmc.YPlane(-10.0, boundary_type='vacuum') + s11 = openmc.YPlane(-10.0, boundary_type="vacuum") s12 = openmc.YPlane(-7.0) s13 = openmc.YPlane(-4.0) s14 = openmc.YPlane(4.0) s15 = openmc.YPlane(7.0) - s16 = openmc.YPlane(10.0, boundary_type='vacuum') + s16 = openmc.YPlane(10.0, boundary_type="vacuum") s17 = openmc.YPlane(0.0) c1 = openmc.Cell(fill=u235) c1.region = ~(-s3 | +s4 | ~(+s13 & -s14)) c2 = openmc.Cell(fill=u238) - c2.region = ~(+s3 & -s4 & +s13 & -s14) & +s2 & -s5 & +s12 & -s15 + c2.region = ~(+s3 & -s4 & +s13 & -s14) & +s2 & -s5 & +s12 & -s15 c3 = openmc.Cell(fill=zr90) - c3.region = ((+s1 & -s7 & +s17 & -s16) | (+s7 & -s6 & +s11 & -s17)) \ - & (-s2 | +s5 | -s12 | +s15) + c3.region = ((+s1 & -s7 & +s17 & -s16) | (+s7 & -s6 & +s11 & -s17)) & ( + -s2 | +s5 | -s12 | +s15 + ) c4 = openmc.Cell(fill=n14) - c4.region = ((+s1 & -s7 & +s11 & -s17) | (+s7 & -s6 & +s17 & -s16)) & \ - ~(+s2 & -s5 & +s12 & -s15) + c4.region = ((+s1 & -s7 & +s11 & -s17) | (+s7 & -s6 & +s17 & -s16)) & ~( + +s2 & -s5 & +s12 & -s15 + ) c5 = openmc.Cell(fill=n14) c5.region = ~(+s1 & -s6 & +s11 & -s16) @@ -66,8 +69,9 @@ def complex_cell(run_in_tmpdir, mpi_intracomm): model.settings.batches = 10 model.settings.inactive = 5 model.settings.particles = 100 - model.settings.source = openmc.IndependentSource(space=openmc.stats.Box( - [-10., -10., -1.], [10., 10., 1.])) + model.settings.source = openmc.IndependentSource( + space=openmc.stats.Box([-10.0, -10.0, -1.0], [10.0, 10.0, 1.0]) + ) model.settings.verbosity = 1 @@ -81,16 +85,15 @@ def complex_cell(run_in_tmpdir, mpi_intracomm): openmc.lib.finalize() -expected_results = ( (1, (( -4., -4., -np.inf), - ( 4., 4., np.inf))), - (2, (( -7., -7., -np.inf), - ( 7., 7., np.inf))), - (3, ((-10., -10., -np.inf), - ( 10., 10., np.inf))), - (4, ((-10., -10., -np.inf), - ( 10., 10., np.inf))), - (5, ((-np.inf, -np.inf, -np.inf), - ( np.inf, np.inf, np.inf))) ) +expected_results = ( + (1, ((-4.0, -4.0, -np.inf), (4.0, 4.0, np.inf))), + (2, ((-7.0, -7.0, -np.inf), (7.0, 7.0, np.inf))), + (3, ((-10.0, -10.0, -np.inf), (10.0, 10.0, np.inf))), + (4, ((-10.0, -10.0, -np.inf), (10.0, 10.0, np.inf))), + (5, ((-np.inf, -np.inf, -np.inf), (np.inf, np.inf, np.inf))), +) + + @pytest.mark.parametrize("cell_id,expected_box", expected_results) def test_cell_box(cell_id, expected_box): cell_box = openmc.lib.cells[cell_id].bounding_box diff --git a/tests/unit_tests/test_config.py b/tests/unit_tests/test_config.py index 9d3f53a7403..46445523d16 100644 --- a/tests/unit_tests/test_config.py +++ b/tests/unit_tests/test_config.py @@ -5,7 +5,7 @@ import pytest -@pytest.fixture(autouse=True, scope='module') +@pytest.fixture(autouse=True, scope="module") def reset_config(): config = dict(openmc.config) try: @@ -19,35 +19,35 @@ def test_config_basics(): assert isinstance(openmc.config, Mapping) for key, value in openmc.config.items(): assert isinstance(key, str) - if key == 'resolve_paths': + if key == "resolve_paths": assert isinstance(value, bool) else: assert isinstance(value, os.PathLike) # Set and delete - openmc.config['cross_sections'] = '/path/to/cross_sections.xml' - del openmc.config['cross_sections'] - assert 'cross_sections' not in openmc.config - assert 'OPENMC_CROSS_SECTIONS' not in os.environ + openmc.config["cross_sections"] = "/path/to/cross_sections.xml" + del openmc.config["cross_sections"] + assert "cross_sections" not in openmc.config + assert "OPENMC_CROSS_SECTIONS" not in os.environ # Can't use any key with pytest.raises(KeyError): - openmc.config['šŸ–'] = '/like/to/eat/bacon' + openmc.config["šŸ–"] = "/like/to/eat/bacon" def test_config_patch(): - openmc.config['cross_sections'] = '/path/to/cross_sections.xml' - with openmc.config.patch('cross_sections', '/path/to/other.xml'): - assert str(openmc.config['cross_sections']) == '/path/to/other.xml' - assert str(openmc.config['cross_sections']) == '/path/to/cross_sections.xml' + openmc.config["cross_sections"] = "/path/to/cross_sections.xml" + with openmc.config.patch("cross_sections", "/path/to/other.xml"): + assert str(openmc.config["cross_sections"]) == "/path/to/other.xml" + assert str(openmc.config["cross_sections"]) == "/path/to/cross_sections.xml" def test_config_set_envvar(): - openmc.config['cross_sections'] = '/path/to/cross_sections.xml' - assert os.environ['OPENMC_CROSS_SECTIONS'] == '/path/to/cross_sections.xml' + openmc.config["cross_sections"] = "/path/to/cross_sections.xml" + assert os.environ["OPENMC_CROSS_SECTIONS"] == "/path/to/cross_sections.xml" - openmc.config['mg_cross_sections'] = '/path/to/mg_cross_sections.h5' - assert os.environ['OPENMC_MG_CROSS_SECTIONS'] == '/path/to/mg_cross_sections.h5' + openmc.config["mg_cross_sections"] = "/path/to/mg_cross_sections.h5" + assert os.environ["OPENMC_MG_CROSS_SECTIONS"] == "/path/to/mg_cross_sections.h5" - openmc.config['chain_file'] = '/path/to/chain_file.xml' - assert os.environ['OPENMC_CHAIN_FILE'] == '/path/to/chain_file.xml' + openmc.config["chain_file"] = "/path/to/chain_file.xml" + assert os.environ["OPENMC_CHAIN_FILE"] == "/path/to/chain_file.xml" diff --git a/tests/unit_tests/test_cylindrical_mesh.py b/tests/unit_tests/test_cylindrical_mesh.py index b408bc0e917..d329df64174 100644 --- a/tests/unit_tests/test_cylindrical_mesh.py +++ b/tests/unit_tests/test_cylindrical_mesh.py @@ -7,17 +7,19 @@ geom_size = 5 + @pytest.fixture() def model(): openmc.reset_auto_ids() - water = openmc.Material(name='water') - water.add_element('H', 2.0) - water.add_element('O', 1.0) - water.set_density('g/cc', 1.0) + water = openmc.Material(name="water") + water.add_element("H", 2.0) + water.add_element("O", 1.0) + water.set_density("g/cc", 1.0) - rpp = openmc.model.RectangularParallelepiped(*([-geom_size, geom_size] * 3), - boundary_type='vacuum') + rpp = openmc.model.RectangularParallelepiped( + *([-geom_size, geom_size] * 3), boundary_type="vacuum" + ) cell = openmc.Cell(region=-rpp, fill=water) @@ -30,13 +32,13 @@ def model(): settings = openmc.Settings() settings.particles = 2000 settings.batches = 10 - settings.run_mode = 'fixed source' + settings.run_mode = "fixed source" # build mesh = openmc.CylindricalMesh( - phi_grid=np.linspace(0, 2*np.pi, 21), + phi_grid=np.linspace(0, 2 * np.pi, 21), z_grid=np.linspace(-geom_size, geom_size, 11), - r_grid=np.linspace(0, geom_size, geom_size) + r_grid=np.linspace(0, geom_size, geom_size), ) tally = openmc.Tally() @@ -49,9 +51,9 @@ def model(): return openmc.Model(geometry=geom, settings=settings, tallies=tallies) + def test_origin_read_write_to_xml(run_in_tmpdir, model): - """Tests that the origin attribute can be written and read back to XML - """ + """Tests that the origin attribute can be written and read back to XML""" mesh = model.tallies[0].filters[0].mesh mesh.origin = [0.1, 0.2, 0.3] model.tallies.export_to_xml() @@ -62,22 +64,24 @@ def test_origin_read_write_to_xml(run_in_tmpdir, model): new_mesh = new_tally.filters[0].mesh np.testing.assert_equal(new_mesh.origin, mesh.origin) -estimators = ('tracklength', 'collision') + +estimators = ("tracklength", "collision") origins = set(permutations((-geom_size, 0, 0))) origins |= set(permutations((geom_size, 0, 0))) test_cases = product(estimators, origins) + def label(p): if isinstance(p, tuple): - return f'origin:{p}' + return f"origin:{p}" if isinstance(p, str): - return f'estimator:{p}' + return f"estimator:{p}" -@pytest.mark.parametrize('estimator,origin', test_cases, ids=label) + +@pytest.mark.parametrize("estimator,origin", test_cases, ids=label) def test_offset_mesh(run_in_tmpdir, model, estimator, origin): - """Tests that the mesh has been moved based on tally results - """ + """Tests that the mesh has been moved based on tally results""" mesh = model.tallies[0].filters[0].mesh model.tallies[0].estimator = estimator # move the center of the cylinder mesh upwards @@ -94,7 +98,7 @@ def test_offset_mesh(run_in_tmpdir, model, estimator, origin): # check that the half of the mesh that is outside of the geometry # contains the zero values - mean = tally.get_reshaped_data('mean', expand_dims=True) + mean = tally.get_reshaped_data("mean", expand_dims=True) centroids = mesh.centroids for ijk in mesh.indices: i, j, k = np.array(ijk) - 1 @@ -103,18 +107,19 @@ def test_offset_mesh(run_in_tmpdir, model, estimator, origin): else: mean[i, j, k] != 0.0 + @pytest.fixture() def void_coincident_geom_model(): """A model with many geometric boundaries coincident with mesh boundaries - across many scales + across many scales """ openmc.reset_auto_ids() model = openmc.model.Model() model.materials = openmc.Materials() - radii = [0.1,1, 5, 50, 100, 150, 250] + radii = [0.1, 1, 5, 50, 100, 150, 250] cylinders = [openmc.ZCylinder(r=ri) for ri in radii] - cylinders[-1].boundary_type = 'vacuum' + cylinders[-1].boundary_type = "vacuum" regions = openmc.model.subdivide(cylinders)[:-1] cells = [openmc.Cell(region=r, fill=None) for r in regions] @@ -122,7 +127,7 @@ def void_coincident_geom_model(): model.geometry = geom - settings = openmc.Settings(run_mode='fixed source') + settings = openmc.Settings(run_mode="fixed source") settings.batches = 2 settings.particles = 1000 model.settings = settings @@ -130,12 +135,12 @@ def void_coincident_geom_model(): mesh = openmc.CylindricalMesh( r_grid=np.linspace(0, 250, 501), z_grid=[-250, 250], - phi_grid=np.linspace(0, 2*np.pi, 2), + phi_grid=np.linspace(0, 2 * np.pi, 2), ) mesh_filter = openmc.MeshFilter(mesh) tally = openmc.Tally() - tally.scores = ['flux'] + tally.scores = ["flux"] tally.filters = [mesh_filter] model.tallies = openmc.Tallies([tally]) @@ -155,6 +160,7 @@ def _check_void_cylindrical_tally(statepoint_filename): d_r = mesh.r_grid[1] - mesh.r_grid[0] assert neutron_flux == pytest.approx(d_r) + def test_void_geom_pnt_src(run_in_tmpdir, void_coincident_geom_model): src = openmc.IndependentSource() src.space = openmc.stats.Point() @@ -175,7 +181,7 @@ def test_void_geom_boundary_src(run_in_tmpdir, void_coincident_geom_model): outer_r = bbox[1][0] - 1e-08 n_sources = 100 - radial_vals = np.linspace(0.0, 2.0*np.pi, n_sources) + radial_vals = np.linspace(0.0, 2.0 * np.pi, n_sources) sources = [] @@ -186,12 +192,12 @@ def test_void_geom_boundary_src(run_in_tmpdir, void_coincident_geom_model): pnt = np.array([np.cos(val), np.sin(val), 0.0]) u = -pnt - src.space = openmc.stats.Point(outer_r*pnt) + src.space = openmc.stats.Point(outer_r * pnt) src.angle = openmc.stats.Monodirectional(u) - src.strength = 0.5/n_sources + src.strength = 0.5 / n_sources sources.append(src) void_coincident_geom_model.settings.source = sources sp_filename = void_coincident_geom_model.run() - _check_void_cylindrical_tally(sp_filename) \ No newline at end of file + _check_void_cylindrical_tally(sp_filename) diff --git a/tests/unit_tests/test_data_decay.py b/tests/unit_tests/test_data_decay.py index 8900e7eace5..bfb4085be23 100644 --- a/tests/unit_tests/test_data_decay.py +++ b/tests/unit_tests/test_data_decay.py @@ -16,67 +16,67 @@ def ufloat_close(a, b): assert a.std_dev == pytest.approx(b.std_dev) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def nb90(): """Nb90 decay data.""" - endf_data = os.environ['OPENMC_ENDF_DATA'] - filename = os.path.join(endf_data, 'decay', 'dec-041_Nb_090.endf') + endf_data = os.environ["OPENMC_ENDF_DATA"] + filename = os.path.join(endf_data, "decay", "dec-041_Nb_090.endf") return openmc.data.Decay.from_endf(filename) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def ba137m(): """Ba137_m1 decay data.""" - endf_data = os.environ['OPENMC_ENDF_DATA'] - filename = os.path.join(endf_data, 'decay', 'dec-056_Ba_137m1.endf') + endf_data = os.environ["OPENMC_ENDF_DATA"] + filename = os.path.join(endf_data, "decay", "dec-056_Ba_137m1.endf") return openmc.data.Decay.from_endf(filename) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def u235_yields(): """U235 fission product yield data.""" - endf_data = os.environ['OPENMC_ENDF_DATA'] - filename = os.path.join(endf_data, 'nfy', 'nfy-092_U_235.endf') + endf_data = os.environ["OPENMC_ENDF_DATA"] + filename = os.path.join(endf_data, "nfy", "nfy-092_U_235.endf") return openmc.data.FissionProductYields.from_endf(filename) def test_get_decay_modes(): - assert openmc.data.get_decay_modes(1.0) == ['beta-'] - assert openmc.data.get_decay_modes(6.0) == ['sf'] - assert openmc.data.get_decay_modes(10.0) == ['unknown'] + assert openmc.data.get_decay_modes(1.0) == ["beta-"] + assert openmc.data.get_decay_modes(6.0) == ["sf"] + assert openmc.data.get_decay_modes(10.0) == ["unknown"] - assert openmc.data.get_decay_modes(1.5) == ['beta-', 'n'] - assert openmc.data.get_decay_modes(1.4) == ['beta-', 'alpha'] - assert openmc.data.get_decay_modes(1.55) == ['beta-', 'n', 'n'] - assert openmc.data.get_decay_modes(1.555) == ['beta-', 'n', 'n', 'n'] - assert openmc.data.get_decay_modes(2.4) == ['ec/beta+', 'alpha'] + assert openmc.data.get_decay_modes(1.5) == ["beta-", "n"] + assert openmc.data.get_decay_modes(1.4) == ["beta-", "alpha"] + assert openmc.data.get_decay_modes(1.55) == ["beta-", "n", "n"] + assert openmc.data.get_decay_modes(1.555) == ["beta-", "n", "n", "n"] + assert openmc.data.get_decay_modes(2.4) == ["ec/beta+", "alpha"] def test_nb90_halflife(nb90): ufloat_close(nb90.half_life, ufloat(52560.0, 180.0)) - ufloat_close(nb90.decay_constant, log(2.)/nb90.half_life) + ufloat_close(nb90.decay_constant, log(2.0) / nb90.half_life) ufloat_close(nb90.decay_energy, ufloat(2265527.5, 25159.400474401213)) def test_nb90_nuclide(nb90): - assert nb90.nuclide['atomic_number'] == 41 - assert nb90.nuclide['mass_number'] == 90 - assert nb90.nuclide['isomeric_state'] == 0 - assert nb90.nuclide['parity'] == 1.0 - assert nb90.nuclide['spin'] == 8.0 - assert not nb90.nuclide['stable'] - assert nb90.nuclide['mass'] == pytest.approx(89.13888) + assert nb90.nuclide["atomic_number"] == 41 + assert nb90.nuclide["mass_number"] == 90 + assert nb90.nuclide["isomeric_state"] == 0 + assert nb90.nuclide["parity"] == 1.0 + assert nb90.nuclide["spin"] == 8.0 + assert not nb90.nuclide["stable"] + assert nb90.nuclide["mass"] == pytest.approx(89.13888) def test_nb90_modes(nb90): assert len(nb90.modes) == 2 ec = nb90.modes[0] - assert ec.modes == ['ec/beta+'] - assert ec.parent == 'Nb90' - assert ec.daughter == 'Zr90' - assert 'Nb90 -> Zr90' in str(ec) + assert ec.modes == ["ec/beta+"] + assert ec.parent == "Nb90" + assert ec.daughter == "Zr90" + assert "Nb90 -> Zr90" in str(ec) ufloat_close(ec.branching_ratio, ufloat(0.0147633, 0.0003386195)) - ufloat_close(ec.energy, ufloat(6111000., 4000.)) + ufloat_close(ec.energy, ufloat(6111000.0, 4000.0)) # Make sure branching ratios sum to 1 total = sum(m.branching_ratio for m in nb90.modes) @@ -84,23 +84,23 @@ def test_nb90_modes(nb90): def test_nb90_spectra(nb90): - assert sorted(nb90.spectra.keys()) == ['e-', 'ec/beta+', 'gamma', 'xray'] + assert sorted(nb90.spectra.keys()) == ["e-", "ec/beta+", "gamma", "xray"] def test_fpy(u235_yields): - assert u235_yields.nuclide['atomic_number'] == 92 - assert u235_yields.nuclide['mass_number'] == 235 - assert u235_yields.nuclide['isomeric_state'] == 0 - assert u235_yields.nuclide['name'] == 'U235' - assert u235_yields.energies == pytest.approx([0.0253, 500.e3, 1.4e7]) + assert u235_yields.nuclide["atomic_number"] == 92 + assert u235_yields.nuclide["mass_number"] == 235 + assert u235_yields.nuclide["isomeric_state"] == 0 + assert u235_yields.nuclide["name"] == "U235" + assert u235_yields.energies == pytest.approx([0.0253, 500.0e3, 1.4e7]) assert len(u235_yields.cumulative) == 3 thermal = u235_yields.cumulative[0] - ufloat_close(thermal['I135'], ufloat(0.0628187, 0.000879461)) + ufloat_close(thermal["I135"], ufloat(0.0628187, 0.000879461)) assert len(u235_yields.independent) == 3 thermal = u235_yields.independent[0] - ufloat_close(thermal['I135'], ufloat(0.0292737, 0.000819663)) + ufloat_close(thermal["I135"], ufloat(0.0292737, 0.000819663)) def test_sources(ba137m, nb90): @@ -115,9 +115,9 @@ def test_sources(ba137m, nb90): assert isinstance(dist, openmc.stats.Univariate) # Check for presence of 662 keV gamma ray in decay of Ba137m - gamma_source = ba137m.sources['photon'] + gamma_source = ba137m.sources["photon"] assert isinstance(gamma_source, openmc.stats.Discrete) - b = np.isclose(gamma_source.x, 661657.) + b = np.isclose(gamma_source.x, 661657.0) assert np.count_nonzero(b) == 1 # Check value of decay/s/atom @@ -126,26 +126,26 @@ def test_sources(ba137m, nb90): # Nb90 decays by β+ and should emit positrons, electrons, and photons sources = nb90.sources - assert len(set(sources.keys()) ^ {'positron', 'electron', 'photon'}) == 0 + assert len(set(sources.keys()) ^ {"positron", "electron", "photon"}) == 0 def test_decay_photon_energy(): # If chain file is not set, we should get a data error - if 'chain_file' in openmc.config: - del openmc.config['chain_file'] + if "chain_file" in openmc.config: + del openmc.config["chain_file"] with pytest.raises(DataError): - openmc.data.decay_photon_energy('I135') + openmc.data.decay_photon_energy("I135") # Set chain file to simple chain - openmc.config['chain_file'] = Path(__file__).parents[1] / "chain_simple.xml" + openmc.config["chain_file"] = Path(__file__).parents[1] / "chain_simple.xml" # Check strength of I135 source and presence of specific spectral line - src = openmc.data.decay_photon_energy('I135') + src = openmc.data.decay_photon_energy("I135") assert isinstance(src, openmc.stats.Discrete) assert src.integral() == pytest.approx(3.920996223799345e-05) - assert 1260409. in src.x + assert 1260409.0 in src.x # Check Xe135 source, which should be tabular - src = openmc.data.decay_photon_energy('Xe135') + src = openmc.data.decay_photon_energy("Xe135") assert isinstance(src, openmc.stats.Tabular) assert src.integral() == pytest.approx(2.076506258964966e-05) diff --git a/tests/unit_tests/test_data_dose.py b/tests/unit_tests/test_data_dose.py index 2d80cf8384f..05d3589c776 100644 --- a/tests/unit_tests/test_data_dose.py +++ b/tests/unit_tests/test_data_dose.py @@ -4,31 +4,31 @@ def test_dose_coefficients(): # Spot checks on values from ICRP tables - energy, dose = dose_coefficients('photon', 'AP') + energy, dose = dose_coefficients("photon", "AP") assert energy[0] == approx(0.01e6) assert dose[0] == approx(0.0685) assert energy[-1] == approx(10e9) assert dose[-1] == approx(90.4) # updated in corrigendum - energy, dose = dose_coefficients('neutron', 'LLAT') + energy, dose = dose_coefficients("neutron", "LLAT") assert energy[0] == approx(1e-3) assert dose[0] == approx(1.04) assert energy[-1] == approx(10e9) assert dose[-1] == approx(1.23e3) - energy, dose = dose_coefficients('electron', 'ISO') + energy, dose = dose_coefficients("electron", "ISO") assert energy[0] == approx(0.01e6) assert dose[0] == approx(0.0188) assert energy[-1] == approx(10e9) assert dose[-1] == approx(699.0) - energy, dose = dose_coefficients('photon', data_source='icrp74') + energy, dose = dose_coefficients("photon", data_source="icrp74") assert energy[0] == approx(0.01e6) - assert dose[0] == approx(7.43*0.00653) + assert dose[0] == approx(7.43 * 0.00653) assert energy[-1] == approx(10.0e6) - assert dose[-1] == approx(24.0*0.990) + assert dose[-1] == approx(24.0 * 0.990) - energy, dose = dose_coefficients('neutron', 'LLAT', data_source='icrp74') + energy, dose = dose_coefficients("neutron", "LLAT", data_source="icrp74") assert energy[0] == approx(1e-3) assert dose[0] == approx(1.68) assert energy[-1] == approx(20.0e6) @@ -36,8 +36,8 @@ def test_dose_coefficients(): # Invalid particle/geometry should raise an exception with raises(ValueError): - dose_coefficients('slime', 'LAT') + dose_coefficients("slime", "LAT") with raises(ValueError): - dose_coefficients('neutron', 'ZZ') + dose_coefficients("neutron", "ZZ") with raises(ValueError): - dose_coefficients('neutron', data_source='icrp7000') + dose_coefficients("neutron", data_source="icrp7000") diff --git a/tests/unit_tests/test_data_kalbach_mann.py b/tests/unit_tests/test_data_kalbach_mann.py index 3b837af7da9..16b5e682779 100644 --- a/tests/unit_tests/test_data_kalbach_mann.py +++ b/tests/unit_tests/test_data_kalbach_mann.py @@ -15,37 +15,37 @@ from . import needs_njoy -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def neutron(): """Neutron AtomicRepresentation.""" return _AtomicRepresentation(z=0, a=1) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def triton(): """Triton AtomicRepresentation.""" return _AtomicRepresentation(z=1, a=3) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def b10(): """B10 AtomicRepresentation.""" return _AtomicRepresentation(z=5, a=10) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def c12(): """C12 AtomicRepresentation.""" return _AtomicRepresentation(z=6, a=12) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def c13(): """C13 AtomicRepresentation.""" return _AtomicRepresentation(z=6, a=13) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def na23(): """Na23 AtomicRepresentation.""" return _AtomicRepresentation(z=11, a=23) @@ -91,9 +91,7 @@ def test_atomic_representation(neutron, triton, b10, c12, c13, na23): def test_separation_energy(triton, b10, c13): """Comparison to hand-calculations on a simple example.""" assert _separation_energy( - compound=c13, - nucleus=b10, - particle=triton + compound=c13, nucleus=b10, particle=triton ) == pytest.approx(18.6880713) @@ -110,7 +108,7 @@ def test_kalbach_slope(): energy_emitted=energy_emitted, za_projectile=1000, za_emitted=1, - za_target=6012 + za_target=6012, ) assert kalbach_slope( @@ -118,16 +116,17 @@ def test_kalbach_slope(): energy_emitted=energy_emitted, za_projectile=1, za_emitted=1003, - za_target=6012 + za_target=6012, ) == pytest.approx(0.8409921475) @pytest.mark.parametrize( - "hdf5_filename, endf_filename", [ - ('O16.h5', 'n-008_O_016.endf'), - ('Ca46.h5', 'n-020_Ca_046.endf'), - ('Hg204.h5', 'n-080_Hg_204.endf') - ] + "hdf5_filename, endf_filename", + [ + ("O16.h5", "n-008_O_016.endf"), + ("Ca46.h5", "n-020_Ca_046.endf"), + ("Hg204.h5", "n-080_Hg_204.endf"), + ], ) def test_comparison_slope_hdf5(hdf5_filename, endf_filename): """Test the calculation of the Kalbach-Mann slope done by OpenMC @@ -147,14 +146,14 @@ def test_comparison_slope_hdf5(hdf5_filename, endf_filename): """ # HDF5 data - hdf5_directory = Path(os.environ['OPENMC_CROSS_SECTIONS']).parent + hdf5_directory = Path(os.environ["OPENMC_CROSS_SECTIONS"]).parent hdf5_data = IncidentNeutron.from_hdf5(hdf5_directory / hdf5_filename) hdf5_product = hdf5_data[5].products[0] hdf5_distribution = hdf5_product.distribution[0] # ENDF data - endf_directory = Path(os.environ['OPENMC_ENDF_DATA']) - endf_path = endf_directory / 'neutrons' / endf_filename + endf_directory = Path(os.environ["OPENMC_ENDF_DATA"]) + endf_path = endf_directory / "neutrons" / endf_filename endf_data = IncidentNeutron.from_endf(endf_path) endf_product = endf_data[5].products[0] endf_distribution = endf_product.distribution[0] @@ -170,7 +169,5 @@ def test_comparison_slope_hdf5(hdf5_filename, endf_filename): assert endf_distribution._calculated_slope[i] np.testing.assert_array_almost_equal( - endf_distribution.slope[i].y, - hdf5_slope.y, - decimal=5 + endf_distribution.slope[i].y, hdf5_slope.y, decimal=5 ) diff --git a/tests/unit_tests/test_data_misc.py b/tests/unit_tests/test_data_misc.py index 226d848a502..84c6ada344a 100644 --- a/tests/unit_tests/test_data_misc.py +++ b/tests/unit_tests/test_data_misc.py @@ -12,29 +12,29 @@ def test_data_library(tmpdir): lib = openmc.data.DataLibrary.from_xml() for f in lib: - assert sorted(f.keys()) == ['materials', 'path', 'type'] + assert sorted(f.keys()) == ["materials", "path", "type"] - f = lib.get_by_material('U235') - assert f['type'] == 'neutron' - assert 'U235' in f['materials'] + f = lib.get_by_material("U235") + assert f["type"] == "neutron" + assert "U235" in f["materials"] - f = lib.get_by_material('c_H_in_H2O', data_type='thermal') - assert f['type'] == 'thermal' - assert 'c_H_in_H2O' in f['materials'] + f = lib.get_by_material("c_H_in_H2O", data_type="thermal") + assert f["type"] == "thermal" + assert "c_H_in_H2O" in f["materials"] - lib.remove_by_material('Pu239') - assert lib.get_by_material('Pu239') is None + lib.remove_by_material("Pu239") + assert lib.get_by_material("Pu239") is None - filename = str(tmpdir.join('test.xml')) + filename = str(tmpdir.join("test.xml")) lib.export_to_xml(filename) assert os.path.exists(filename) new_lib = openmc.data.DataLibrary() - directory = os.path.dirname(os.environ['OPENMC_CROSS_SECTIONS']) - new_lib.register_file(os.path.join(directory, 'H1.h5')) - assert new_lib[-1]['type'] == 'neutron' - new_lib.register_file(os.path.join(directory, 'c_Zr_in_ZrH.h5')) - assert new_lib[-1]['type'] == 'thermal' + directory = os.path.dirname(os.environ["OPENMC_CROSS_SECTIONS"]) + new_lib.register_file(os.path.join(directory, "H1.h5")) + assert new_lib[-1]["type"] == "neutron" + new_lib.register_file(os.path.join(directory, "c_Zr_in_ZrH.h5")) + assert new_lib[-1]["type"] == "thermal" def test_depletion_chain_data_library(run_in_tmpdir): @@ -45,33 +45,33 @@ def test_depletion_chain_data_library(run_in_tmpdir): assert len(dep_lib.libraries) == prev_len + 1 # Inspect dep_dict = dep_lib.libraries[-1] - assert dep_dict['materials'] == [] - assert dep_dict['type'] == 'depletion_chain' - assert dep_dict['path'] == str(chain_path) + assert dep_dict["materials"] == [] + assert dep_dict["type"] == "depletion_chain" + assert dep_dict["path"] == str(chain_path) out_path = "cross_section_chain.xml" dep_lib.export_to_xml(out_path) dep_import = openmc.data.DataLibrary.from_xml(out_path) for lib in reversed(dep_import.libraries): - if lib['type'] == 'depletion_chain': + if lib["type"] == "depletion_chain": break else: raise IndexError("depletion_chain not found in exported DataLibrary") - assert os.path.exists(lib['path']) + assert os.path.exists(lib["path"]) def test_linearize(): """Test linearization of a continuous function.""" - x, y = openmc.data.linearize([-1., 1.], lambda x: 1 - x*x) + x, y = openmc.data.linearize([-1.0, 1.0], lambda x: 1 - x * x) f = openmc.data.Tabulated1D(x, y) - assert f(-0.5) == pytest.approx(1 - 0.5*0.5, 0.001) - assert f(0.32) == pytest.approx(1 - 0.32*0.32, 0.001) + assert f(-0.5) == pytest.approx(1 - 0.5 * 0.5, 0.001) + assert f(0.32) == pytest.approx(1 - 0.32 * 0.32, 0.001) def test_thin(): """Test thinning of a tabulated function.""" - x = np.linspace(0., 2*np.pi, 1000) + x = np.linspace(0.0, 2 * np.pi, 1000) y = np.sin(x) x_thin, y_thin = openmc.data.thin(x, y) f = openmc.data.Tabulated1D(x_thin, y_thin) @@ -79,19 +79,19 @@ def test_thin(): def test_atomic_mass(): - assert openmc.data.atomic_mass('H1') == 1.007825031898 - assert openmc.data.atomic_mass('U235') == 235.043928117 - assert openmc.data.atomic_mass('Li6') == 6.01512288742 - assert openmc.data.atomic_mass('Pb220') == 220.025905 + assert openmc.data.atomic_mass("H1") == 1.007825031898 + assert openmc.data.atomic_mass("U235") == 235.043928117 + assert openmc.data.atomic_mass("Li6") == 6.01512288742 + assert openmc.data.atomic_mass("Pb220") == 220.025905 with pytest.raises(KeyError): - openmc.data.atomic_mass('U100') + openmc.data.atomic_mass("U100") def test_atomic_weight(): - assert openmc.data.atomic_weight('C') == 12.011115164865895 - assert openmc.data.atomic_weight('carbon') == 12.011115164865895 + assert openmc.data.atomic_weight("C") == 12.011115164865895 + assert openmc.data.atomic_weight("carbon") == 12.011115164865895 with pytest.raises(ValueError): - openmc.data.atomic_weight('Qt') + openmc.data.atomic_weight("Qt") def test_water_density(): @@ -99,46 +99,48 @@ def test_water_density(): # These test values are from IAPWS R7-97(2012). They are actually specific # volumes so they need to be inverted. They also need to be divided by 1000 # to convert from [kg / m^3] to [g / cm^3]. - assert dens(300.0, 3.0) == pytest.approx(1e-3/0.100215168e-2, 1e-6) - assert dens(300.0, 80.0) == pytest.approx(1e-3/0.971180894e-3, 1e-6) - assert dens(500.0, 3.0) == pytest.approx(1e-3/0.120241800e-2, 1e-6) + assert dens(300.0, 3.0) == pytest.approx(1e-3 / 0.100215168e-2, 1e-6) + assert dens(300.0, 80.0) == pytest.approx(1e-3 / 0.971180894e-3, 1e-6) + assert dens(500.0, 3.0) == pytest.approx(1e-3 / 0.120241800e-2, 1e-6) def test_gnds_name(): - assert openmc.data.gnds_name(1, 1) == 'H1' - assert openmc.data.gnds_name(40, 90) == ('Zr90') - assert openmc.data.gnds_name(95, 242, 0) == ('Am242') - assert openmc.data.gnds_name(95, 242, 1) == ('Am242_m1') - assert openmc.data.gnds_name(95, 242, 10) == ('Am242_m10') + assert openmc.data.gnds_name(1, 1) == "H1" + assert openmc.data.gnds_name(40, 90) == ("Zr90") + assert openmc.data.gnds_name(95, 242, 0) == ("Am242") + assert openmc.data.gnds_name(95, 242, 1) == ("Am242_m1") + assert openmc.data.gnds_name(95, 242, 10) == ("Am242_m10") def test_isotopes(): - hydrogen_isotopes = [('H1', 0.99984426), ('H2', 0.00015574)] - assert openmc.data.isotopes('H') == hydrogen_isotopes - assert openmc.data.isotopes('hydrogen') == hydrogen_isotopes - assert openmc.data.isotopes('Al') == [('Al27', 1.0)] - assert openmc.data.isotopes('Aluminum') == [('Al27', 1.0)] - assert openmc.data.isotopes('aluminium') == [('Al27', 1.0)] + hydrogen_isotopes = [("H1", 0.99984426), ("H2", 0.00015574)] + assert openmc.data.isotopes("H") == hydrogen_isotopes + assert openmc.data.isotopes("hydrogen") == hydrogen_isotopes + assert openmc.data.isotopes("Al") == [("Al27", 1.0)] + assert openmc.data.isotopes("Aluminum") == [("Al27", 1.0)] + assert openmc.data.isotopes("aluminium") == [("Al27", 1.0)] with pytest.raises(ValueError): - openmc.data.isotopes('Š§Š¾Ń€Š½Š¾Š±ŠøŠ»ŃŒ') + openmc.data.isotopes("Š§Š¾Ń€Š½Š¾Š±ŠøŠ»ŃŒ") def test_zam(): - assert openmc.data.zam('H1') == (1, 1, 0) - assert openmc.data.zam('Zr90') == (40, 90, 0) - assert openmc.data.zam('Am242') == (95, 242, 0) - assert openmc.data.zam('Am242_m1') == (95, 242, 1) - assert openmc.data.zam('Am242_m10') == (95, 242, 10) + assert openmc.data.zam("H1") == (1, 1, 0) + assert openmc.data.zam("Zr90") == (40, 90, 0) + assert openmc.data.zam("Am242") == (95, 242, 0) + assert openmc.data.zam("Am242_m1") == (95, 242, 1) + assert openmc.data.zam("Am242_m10") == (95, 242, 10) with pytest.raises(ValueError): - openmc.data.zam('garbage') + openmc.data.zam("garbage") def test_half_life(): - assert openmc.data.half_life('H2') is None - assert openmc.data.half_life('U235') == pytest.approx(2.22102e16) - assert openmc.data.half_life('Am242') == pytest.approx(57672.0) - assert openmc.data.half_life('Am242_m1') == pytest.approx(4449622000.0) - assert openmc.data.decay_constant('H2') == 0.0 - assert openmc.data.decay_constant('U235') == pytest.approx(log(2.0)/2.22102e16) - assert openmc.data.decay_constant('Am242') == pytest.approx(log(2.0)/57672.0) - assert openmc.data.decay_constant('Am242_m1') == pytest.approx(log(2.0)/4449622000.0) + assert openmc.data.half_life("H2") is None + assert openmc.data.half_life("U235") == pytest.approx(2.22102e16) + assert openmc.data.half_life("Am242") == pytest.approx(57672.0) + assert openmc.data.half_life("Am242_m1") == pytest.approx(4449622000.0) + assert openmc.data.decay_constant("H2") == 0.0 + assert openmc.data.decay_constant("U235") == pytest.approx(log(2.0) / 2.22102e16) + assert openmc.data.decay_constant("Am242") == pytest.approx(log(2.0) / 57672.0) + assert openmc.data.decay_constant("Am242_m1") == pytest.approx( + log(2.0) / 4449622000.0 + ) diff --git a/tests/unit_tests/test_data_multipole.py b/tests/unit_tests/test_data_multipole.py index 4c2ba96d620..d40a639d52b 100644 --- a/tests/unit_tests/test_data_multipole.py +++ b/tests/unit_tests/test_data_multipole.py @@ -6,59 +6,65 @@ import openmc.data -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def u235(): - directory = pathlib.Path(os.environ['OPENMC_CROSS_SECTIONS']).parent - u235 = directory / 'wmp' / '092235.h5' + directory = pathlib.Path(os.environ["OPENMC_CROSS_SECTIONS"]).parent + u235 = directory / "wmp" / "092235.h5" return openmc.data.WindowedMultipole.from_hdf5(u235) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def b10(): - directory = pathlib.Path(os.environ['OPENMC_CROSS_SECTIONS']).parent - b10 = directory / 'wmp' / '005010.h5' + directory = pathlib.Path(os.environ["OPENMC_CROSS_SECTIONS"]).parent + b10 = directory / "wmp" / "005010.h5" return openmc.data.WindowedMultipole.from_hdf5(b10) def test_evaluate(u235): """Test the cross section evaluation of a library.""" - energies = [1e-3, 1.0, 10.0, 50.] + energies = [1e-3, 1.0, 10.0, 50.0] scattering, absorption, fission = u235(energies, 0.0) - assert (scattering[1], absorption[1], fission[1]) == \ - pytest.approx((13.09, 77.56, 67.36), rel=1e-3) + assert (scattering[1], absorption[1], fission[1]) == pytest.approx( + (13.09, 77.56, 67.36), rel=1e-3 + ) scattering, absorption, fission = u235(energies, 300.0) - assert (scattering[2], absorption[2], fission[2]) == \ - pytest.approx((11.24, 21.26, 15.50), rel=1e-3) + assert (scattering[2], absorption[2], fission[2]) == pytest.approx( + (11.24, 21.26, 15.50), rel=1e-3 + ) def test_evaluate_none_poles(b10): """Test a library with no poles, i.e., purely polynomials.""" energies = [1e-3, 1.0, 10.0, 1e3, 1e5] scattering, absorption, fission = b10(energies, 0.0) - assert (scattering[0], absorption[0], fission[0]) == \ - pytest.approx((2.201, 19330., 0.), rel=1e-3) + assert (scattering[0], absorption[0], fission[0]) == pytest.approx( + (2.201, 19330.0, 0.0), rel=1e-3 + ) scattering, absorption, fission = b10(energies, 300.0) - assert (scattering[-1], absorption[-1], fission[-1]) == \ - pytest.approx((2.878, 1.982, 0.), rel=1e-3) + assert (scattering[-1], absorption[-1], fission[-1]) == pytest.approx( + (2.878, 1.982, 0.0), rel=1e-3 + ) def test_export_to_hdf5(tmpdir, u235): - filename = str(tmpdir.join('092235.h5')) + filename = str(tmpdir.join("092235.h5")) u235.export_to_hdf5(filename) assert os.path.exists(filename) def test_from_endf(): - pytest.importorskip('vectfit') - endf_data = os.environ['OPENMC_ENDF_DATA'] - endf_file = os.path.join(endf_data, 'neutrons', 'n-001_H_001.endf') + pytest.importorskip("vectfit") + endf_data = os.environ["OPENMC_ENDF_DATA"] + endf_file = os.path.join(endf_data, "neutrons", "n-001_H_001.endf") return openmc.data.WindowedMultipole.from_endf( - endf_file, log=True, wmp_options={"n_win": 400, "n_cf": 3}) + endf_file, log=True, wmp_options={"n_win": 400, "n_cf": 3} + ) def test_from_endf_search(): - pytest.importorskip('vectfit') - endf_data = os.environ['OPENMC_ENDF_DATA'] - endf_file = os.path.join(endf_data, 'neutrons', 'n-095_Am_244.endf') + pytest.importorskip("vectfit") + endf_data = os.environ["OPENMC_ENDF_DATA"] + endf_file = os.path.join(endf_data, "neutrons", "n-095_Am_244.endf") return openmc.data.WindowedMultipole.from_endf( - endf_file, log=True, wmp_options={"search": True, 'rtol':1e-2}) + endf_file, log=True, wmp_options={"search": True, "rtol": 1e-2} + ) diff --git a/tests/unit_tests/test_data_neutron.py b/tests/unit_tests/test_data_neutron.py index db6ae1eb850..9556f61a73b 100644 --- a/tests/unit_tests/test_data_neutron.py +++ b/tests/unit_tests/test_data_neutron.py @@ -8,152 +8,162 @@ from . import needs_njoy -_TEMPERATURES = [300., 600., 900.] +_TEMPERATURES = [300.0, 600.0, 900.0] -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def pu239(): """Pu239 HDF5 data.""" - directory = os.path.dirname(os.environ['OPENMC_CROSS_SECTIONS']) - filename = os.path.join(directory, 'Pu239.h5') + directory = os.path.dirname(os.environ["OPENMC_CROSS_SECTIONS"]) + filename = os.path.join(directory, "Pu239.h5") return openmc.data.IncidentNeutron.from_hdf5(filename) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def xe135(): """Xe135 ENDF data (contains SLBW resonance range)""" - endf_data = os.environ['OPENMC_ENDF_DATA'] - filename = os.path.join(endf_data, 'neutrons', 'n-054_Xe_135.endf') + endf_data = os.environ["OPENMC_ENDF_DATA"] + filename = os.path.join(endf_data, "neutrons", "n-054_Xe_135.endf") return openmc.data.IncidentNeutron.from_endf(filename) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def sm150(): """Sm150 ENDF data (contains MLBW resonance range)""" - endf_data = os.environ['OPENMC_ENDF_DATA'] - filename = os.path.join(endf_data, 'neutrons', 'n-062_Sm_150.endf') + endf_data = os.environ["OPENMC_ENDF_DATA"] + filename = os.path.join(endf_data, "neutrons", "n-062_Sm_150.endf") return openmc.data.IncidentNeutron.from_endf(filename) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def gd154(): """Gd154 ENDF data (contains Reich Moore resonance range and reosnance covariance with LCOMP=1).""" - endf_data = os.environ['OPENMC_ENDF_DATA'] - filename = os.path.join(endf_data, 'neutrons', 'n-064_Gd_154.endf') + endf_data = os.environ["OPENMC_ENDF_DATA"] + filename = os.path.join(endf_data, "neutrons", "n-064_Gd_154.endf") return openmc.data.IncidentNeutron.from_endf(filename, covariance=True) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def cl35(): """Cl35 ENDF data (contains RML resonance range)""" - endf_data = os.environ['OPENMC_ENDF_DATA'] - filename = os.path.join(endf_data, 'neutrons', 'n-017_Cl_035.endf') + endf_data = os.environ["OPENMC_ENDF_DATA"] + filename = os.path.join(endf_data, "neutrons", "n-017_Cl_035.endf") return openmc.data.IncidentNeutron.from_endf(filename) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def am241(): """Am241 ENDF data (contains Madland-Nix fission energy distribution).""" - endf_data = os.environ['OPENMC_ENDF_DATA'] - filename = os.path.join(endf_data, 'neutrons', 'n-095_Am_241.endf') + endf_data = os.environ["OPENMC_ENDF_DATA"] + filename = os.path.join(endf_data, "neutrons", "n-095_Am_241.endf") return openmc.data.IncidentNeutron.from_endf(filename) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def u233(): """U233 ENDF data (contains Watt fission energy distribution).""" - endf_data = os.environ['OPENMC_ENDF_DATA'] - filename = os.path.join(endf_data, 'neutrons', 'n-092_U_233.endf') + endf_data = os.environ["OPENMC_ENDF_DATA"] + filename = os.path.join(endf_data, "neutrons", "n-092_U_233.endf") return openmc.data.IncidentNeutron.from_endf(filename) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def u236(): """U236 ENDF data (contains Watt fission energy distribution).""" - endf_data = os.environ['OPENMC_ENDF_DATA'] - filename = os.path.join(endf_data, 'neutrons', 'n-092_U_236.endf') + endf_data = os.environ["OPENMC_ENDF_DATA"] + filename = os.path.join(endf_data, "neutrons", "n-092_U_236.endf") return openmc.data.IncidentNeutron.from_endf(filename) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def na22(): """Na22 ENDF data (contains evaporation spectrum).""" - endf_data = os.environ['OPENMC_ENDF_DATA'] - filename = os.path.join(endf_data, 'neutrons', 'n-011_Na_022.endf') + endf_data = os.environ["OPENMC_ENDF_DATA"] + filename = os.path.join(endf_data, "neutrons", "n-011_Na_022.endf") return openmc.data.IncidentNeutron.from_endf(filename) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def na23(): """Na23 ENDF data (contains MLBW resonance covariance with LCOMP=0).""" - endf_data = os.environ['OPENMC_ENDF_DATA'] - filename = os.path.join(endf_data, 'neutrons', 'n-011_Na_023.endf') + endf_data = os.environ["OPENMC_ENDF_DATA"] + filename = os.path.join(endf_data, "neutrons", "n-011_Na_023.endf") return openmc.data.IncidentNeutron.from_endf(filename, covariance=True) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def be9(): """Be9 ENDF data (contains laboratory angle-energy distribution).""" - endf_data = os.environ['OPENMC_ENDF_DATA'] - filename = os.path.join(endf_data, 'neutrons', 'n-004_Be_009.endf') + endf_data = os.environ["OPENMC_ENDF_DATA"] + filename = os.path.join(endf_data, "neutrons", "n-004_Be_009.endf") return openmc.data.IncidentNeutron.from_endf(filename) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def h2(): - endf_data = os.environ['OPENMC_ENDF_DATA'] - endf_file = os.path.join(endf_data, 'neutrons', 'n-001_H_002.endf') - return openmc.data.IncidentNeutron.from_njoy( - endf_file, temperatures=_TEMPERATURES) + endf_data = os.environ["OPENMC_ENDF_DATA"] + endf_file = os.path.join(endf_data, "neutrons", "n-001_H_002.endf") + return openmc.data.IncidentNeutron.from_njoy(endf_file, temperatures=_TEMPERATURES) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def am244(): - endf_data = os.environ['OPENMC_ENDF_DATA'] - endf_file = os.path.join(endf_data, 'neutrons', 'n-095_Am_244.endf') + endf_data = os.environ["OPENMC_ENDF_DATA"] + endf_file = os.path.join(endf_data, "neutrons", "n-095_Am_244.endf") return openmc.data.IncidentNeutron.from_njoy(endf_file) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def ti50(): """Ti50 ENDF data (contains Multi-level Breit-Wigner resonance range and - resonance covariance with LCOMP=1).""" - endf_data = os.environ['OPENMC_ENDF_DATA'] - filename = os.path.join(endf_data, 'neutrons', 'n-022_Ti_050.endf') + resonance covariance with LCOMP=1).""" + endf_data = os.environ["OPENMC_ENDF_DATA"] + filename = os.path.join(endf_data, "neutrons", "n-022_Ti_050.endf") return openmc.data.IncidentNeutron.from_endf(filename, covariance=True) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def cf252(): """Cf252 ENDF data (contains RM resonance covariance with LCOMP=0).""" - endf_data = os.environ['OPENMC_ENDF_DATA'] - filename = os.path.join(endf_data, 'neutrons', 'n-098_Cf_252.endf') + endf_data = os.environ["OPENMC_ENDF_DATA"] + filename = os.path.join(endf_data, "neutrons", "n-098_Cf_252.endf") return openmc.data.IncidentNeutron.from_endf(filename, covariance=True) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def th232(): """Th232 ENDF data (contains RM resonance covariance with LCOMP=2).""" - endf_data = os.environ['OPENMC_ENDF_DATA'] - filename = os.path.join(endf_data, 'neutrons', 'n-090_Th_232.endf') + endf_data = os.environ["OPENMC_ENDF_DATA"] + filename = os.path.join(endf_data, "neutrons", "n-090_Th_232.endf") return openmc.data.IncidentNeutron.from_endf(filename, covariance=True) def test_attributes(pu239): - assert pu239.name == 'Pu239' + assert pu239.name == "Pu239" assert pu239.mass_number == 239 assert pu239.metastable == 0 - assert pu239.atomic_symbol == 'Pu' + assert pu239.atomic_symbol == "Pu" assert pu239.atomic_weight_ratio == pytest.approx(236.9986) def test_fission_energy(pu239): fer = pu239.fission_energy assert isinstance(fer, openmc.data.FissionEnergyRelease) - components = ['betas', 'delayed_neutrons', 'delayed_photons', 'fragments', - 'neutrinos', 'prompt_neutrons', 'prompt_photons', 'recoverable', - 'total', 'q_prompt', 'q_recoverable', 'q_total'] + components = [ + "betas", + "delayed_neutrons", + "delayed_photons", + "fragments", + "neutrinos", + "prompt_neutrons", + "prompt_photons", + "recoverable", + "total", + "q_prompt", + "q_recoverable", + "q_total", + ] for c in components: assert isinstance(getattr(fer, c), Callable) @@ -161,7 +171,7 @@ def test_fission_energy(pu239): def test_energy_grid(pu239): assert isinstance(pu239.energy, Mapping) for temp, grid in pu239.energy.items(): - assert temp.endswith('K') + assert temp.endswith("K") assert np.all(np.diff(grid) >= 0.0) @@ -177,13 +187,13 @@ def test_elastic(pu239): assert elastic.center_of_mass assert elastic.q_value == 0.0 assert elastic.mt == 2 - assert '0K' in elastic.xs - assert '294K' in elastic.xs + assert "0K" in elastic.xs + assert "294K" in elastic.xs assert len(elastic.products) == 1 p = elastic.products[0] assert isinstance(p, openmc.data.Product) - assert p.particle == 'neutron' - assert p.emission_mode == 'prompt' + assert p.particle == "neutron" + assert p.emission_mode == "prompt" assert len(p.distribution) == 1 d = p.distribution[0] assert isinstance(d, openmc.data.UncorrelatedAngleEnergy) @@ -197,25 +207,25 @@ def test_fission(pu239): assert not fission.center_of_mass assert fission.q_value == pytest.approx(198902000.0) assert fission.mt == 18 - assert '294K' in fission.xs + assert "294K" in fission.xs assert len(fission.products) == 8 prompt = fission.products[0] - assert prompt.particle == 'neutron' + assert prompt.particle == "neutron" assert prompt.yield_(1.0e-5) == pytest.approx(2.874262) - delayed = [p for p in fission.products if p.emission_mode == 'delayed'] + delayed = [p for p in fission.products if p.emission_mode == "delayed"] assert len(delayed) == 6 - assert all(d.particle == 'neutron' for d in delayed) + assert all(d.particle == "neutron" for d in delayed) assert sum(d.decay_rate for d in delayed) == pytest.approx(4.037212) assert sum(d.yield_(1.0) for d in delayed) == pytest.approx(0.00645) photon = fission.products[-1] - assert photon.particle == 'photon' + assert photon.particle == "photon" @needs_njoy def test_derived_products(am244): fission = am244.reactions[18] total_neutron = fission.derived_products[0] - assert total_neutron.emission_mode == 'total' + assert total_neutron.emission_mode == "total" assert total_neutron.yield_(6e6) == pytest.approx(4.2558) @@ -235,14 +245,14 @@ def test_kerma(run_in_tmpdir, am244, h2): read_in = openmc.data.IncidentNeutron.from_hdf5("H2.h5") assert 301 in read_in assert 901 in read_in - assert np.all(read_in[901].xs['300K'].y == h2[901].xs['300K'].y) + assert np.all(read_in[901].xs["300K"].y == h2[901].xs["300K"].y) def test_urr(pu239): for T, ptable in pu239.urr.items(): - assert T.endswith('K') + assert T.endswith("K") assert isinstance(ptable, openmc.data.ProbabilityTables) - ptable = pu239.urr['294K'] + ptable = pu239.urr["294K"] assert ptable.absorption_flag == -1 assert ptable.energy[0] == pytest.approx(2500.001) assert ptable.energy[-1] == pytest.approx(29999.99) @@ -262,11 +272,11 @@ def test_get_reaction_components(h2): def test_export_to_hdf5(tmpdir, pu239, gd154): - filename = str(tmpdir.join('pu239.h5')) + filename = str(tmpdir.join("pu239.h5")) pu239.export_to_hdf5(filename) assert os.path.exists(filename) with pytest.raises(NotImplementedError): - gd154.export_to_hdf5('gd154.h5') + gd154.export_to_hdf5("gd154.h5") def test_slbw(xe135): @@ -276,18 +286,18 @@ def test_slbw(xe135): resolved = res.resolved assert isinstance(resolved, openmc.data.SingleLevelBreitWigner) assert resolved.energy_min == pytest.approx(1e-5) - assert resolved.energy_max == pytest.approx(190.) + assert resolved.energy_max == pytest.approx(190.0) assert resolved.target_spin == pytest.approx(1.5) assert isinstance(resolved.parameters, pd.DataFrame) s = resolved.parameters.iloc[0] - assert s['energy'] == pytest.approx(0.084) + assert s["energy"] == pytest.approx(0.084) def test_mlbw(sm150): resolved = sm150.resonances.resolved assert isinstance(resolved, openmc.data.MultiLevelBreitWigner) assert resolved.energy_min == pytest.approx(1e-5) - assert resolved.energy_max == pytest.approx(1570.) + assert resolved.energy_max == pytest.approx(1570.0) assert resolved.target_spin == 0.0 @@ -301,15 +311,15 @@ def test_reichmoore(gd154): assert isinstance(resolved, openmc.data.ReichMoore) assert isinstance(unresolved, openmc.data.Unresolved) assert resolved.energy_min == pytest.approx(1e-5) - assert resolved.energy_max == pytest.approx(2760.) + assert resolved.energy_max == pytest.approx(2760.0) assert resolved.target_spin == 0.0 assert resolved.channel_radius[0](1.0) == pytest.approx(0.74) assert isinstance(resolved.parameters, pd.DataFrame) - assert (resolved.parameters['L'] == 0).all() - assert (resolved.parameters['J'] <= 0.5).all() - assert (resolved.parameters['fissionWidthA'] == 0.0).all() + assert (resolved.parameters["L"] == 0).all() + assert (resolved.parameters["J"] <= 0.5).all() + assert (resolved.parameters["fissionWidthA"] == 0.0).all() - elastic = gd154.reactions[2].xs['0K'] + elastic = gd154.reactions[2].xs["0K"] assert isinstance(elastic, openmc.data.ResonancesWithBackground) @@ -327,16 +337,18 @@ def test_mlbw_cov_lcomp0(cf252): # Testing on first range only cov = cf252.resonance_covariance.ranges[0] res = cf252.resonances.ranges[0] - assert cov.parameters['energy'][0] == pytest.approx(-3.5) - assert res.parameters['energy'][0] == cov.parameters['energy'][0] - assert isinstance(cov, openmc.data.resonance_covariance.MultiLevelBreitWignerCovariance) + assert cov.parameters["energy"][0] == pytest.approx(-3.5) + assert res.parameters["energy"][0] == cov.parameters["energy"][0] + assert isinstance( + cov, openmc.data.resonance_covariance.MultiLevelBreitWignerCovariance + ) assert cov.energy_min == pytest.approx(1e-5) - assert cov.energy_max == pytest.approx(1000.) - assert cov.covariance[0,0] == pytest.approx(1.225e-05) + assert cov.energy_max == pytest.approx(1000.0) + assert cov.covariance[0, 0] == pytest.approx(1.225e-05) - subset = cov.subset('energy', [0, 100]) + subset = cov.subset("energy", [0, 100]) assert not subset.parameters.empty - assert (subset.file2res.parameters['energy'] < 100).all() + assert (subset.file2res.parameters["energy"] < 100).all() samples = cov.sample(1) @@ -344,16 +356,18 @@ def test_mlbw_cov_lcomp1(ti50): # Testing on first range only cov = ti50.resonance_covariance.ranges[0] res = ti50.resonances.ranges[0] - assert cov.parameters['energy'][0] == pytest.approx(-21020.) - assert res.parameters['energy'][0] == cov.parameters['energy'][0] - assert isinstance(cov, openmc.data.resonance_covariance.MultiLevelBreitWignerCovariance) + assert cov.parameters["energy"][0] == pytest.approx(-21020.0) + assert res.parameters["energy"][0] == cov.parameters["energy"][0] + assert isinstance( + cov, openmc.data.resonance_covariance.MultiLevelBreitWignerCovariance + ) assert cov.energy_min == pytest.approx(1e-5) - assert cov.energy_max == pytest.approx(587000.) - assert cov.covariance[0,0] == pytest.approx(1.410177e5) + assert cov.energy_max == pytest.approx(587000.0) + assert cov.covariance[0, 0] == pytest.approx(1.410177e5) - subset = cov.subset('L', [1, 1]) + subset = cov.subset("L", [1, 1]) assert not subset.parameters.empty - assert (subset.file2res.parameters['L'] == 1).all() + assert (subset.file2res.parameters["L"] == 1).all() cov.sample(1) @@ -361,16 +375,18 @@ def test_mlbw_cov_lcomp2(na23): # Testing on first range only cov = na23.resonance_covariance.ranges[0] res = na23.resonances.ranges[0] - assert cov.parameters['energy'][0] == pytest.approx(2810.) - assert res.parameters['energy'][0] == cov.parameters['energy'][0] - assert isinstance(cov, openmc.data.resonance_covariance.MultiLevelBreitWignerCovariance) + assert cov.parameters["energy"][0] == pytest.approx(2810.0) + assert res.parameters["energy"][0] == cov.parameters["energy"][0] + assert isinstance( + cov, openmc.data.resonance_covariance.MultiLevelBreitWignerCovariance + ) assert cov.energy_min == pytest.approx(600) - assert cov.energy_max == pytest.approx(500000.) - assert cov.covariance[0,0] == pytest.approx(16.1064163584) + assert cov.energy_max == pytest.approx(500000.0) + assert cov.covariance[0, 0] == pytest.approx(16.1064163584) - subset = cov.subset('L', [1, 1]) + subset = cov.subset("L", [1, 1]) assert not subset.parameters.empty - assert (subset.file2res.parameters['L'] == 1).all() + assert (subset.file2res.parameters["L"] == 1).all() cov.sample(1) @@ -378,16 +394,16 @@ def test_rmcov_lcomp1(gd154): # Testing on first range only cov = gd154.resonance_covariance.ranges[0] res = gd154.resonances.ranges[0] - assert cov.parameters['energy'][0] == pytest.approx(-2.200001) - assert res.parameters['energy'][0] == cov.parameters['energy'][0] + assert cov.parameters["energy"][0] == pytest.approx(-2.200001) + assert res.parameters["energy"][0] == cov.parameters["energy"][0] assert isinstance(cov, openmc.data.resonance_covariance.ReichMooreCovariance) assert cov.energy_min == pytest.approx(1e-5) - assert cov.energy_max == pytest.approx(2760.) - assert cov.covariance[0,0] == pytest.approx(0.8895997) + assert cov.energy_max == pytest.approx(2760.0) + assert cov.covariance[0, 0] == pytest.approx(0.8895997) - subset = cov.subset('energy', [0, 100]) + subset = cov.subset("energy", [0, 100]) assert not subset.parameters.empty - assert (subset.file2res.parameters['energy'] < 100).all() + assert (subset.file2res.parameters["energy"] < 100).all() cov.sample(1) @@ -395,16 +411,16 @@ def test_rmcov_lcomp2(th232): # Testing on first range only cov = th232.resonance_covariance.ranges[0] res = th232.resonances.ranges[0] - assert cov.parameters['energy'][0] == pytest.approx(-2000) - assert res.parameters['energy'][0] == cov.parameters['energy'][0] + assert cov.parameters["energy"][0] == pytest.approx(-2000) + assert res.parameters["energy"][0] == cov.parameters["energy"][0] assert isinstance(cov, openmc.data.resonance_covariance.ReichMooreCovariance) assert cov.energy_min == pytest.approx(1e-5) - assert cov.energy_max == pytest.approx(4000.) - assert cov.covariance[0,0] == pytest.approx(246.6043092496) + assert cov.energy_max == pytest.approx(4000.0) + assert cov.covariance[0, 0] == pytest.approx(246.6043092496) - subset = cov.subset('energy', [0, 100]) + subset = cov.subset("energy", [0, 100]) assert not subset.parameters.empty - assert (subset.file2res.parameters['energy'] < 100).all() + assert (subset.file2res.parameters["energy"] < 100).all() cov.sample(1) @@ -444,22 +460,22 @@ def test_laboratory(be9): assert isinstance(dist, openmc.data.LaboratoryAngleEnergy) assert list(dist.breakpoints) == [18] assert list(dist.interpolation) == [2] - assert dist.energy[0] == pytest.approx(1748830.) - assert dist.energy[-1] == pytest.approx(20.e6) + assert dist.energy[0] == pytest.approx(1748830.0) + assert dist.energy[-1] == pytest.approx(20.0e6) assert len(dist.energy) == len(dist.energy_out) == len(dist.mu) for eout, mu in zip(dist.energy_out, dist.mu): assert len(eout) == len(mu) - assert np.all((-1. <= mu.x) & (mu.x <= 1.)) + assert np.all((-1.0 <= mu.x) & (mu.x <= 1.0)) @needs_njoy def test_correlated(tmpdir): - endf_data = os.environ['OPENMC_ENDF_DATA'] - endf_file = os.path.join(endf_data, 'neutrons', 'n-014_Si_030.endf') + endf_data = os.environ["OPENMC_ENDF_DATA"] + endf_file = os.path.join(endf_data, "neutrons", "n-014_Si_030.endf") si30 = openmc.data.IncidentNeutron.from_njoy(endf_file, heatr=False) # Convert to HDF5 and read back - filename = str(tmpdir.join('si30.h5')) + filename = str(tmpdir.join("si30.h5")) si30.export_to_hdf5(filename) si30_copy = openmc.data.IncidentNeutron.from_hdf5(filename) @@ -467,7 +483,7 @@ def test_correlated(tmpdir): @needs_njoy def test_nbody(tmpdir, h2): # Convert to HDF5 and read back - filename = str(tmpdir.join('h2.h5')) + filename = str(tmpdir.join("h2.h5")) h2.export_to_hdf5(filename) h2_copy = openmc.data.IncidentNeutron.from_hdf5(filename) @@ -481,10 +497,10 @@ def test_nbody(tmpdir, h2): @needs_njoy def test_ace_convert(run_in_tmpdir): - endf_data = os.environ['OPENMC_ENDF_DATA'] - filename = os.path.join(endf_data, 'neutrons', 'n-001_H_001.endf') - ace_ascii = 'ace_ascii' - ace_binary = 'ace_binary' + endf_data = os.environ["OPENMC_ENDF_DATA"] + filename = os.path.join(endf_data, "neutrons", "n-001_H_001.endf") + ace_ascii = "ace_ascii" + ace_binary = "ace_binary" openmc.data.njoy.make_ace(filename, acer=ace_ascii) # Convert to binary @@ -505,19 +521,19 @@ def test_ace_convert(run_in_tmpdir): def test_ace_table_types(): TT = openmc.data.ace.TableType - assert TT.from_suffix('c') == TT.NEUTRON_CONTINUOUS - assert TT.from_suffix('nc') == TT.NEUTRON_CONTINUOUS - assert TT.from_suffix('80c') == TT.NEUTRON_CONTINUOUS - assert TT.from_suffix('t') == TT.THERMAL_SCATTERING - assert TT.from_suffix('20t') == TT.THERMAL_SCATTERING + assert TT.from_suffix("c") == TT.NEUTRON_CONTINUOUS + assert TT.from_suffix("nc") == TT.NEUTRON_CONTINUOUS + assert TT.from_suffix("80c") == TT.NEUTRON_CONTINUOUS + assert TT.from_suffix("t") == TT.THERMAL_SCATTERING + assert TT.from_suffix("20t") == TT.THERMAL_SCATTERING with pytest.raises(ValueError): - TT.from_suffix('z') + TT.from_suffix("z") @needs_njoy def test_high_temperature(): - endf_data = os.environ['OPENMC_ENDF_DATA'] - endf_file = os.path.join(endf_data, 'neutrons', 'n-001_H_001.endf') + endf_data = os.environ["OPENMC_ENDF_DATA"] + endf_file = os.path.join(endf_data, "neutrons", "n-001_H_001.endf") # Ensure that from_njoy works when given a high temperature openmc.data.IncidentNeutron.from_njoy(endf_file, temperatures=[123_456.0]) diff --git a/tests/unit_tests/test_data_photon.py b/tests/unit_tests/test_data_photon.py index 010ac1f353e..058f64dd327 100644 --- a/tests/unit_tests/test_data_photon.py +++ b/tests/unit_tests/test_data_photon.py @@ -8,17 +8,17 @@ import openmc.data -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def elements_endf(): """Dictionary of element ENDF data indexed by atomic symbol.""" - endf_data = os.environ['OPENMC_ENDF_DATA'] - elements = {'H': 1, 'O': 8, 'Al': 13, 'Cu': 29, 'Ag': 47, 'U': 92, 'Pu': 94} + endf_data = os.environ["OPENMC_ENDF_DATA"] + elements = {"H": 1, "O": 8, "Al": 13, "Cu": 29, "Ag": 47, "U": 92, "Pu": 94} data = {} for symbol, Z in elements.items(): - p_file = 'photoat-{:03}_{}_000.endf'.format(Z, symbol) - p_path = os.path.join(endf_data, 'photoat', p_file) - a_file = 'atom-{:03}_{}_000.endf'.format(Z, symbol) - a_path = os.path.join(endf_data, 'atomic_relax', a_file) + p_file = "photoat-{:03}_{}_000.endf".format(Z, symbol) + p_path = os.path.join(endf_data, "photoat", p_file) + a_file = "atom-{:03}_{}_000.endf".format(Z, symbol) + a_path = os.path.join(endf_data, "atomic_relax", a_file) data[symbol] = openmc.data.IncidentPhoton.from_endf(p_path, a_path) return data @@ -30,24 +30,16 @@ def element(request, elements_endf): @pytest.mark.parametrize( - 'element, atomic_number', [ - ('Al', 13), - ('Cu', 29), - ('Pu', 94) - ], - indirect=['element'] + "element, atomic_number", [("Al", 13), ("Cu", 29), ("Pu", 94)], indirect=["element"] ) def test_attributes(element, atomic_number): assert element.atomic_number == atomic_number @pytest.mark.parametrize( - 'element, subshell, binding_energy, num_electrons', [ - ('H', 'K', 13.61, 1.0), - ('O', 'L3', 14.15, 2.67), - ('U', 'P2', 34.09, 2.0) - ], - indirect=['element'] + "element, subshell, binding_energy, num_electrons", + [("H", "K", 13.61, 1.0), ("O", "L3", 14.15, 2.67), ("U", "P2", 34.09, 2.0)], + indirect=["element"], ) def test_atomic_relaxation(element, subshell, binding_energy, num_electrons): atom_relax = element.atomic_relaxation @@ -57,7 +49,7 @@ def test_atomic_relaxation(element, subshell, binding_energy, num_electrons): assert atom_relax.num_electrons[subshell] == num_electrons -@pytest.mark.parametrize('element', ['Al', 'Cu', 'Pu'], indirect=True) +@pytest.mark.parametrize("element", ["Al", "Cu", "Pu"], indirect=True) def test_transitions(element): transitions = element.atomic_relaxation.transitions assert transitions @@ -65,53 +57,40 @@ def test_transitions(element): for matrix in transitions.values(): assert isinstance(matrix, pd.core.frame.DataFrame) assert len(matrix.columns) == 4 - assert sum(matrix['probability']) == pytest.approx(1.0) + assert sum(matrix["probability"]) == pytest.approx(1.0) @pytest.mark.parametrize( - 'element, I, i_shell, ionization_energy, num_electrons', [ - ('H', 19.2, 0, 13.6, 1), - ('O', 95.0, 2, 13.62, 4), - ('U', 890.0, 25, 6.033, -3) - ], - indirect=['element'] + "element, I, i_shell, ionization_energy, num_electrons", + [("H", 19.2, 0, 13.6, 1), ("O", 95.0, 2, 13.62, 4), ("U", 890.0, 25, 6.033, -3)], + indirect=["element"], ) def test_bremsstrahlung(element, I, i_shell, ionization_energy, num_electrons): brems = element.bremsstrahlung assert isinstance(brems, Mapping) - assert brems['I'] == I - assert brems['num_electrons'][i_shell] == num_electrons - assert brems['ionization_energy'][i_shell] == ionization_energy - assert np.all(np.diff(brems['electron_energy']) > 0.0) - assert np.all(np.diff(brems['photon_energy']) > 0.0) - assert brems['photon_energy'][0] == 0.0 - assert brems['photon_energy'][-1] == 1.0 - assert brems['dcs'].shape == (200, 30) + assert brems["I"] == I + assert brems["num_electrons"][i_shell] == num_electrons + assert brems["ionization_energy"][i_shell] == ionization_energy + assert np.all(np.diff(brems["electron_energy"]) > 0.0) + assert np.all(np.diff(brems["photon_energy"]) > 0.0) + assert brems["photon_energy"][0] == 0.0 + assert brems["photon_energy"][-1] == 1.0 + assert brems["dcs"].shape == (200, 30) @pytest.mark.parametrize( - 'element, n_shell', [ - ('H', 1), - ('O', 3), - ('Al', 5) - ], - indirect=['element'] + "element, n_shell", [("H", 1), ("O", 3), ("Al", 5)], indirect=["element"] ) def test_compton_profiles(element, n_shell): profile = element.compton_profiles assert profile assert isinstance(profile, Mapping) - assert all(isinstance(x, Callable) for x in profile['J']) + assert all(isinstance(x, Callable) for x in profile["J"]) assert all(len(x) == n_shell for x in profile.values()) @pytest.mark.parametrize( - 'element, reaction', [ - ('Cu', 541), - ('Ag', 502), - ('Pu', 504) - ], - indirect=['element'] + "element, reaction", [("Cu", 541), ("Ag", 502), ("Pu", 504)], indirect=["element"] ) def test_reactions(element, reaction): reactions = element.reactions @@ -121,9 +100,9 @@ def test_reactions(element, reaction): reactions[18] -@pytest.mark.parametrize('element', ['Pu'], indirect=True) +@pytest.mark.parametrize("element", ["Pu"], indirect=True) def test_export_to_hdf5(tmpdir, element): - filename = str(tmpdir.join('tmp.h5')) + filename = str(tmpdir.join("tmp.h5")) element.export_to_hdf5(filename) assert os.path.exists(filename) # Read in data from hdf5 @@ -135,18 +114,23 @@ def test_export_to_hdf5(tmpdir, element): xs2 = element2[mt].xs(energy) assert np.allclose(xs, xs2) assert element[502].scattering_factor == element2[502].scattering_factor - assert element.atomic_relaxation.transitions['O3'].equals( - element2.atomic_relaxation.transitions['O3']) - assert (element.compton_profiles['binding_energy'] == - element2.compton_profiles['binding_energy']).all() - assert (element.bremsstrahlung['electron_energy'] == - element2.bremsstrahlung['electron_energy']).all() + assert element.atomic_relaxation.transitions["O3"].equals( + element2.atomic_relaxation.transitions["O3"] + ) + assert ( + element.compton_profiles["binding_energy"] + == element2.compton_profiles["binding_energy"] + ).all() + assert ( + element.bremsstrahlung["electron_energy"] + == element2.bremsstrahlung["electron_energy"] + ).all() # Export to hdf5 again - element2.export_to_hdf5(filename, 'w') + element2.export_to_hdf5(filename, "w") def test_photodat_only(run_in_tmpdir): - endf_dir = Path(os.environ['OPENMC_ENDF_DATA']) - photoatomic_file = endf_dir / 'photoat' / 'photoat-001_H_000.endf' + endf_dir = Path(os.environ["OPENMC_ENDF_DATA"]) + photoatomic_file = endf_dir / "photoat" / "photoat-001_H_000.endf" data = openmc.data.IncidentPhoton.from_endf(photoatomic_file) - data.export_to_hdf5('tmp.h5', 'w') + data.export_to_hdf5("tmp.h5", "w") diff --git a/tests/unit_tests/test_data_thermal.py b/tests/unit_tests/test_data_thermal.py index 9f5a8cb4046..eaf821265cf 100644 --- a/tests/unit_tests/test_data_thermal.py +++ b/tests/unit_tests/test_data_thermal.py @@ -10,46 +10,47 @@ from . import needs_njoy -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def h2o(): """H in H2O thermal scattering data.""" - directory = os.path.dirname(os.environ['OPENMC_CROSS_SECTIONS']) - filename = os.path.join(directory, 'c_H_in_H2O.h5') + directory = os.path.dirname(os.environ["OPENMC_CROSS_SECTIONS"]) + filename = os.path.join(directory, "c_H_in_H2O.h5") return openmc.data.ThermalScattering.from_hdf5(filename) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def graphite(): """Graphite thermal scattering data.""" - directory = os.path.dirname(os.environ['OPENMC_CROSS_SECTIONS']) - filename = os.path.join(directory, 'c_Graphite.h5') + directory = os.path.dirname(os.environ["OPENMC_CROSS_SECTIONS"]) + filename = os.path.join(directory, "c_Graphite.h5") return openmc.data.ThermalScattering.from_hdf5(filename) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def h2o_njoy(): """H in H2O generated using NJOY.""" - endf_data = os.environ['OPENMC_ENDF_DATA'] - path_h1 = os.path.join(endf_data, 'neutrons', 'n-001_H_001.endf') - path_h2o = os.path.join(endf_data, 'thermal_scatt', 'tsl-HinH2O.endf') + endf_data = os.environ["OPENMC_ENDF_DATA"] + path_h1 = os.path.join(endf_data, "neutrons", "n-001_H_001.endf") + path_h2o = os.path.join(endf_data, "thermal_scatt", "tsl-HinH2O.endf") return openmc.data.ThermalScattering.from_njoy( - path_h1, path_h2o, temperatures=[293.6, 500.0]) + path_h1, path_h2o, temperatures=[293.6, 500.0] + ) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def hzrh(): """H in ZrH thermal scattering data.""" - endf_data = os.environ['OPENMC_ENDF_DATA'] - filename = os.path.join(endf_data, 'thermal_scatt', 'tsl-HinZrH.endf') + endf_data = os.environ["OPENMC_ENDF_DATA"] + filename = os.path.join(endf_data, "thermal_scatt", "tsl-HinZrH.endf") return openmc.data.ThermalScattering.from_endf(filename) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def hzrh_njoy(): """H in ZrH generated using NJOY.""" - endf_data = os.environ['OPENMC_ENDF_DATA'] - path_h1 = os.path.join(endf_data, 'neutrons', 'n-001_H_001.endf') - path_hzrh = os.path.join(endf_data, 'thermal_scatt', 'tsl-HinZrH.endf') + endf_data = os.environ["OPENMC_ENDF_DATA"] + path_h1 = os.path.join(endf_data, "neutrons", "n-001_H_001.endf") + path_hzrh = os.path.join(endf_data, "thermal_scatt", "tsl-HinZrH.endf") with_endf_data = openmc.data.ThermalScattering.from_njoy( path_h1, path_hzrh, temperatures=[296.0], iwt=0 ) @@ -59,18 +60,18 @@ def hzrh_njoy(): return with_endf_data, without_endf_data -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def sio2(): """SiO2 thermal scattering data.""" - endf_data = os.environ['OPENMC_ENDF_DATA'] - filename = os.path.join(endf_data, 'thermal_scatt', 'tsl-SiO2.endf') + endf_data = os.environ["OPENMC_ENDF_DATA"] + filename = os.path.join(endf_data, "thermal_scatt", "tsl-SiO2.endf") return openmc.data.ThermalScattering.from_endf(filename) def test_h2o_attributes(h2o): - assert h2o.name == 'c_H_in_H2O' - assert h2o.nuclides == ['H1'] - assert h2o.temperatures == ['294K'] + assert h2o.name == "c_H_in_H2O" + assert h2o.nuclides == ["H1"] + assert h2o.temperatures == ["294K"] assert h2o.atomic_weight_ratio == pytest.approx(0.999167) assert h2o.energy_max == pytest.approx(4.46) assert isinstance(repr(h2o), str) @@ -79,102 +80,121 @@ def test_h2o_attributes(h2o): def test_h2o_xs(h2o): assert not h2o.elastic for temperature, func in h2o.inelastic.xs.items(): - assert temperature.endswith('K') + assert temperature.endswith("K") assert isinstance(func, Callable) def test_graphite_attributes(graphite): - assert graphite.name == 'c_Graphite' - assert graphite.nuclides == ['C0', 'C12', 'C13'] - assert graphite.temperatures == ['296K'] + assert graphite.name == "c_Graphite" + assert graphite.nuclides == ["C0", "C12", "C13"] + assert graphite.temperatures == ["296K"] assert graphite.atomic_weight_ratio == pytest.approx(11.898) assert graphite.energy_max == pytest.approx(4.46) def test_graphite_xs(graphite): for temperature, func in graphite.elastic.xs.items(): - assert temperature.endswith('K') + assert temperature.endswith("K") assert isinstance(func, openmc.data.CoherentElastic) for temperature, func in graphite.inelastic.xs.items(): - assert temperature.endswith('K') + assert temperature.endswith("K") assert isinstance(func, Callable) - elastic = graphite.elastic.xs['296K'] + elastic = graphite.elastic.xs["296K"] assert elastic([1e-3, 1.0]) == pytest.approx([0.0, 0.62586153]) + @needs_njoy def test_graphite_njoy(): - endf_data = os.environ['OPENMC_ENDF_DATA'] - path_c0 = os.path.join(endf_data, 'neutrons', 'n-006_C_000.endf') - path_gr = os.path.join(endf_data, 'thermal_scatt', 'tsl-graphite.endf') + endf_data = os.environ["OPENMC_ENDF_DATA"] + path_c0 = os.path.join(endf_data, "neutrons", "n-006_C_000.endf") + path_gr = os.path.join(endf_data, "thermal_scatt", "tsl-graphite.endf") graphite = openmc.data.ThermalScattering.from_njoy( - path_c0, path_gr, temperatures=[296.0]) - assert graphite.nuclides == ['C0', 'C12', 'C13'] + path_c0, path_gr, temperatures=[296.0] + ) + assert graphite.nuclides == ["C0", "C12", "C13"] assert graphite.atomic_weight_ratio == pytest.approx(11.898) assert graphite.energy_max == pytest.approx(2.02) - assert graphite.temperatures == ['296K'] + assert graphite.temperatures == ["296K"] @needs_njoy def test_export_to_hdf5(tmpdir, h2o_njoy, hzrh_njoy, graphite): - filename = str(tmpdir.join('water.h5')) + filename = str(tmpdir.join("water.h5")) h2o_njoy.export_to_hdf5(filename) assert os.path.exists(filename) # Graphite covers export of coherent elastic data - filename = str(tmpdir.join('graphite.h5')) + filename = str(tmpdir.join("graphite.h5")) graphite.export_to_hdf5(filename) assert os.path.exists(filename) # H in ZrH covers export of incoherent elastic data, and incoherent # inelastic angle-energy distributions - filename = str(tmpdir.join('hzrh.h5')) + filename = str(tmpdir.join("hzrh.h5")) hzrh_njoy[0].export_to_hdf5(filename) assert os.path.exists(filename) - hzrh_njoy[1].export_to_hdf5(filename, 'w') + hzrh_njoy[1].export_to_hdf5(filename, "w") assert os.path.exists(filename) @needs_njoy def test_continuous_dist(h2o_njoy): for temperature, dist in h2o_njoy.inelastic.distribution.items(): - assert temperature.endswith('K') + assert temperature.endswith("K") assert isinstance(dist, openmc.data.IncoherentInelasticAE) def test_h2o_endf(): - endf_data = os.environ['OPENMC_ENDF_DATA'] - filename = os.path.join(endf_data, 'thermal_scatt', 'tsl-HinH2O.endf') + endf_data = os.environ["OPENMC_ENDF_DATA"] + filename = os.path.join(endf_data, "thermal_scatt", "tsl-HinH2O.endf") h2o = openmc.data.ThermalScattering.from_endf(filename) assert not h2o.elastic assert h2o.atomic_weight_ratio == pytest.approx(0.99917) assert h2o.energy_max == pytest.approx(3.99993) - assert h2o.temperatures == ['294K', '350K', '400K', '450K', '500K', '550K', - '600K', '650K', '800K'] + assert h2o.temperatures == [ + "294K", + "350K", + "400K", + "450K", + "500K", + "550K", + "600K", + "650K", + "800K", + ] def test_hzrh_attributes(hzrh): assert hzrh.atomic_weight_ratio == pytest.approx(0.99917) assert hzrh.energy_max == pytest.approx(1.9734) - assert hzrh.temperatures == ['296K', '400K', '500K', '600K', '700K', '800K', - '1000K', '1200K'] + assert hzrh.temperatures == [ + "296K", + "400K", + "500K", + "600K", + "700K", + "800K", + "1000K", + "1200K", + ] def test_hzrh_elastic(hzrh): rx = hzrh.elastic for temperature, func in rx.xs.items(): - assert temperature.endswith('K') + assert temperature.endswith("K") assert isinstance(func, openmc.data.IncoherentElastic) - xs = rx.xs['296K'] + xs = rx.xs["296K"] sig_b, W = xs.bound_xs, xs.debye_waller assert sig_b == pytest.approx(81.98006) assert W == pytest.approx(8.486993) for i in range(10): E = random.uniform(0.0, hzrh.energy_max) - assert xs(E) == pytest.approx(sig_b/2 * ((1 - exp(-4*E*W))/(2*E*W))) + assert xs(E) == pytest.approx(sig_b / 2 * ((1 - exp(-4 * E * W)) / (2 * E * W))) for temperature, dist in rx.distribution.items(): - assert temperature.endswith('K') + assert temperature.endswith("K") assert dist.debye_waller > 0.0 @@ -185,7 +205,7 @@ def test_hzrh_njoy(hzrh_njoy): # First check version using ENDF incoherent elastic data assert endf.atomic_weight_ratio == pytest.approx(0.999167) assert endf.energy_max == pytest.approx(1.855) - assert endf.temperatures == ['296K'] + assert endf.temperatures == ["296K"] # Now check version using ACE incoherent elastic data (discretized) assert ace.atomic_weight_ratio == endf.atomic_weight_ratio @@ -193,34 +213,41 @@ def test_hzrh_njoy(hzrh_njoy): # Cross sections should be about the same (within 1%) E = np.linspace(1e-5, endf.energy_max) - xs1 = endf.elastic.xs['296K'](E) - xs2 = ace.elastic.xs['296K'](E) + xs1 = endf.elastic.xs["296K"](E) + xs2 = ace.elastic.xs["296K"](E) assert xs1 == pytest.approx(xs2, rel=0.01) # Check discrete incoherent elastic distribution - d = ace.elastic.distribution['296K'] + d = ace.elastic.distribution["296K"] assert np.all((-1.0 <= d.mu_out) & (d.mu_out <= 1.0)) # Check discrete incoherent inelastic distribution - d = endf.inelastic.distribution['296K'] + d = endf.inelastic.distribution["296K"] assert d.skewed assert np.all((-1.0 <= d.mu_out) & (d.mu_out <= 1.0)) - assert np.all((0.0 <= d.energy_out) & (d.energy_out < 3*endf.energy_max)) + assert np.all((0.0 <= d.energy_out) & (d.energy_out < 3 * endf.energy_max)) def test_sio2_attributes(sio2): assert sio2.atomic_weight_ratio == pytest.approx(27.84423) assert sio2.energy_max == pytest.approx(2.46675) - assert sio2.temperatures == ['294K', '350K', '400K', '500K', '800K', - '1000K', '1200K'] + assert sio2.temperatures == [ + "294K", + "350K", + "400K", + "500K", + "800K", + "1000K", + "1200K", + ] def test_sio2_elastic(sio2): rx = sio2.elastic for temperature, func in rx.xs.items(): - assert temperature.endswith('K') + assert temperature.endswith("K") assert isinstance(func, openmc.data.CoherentElastic) - xs = rx.xs['294K'] + xs = rx.xs["294K"] assert len(xs) == 317 assert xs.bragg_edges[0] == pytest.approx(0.000711634) assert xs.factors[0] == pytest.approx(2.6958e-14) @@ -240,78 +267,198 @@ def test_sio2_elastic(sio2): assert xs(E) == pytest.approx(P / E) for temperature, dist in rx.distribution.items(): - assert temperature.endswith('K') + assert temperature.endswith("K") assert dist.coherent_xs is rx.xs[temperature] def test_get_thermal_name(): f = openmc.data.get_thermal_name # Names which are recognized - assert f('lwtr') == 'c_H_in_H2O' - assert f('hh2o') == 'c_H_in_H2O' + assert f("lwtr") == "c_H_in_H2O" + assert f("hh2o") == "c_H_in_H2O" - with pytest.warns(UserWarning, match='is not recognized'): + with pytest.warns(UserWarning, match="is not recognized"): # Names which can be guessed - assert f('lw00') == 'c_H_in_H2O' - assert f('graphite') == 'c_Graphite' - assert f('D_in_D2O') == 'c_D_in_D2O' + assert f("lw00") == "c_H_in_H2O" + assert f("graphite") == "c_Graphite" + assert f("D_in_D2O") == "c_D_in_D2O" # Not in values, but very close - assert f('hluci') == 'c_H_in_C5O2H8' - assert f('ortho_d') == 'c_ortho_D' + assert f("hluci") == "c_H_in_C5O2H8" + assert f("ortho_d") == "c_ortho_D" # Names that don't remotely match anything - assert f('boogie_monster') == 'c_boogie_monster' + assert f("boogie_monster") == "c_boogie_monster" @pytest.fixture def fake_mixed_elastic(): fake_tsl = openmc.data.ThermalScattering("c_D_in_7LiD", 1.9968, 4.9, [0.0253]) - fake_tsl.nuclides = ['H2'] + fake_tsl.nuclides = ["H2"] # Create elastic reaction - bragg_edges = [0.00370672, 0.00494229, 0.00988458, 0.01359131, 0.01482688, - 0.01976918, 0.02347589, 0.02471147, 0.02965376, 0.03336048, - 0.03953834, 0.04324506, 0.04448063, 0.04942292, 0.05312964, - 0.05436522, 0.05930751, 0.06301423, 0.0642498 , 0.06919209, - 0.07289881, 0.07907667, 0.08278339, 0.08401896, 0.08896126, - 0.09266798, 0.09390355, 0.09884584, 0.1025526 , 0.1037882 , - 0.1087305 , 0.1124372 , 0.1186151 , 0.1223218 , 0.1235574 , - 0.1284997 , 0.1322064 , 0.133442 , 0.142091 , 0.1433266 , - 0.1482688 , 0.1519756 , 0.1581534 , 0.1618601 , 0.1630957 , - 0.168038 , 0.1717447 , 0.1729803 , 0.1779226 , 0.1816293 , - 0.1828649 , 0.1878072 , 0.1915139 , 0.1976918 , 0.2026341 , - 0.2075763 , 0.2125186 , 0.2174609 , 0.2224032 , 0.2273455 , - 0.2421724 , 0.2471147 , 0.252057 , 0.2569993 , 0.2619415 , - 0.2668838 , 0.2767684 , 0.2817107 , 0.2915953 , 0.3064222 , - 0.3261913 , 0.366965] - factors = [0.00375735, 0.01386287, 0.02595574, 0.02992438, 0.03549502, - 0.03855745, 0.04058831, 0.04986305, 0.05703106, 0.05855471, - 0.06078031, 0.06212291, 0.06656602, 0.06930339, 0.0697072 , - 0.07201456, 0.07263853, 0.07313129, 0.07465531, 0.07714482, - 0.07759976, 0.077809 , 0.07790282, 0.07927957, 0.08013058, - 0.08026637, 0.08073475, 0.08112202, 0.08123039, 0.08187171, - 0.08213756, 0.08218236, 0.08236572, 0.08240729, 0.08259795, - 0.08297893, 0.08300455, 0.08314566, 0.08315611, 0.08337715, - 0.08350026, 0.08350663, 0.08352815, 0.08353776, 0.0836098 , - 0.08367017, 0.08367361, 0.0837242 , 0.08375069, 0.08375227, - 0.08377006, 0.08381488, 0.08381644, 0.08382698, 0.08386266, - 0.08387756, 0.08388445, 0.08388974, 0.08390341, 0.08391088, - 0.08391695, 0.08392361, 0.08392684, 0.08392818, 0.08393161, - 0.08393546, 0.08393685, 0.08393801, 0.08393976, 0.08394167, - 0.08394288, 0.08394398] + bragg_edges = [ + 0.00370672, + 0.00494229, + 0.00988458, + 0.01359131, + 0.01482688, + 0.01976918, + 0.02347589, + 0.02471147, + 0.02965376, + 0.03336048, + 0.03953834, + 0.04324506, + 0.04448063, + 0.04942292, + 0.05312964, + 0.05436522, + 0.05930751, + 0.06301423, + 0.0642498, + 0.06919209, + 0.07289881, + 0.07907667, + 0.08278339, + 0.08401896, + 0.08896126, + 0.09266798, + 0.09390355, + 0.09884584, + 0.1025526, + 0.1037882, + 0.1087305, + 0.1124372, + 0.1186151, + 0.1223218, + 0.1235574, + 0.1284997, + 0.1322064, + 0.133442, + 0.142091, + 0.1433266, + 0.1482688, + 0.1519756, + 0.1581534, + 0.1618601, + 0.1630957, + 0.168038, + 0.1717447, + 0.1729803, + 0.1779226, + 0.1816293, + 0.1828649, + 0.1878072, + 0.1915139, + 0.1976918, + 0.2026341, + 0.2075763, + 0.2125186, + 0.2174609, + 0.2224032, + 0.2273455, + 0.2421724, + 0.2471147, + 0.252057, + 0.2569993, + 0.2619415, + 0.2668838, + 0.2767684, + 0.2817107, + 0.2915953, + 0.3064222, + 0.3261913, + 0.366965, + ] + factors = [ + 0.00375735, + 0.01386287, + 0.02595574, + 0.02992438, + 0.03549502, + 0.03855745, + 0.04058831, + 0.04986305, + 0.05703106, + 0.05855471, + 0.06078031, + 0.06212291, + 0.06656602, + 0.06930339, + 0.0697072, + 0.07201456, + 0.07263853, + 0.07313129, + 0.07465531, + 0.07714482, + 0.07759976, + 0.077809, + 0.07790282, + 0.07927957, + 0.08013058, + 0.08026637, + 0.08073475, + 0.08112202, + 0.08123039, + 0.08187171, + 0.08213756, + 0.08218236, + 0.08236572, + 0.08240729, + 0.08259795, + 0.08297893, + 0.08300455, + 0.08314566, + 0.08315611, + 0.08337715, + 0.08350026, + 0.08350663, + 0.08352815, + 0.08353776, + 0.0836098, + 0.08367017, + 0.08367361, + 0.0837242, + 0.08375069, + 0.08375227, + 0.08377006, + 0.08381488, + 0.08381644, + 0.08382698, + 0.08386266, + 0.08387756, + 0.08388445, + 0.08388974, + 0.08390341, + 0.08391088, + 0.08391695, + 0.08392361, + 0.08392684, + 0.08392818, + 0.08393161, + 0.08393546, + 0.08393685, + 0.08393801, + 0.08393976, + 0.08394167, + 0.08394288, + 0.08394398, + ] coherent_xs = openmc.data.CoherentElastic(bragg_edges, factors) - incoherent_xs = openmc.data.Tabulated1D([0.00370672, 0.00370672], [0.00370672, 0.00370672]) - elastic_xs = {'294K': openmc.data.Sum((coherent_xs, incoherent_xs))} + incoherent_xs = openmc.data.Tabulated1D( + [0.00370672, 0.00370672], [0.00370672, 0.00370672] + ) + elastic_xs = {"294K": openmc.data.Sum((coherent_xs, incoherent_xs))} coherent_dist = openmc.data.CoherentElasticAE(coherent_xs) - incoherent_dist = openmc.data.IncoherentElasticAEDiscrete([ - [-0.6, -0.18, 0.18, 0.6], [-0.6, -0.18, 0.18, 0.6] - ]) - elastic_dist = {'294K': openmc.data.MixedElasticAE(coherent_dist, incoherent_dist)} + incoherent_dist = openmc.data.IncoherentElasticAEDiscrete( + [[-0.6, -0.18, 0.18, 0.6], [-0.6, -0.18, 0.18, 0.6]] + ) + elastic_dist = {"294K": openmc.data.MixedElasticAE(coherent_dist, incoherent_dist)} fake_tsl.elastic = openmc.data.ThermalScatteringReaction(elastic_xs, elastic_dist) # Create inelastic reaction - inelastic_xs = {'294K': openmc.data.Tabulated1D([1.0e-5, 4.9], [13.4, 3.35])} + inelastic_xs = {"294K": openmc.data.Tabulated1D([1.0e-5, 4.9], [13.4, 3.35])} breakpoints = [3] interpolation = [2] energy = [1.0e-5, 4.3e-2, 4.9] @@ -323,11 +470,16 @@ def fake_mixed_elastic(): for eout in energy_out: eout.normalize() eout.c = eout.cdf() - discrete = openmc.stats.Discrete([-0.9, -0.6, -0.3, -0.1, 0.1, 0.3, 0.6, 0.9], [1/8]*8) + discrete = openmc.stats.Discrete( + [-0.9, -0.6, -0.3, -0.1, 0.1, 0.3, 0.6, 0.9], [1 / 8] * 8 + ) discrete.c = discrete.cdf()[1:] - mu = [[discrete]*4]*3 - inelastic_dist = {'294K': openmc.data.IncoherentInelasticAE( - breakpoints, interpolation, energy, energy_out, mu)} + mu = [[discrete] * 4] * 3 + inelastic_dist = { + "294K": openmc.data.IncoherentInelasticAE( + breakpoints, interpolation, energy, energy_out, mu + ) + } inelastic = openmc.data.ThermalScatteringReaction(inelastic_xs, inelastic_dist) fake_tsl.inelastic = inelastic @@ -337,23 +489,23 @@ def fake_mixed_elastic(): def test_mixed_elastic(fake_mixed_elastic, run_in_tmpdir): # Write data to HDF5 and then read back original = fake_mixed_elastic - original.export_to_hdf5('c_D_in_7LiD.h5') - copy = openmc.data.ThermalScattering.from_hdf5('c_D_in_7LiD.h5') + original.export_to_hdf5("c_D_in_7LiD.h5") + copy = openmc.data.ThermalScattering.from_hdf5("c_D_in_7LiD.h5") # Make sure data did not change as a result of HDF5 writing/reading assert original == copy # Create modified cross_sections.xml file that includes the above data xs = openmc.data.DataLibrary.from_xml() - xs.register_file('c_D_in_7LiD.h5') - xs.export_to_xml('cross_sections_mixed.xml') + xs.register_file("c_D_in_7LiD.h5") + xs.export_to_xml("cross_sections_mixed.xml") # Create a minimal model that includes the new data and run it mat = openmc.Material() - mat.add_nuclide('H2', 1.0) - mat.add_nuclide('Li7', 1.0) - mat.set_density('g/cm3', 1.0) - mat.add_s_alpha_beta('c_D_in_7LiD') + mat.add_nuclide("H2", 1.0) + mat.add_nuclide("Li7", 1.0) + mat.set_density("g/cm3", 1.0) + mat.add_s_alpha_beta("c_D_in_7LiD") sph = openmc.Sphere(r=10.0, boundary_type="vacuum") cell = openmc.Cell(fill=mat, region=-sph) model = openmc.Model() @@ -362,7 +514,7 @@ def test_mixed_elastic(fake_mixed_elastic, run_in_tmpdir): model.materials.cross_sections = "cross_sections_mixed.xml" model.settings.particles = 1000 model.settings.batches = 10 - model.settings.run_mode = 'fixed source' + model.settings.run_mode = "fixed source" model.settings.source = openmc.IndependentSource( energy=openmc.stats.Discrete([3.0], [1.0]) # 3 eV source ) diff --git a/tests/unit_tests/test_deplete_activation.py b/tests/unit_tests/test_deplete_activation.py index 6afea8c9f9f..1b9611c86c8 100644 --- a/tests/unit_tests/test_deplete_activation.py +++ b/tests/unit_tests/test_deplete_activation.py @@ -13,28 +13,27 @@ def model(): """Sphere of single nuclide""" model = openmc.Model() - w = openmc.Material(name='tungsten') - w.add_nuclide('W186', 1.0) - w.set_density('g/cm3', 19.3) + w = openmc.Material(name="tungsten") + w.add_nuclide("W186", 1.0) + w.set_density("g/cm3", 19.3) w.depletable = True r = uniform(1.0, 10.0) - w.volume = 4/3 * pi * r**3 + w.volume = 4 / 3 * pi * r**3 - surf = openmc.Sphere(r=r, boundary_type='vacuum') + surf = openmc.Sphere(r=r, boundary_type="vacuum") cell = openmc.Cell(fill=w, region=-surf) model.geometry = openmc.Geometry([cell]) model.settings.batches = 10 model.settings.particles = 1000 model.settings.source = openmc.IndependentSource( - space=openmc.stats.Point(), - energy=openmc.stats.Discrete([1.0e6], [1.0]) + space=openmc.stats.Point(), energy=openmc.stats.Discrete([1.0e6], [1.0]) ) - model.settings.run_mode = 'fixed source' + model.settings.run_mode = "fixed source" - rx_tally = openmc.Tally(name='activation tally') - rx_tally.scores = ['(n,gamma)'] + rx_tally = openmc.Tally(name="activation tally") + rx_tally.scores = ["(n,gamma)"] model.tallies.append(rx_tally) return model @@ -43,29 +42,43 @@ def model(): ENERGIES = np.logspace(log10(1e-5), log10(2e7), 100) -@pytest.mark.parametrize("reaction_rate_mode,reaction_rate_opts,tolerance", [ - ("direct", {}, 1e-5), - ("flux", {'energies': ENERGIES}, 0.01), - ("flux", {'energies': ENERGIES, 'reactions': ['(n,gamma)']}, 1e-5), - ("flux", {'energies': ENERGIES, 'reactions': ['(n,gamma)'], 'nuclides': ['W186', 'H3']}, 1e-2), -]) -def test_activation(run_in_tmpdir, model, reaction_rate_mode, reaction_rate_opts, tolerance): +@pytest.mark.parametrize( + "reaction_rate_mode,reaction_rate_opts,tolerance", + [ + ("direct", {}, 1e-5), + ("flux", {"energies": ENERGIES}, 0.01), + ("flux", {"energies": ENERGIES, "reactions": ["(n,gamma)"]}, 1e-5), + ( + "flux", + { + "energies": ENERGIES, + "reactions": ["(n,gamma)"], + "nuclides": ["W186", "H3"], + }, + 1e-2, + ), + ], +) +def test_activation( + run_in_tmpdir, model, reaction_rate_mode, reaction_rate_opts, tolerance +): # Determine (n.gamma) reaction rate using initial run sp = model.run() with openmc.StatePoint(sp) as sp: - tally = sp.get_tally(name='activation tally') + tally = sp.get_tally(name="activation tally") capture_rate = tally.mean.flat[0] # Create one-nuclide depletion chain chain = openmc.deplete.Chain() - w186 = openmc.deplete.Nuclide('W186') - w186.add_reaction('(n,gamma)', None, 0.0, 1.0) + w186 = openmc.deplete.Nuclide("W186") + w186.add_reaction("(n,gamma)", None, 0.0, 1.0) chain.add_nuclide(w186) - chain.export_to_xml('test_chain.xml') + chain.export_to_xml("test_chain.xml") # Create transport operator op = openmc.deplete.CoupledOperator( - model, 'test_chain.xml', + model, + "test_chain.xml", normalization_mode="source-rate", reaction_rate_mode=reaction_rate_mode, reaction_rate_opts=reaction_rate_opts, @@ -90,24 +103,22 @@ def test_activation(run_in_tmpdir, model, reaction_rate_mode, reaction_rate_opts # So we need to know the initial number of atoms (n0), the capture rate (r), # and choose an irradiation time (t) - w = model.geometry.get_materials_by_name('tungsten')[0] + w = model.geometry.get_materials_by_name("tungsten")[0] atom_densities = w.get_nuclide_atom_densities() - atom_per_cc = 1e24 * atom_densities['W186'] # Density in atom/cm^3 + atom_per_cc = 1e24 * atom_densities["W186"] # Density in atom/cm^3 n0 = atom_per_cc * w.volume # Absolute number of atoms # Pick a random irradiation time and then determine necessary source rate to # reduce material by half t = uniform(1.0, 5.0) * 86400 - source_rates = [n0/(capture_rate*t) * log(2.0)] + source_rates = [n0 / (capture_rate * t) * log(2.0)] # Now activate the material - integrator = openmc.deplete.PredictorIntegrator( - op, [t], source_rates=source_rates - ) + integrator = openmc.deplete.PredictorIntegrator(op, [t], source_rates=source_rates) integrator.integrate() # Get resulting number of atoms - results = openmc.deplete.Results('depletion_results.h5') + results = openmc.deplete.Results("depletion_results.h5") _, atoms = results.get_atoms(w, "W186") assert atoms[0] == pytest.approx(n0) @@ -119,43 +130,43 @@ def test_decay(run_in_tmpdir): # Create a model with a single nuclide, Sr89 mat = openmc.Material() - mat.add_nuclide('Sr89', 1.0) - mat.set_density('g/cm3', 1.0) + mat.add_nuclide("Sr89", 1.0) + mat.set_density("g/cm3", 1.0) mat.depletable = True r = 5.0 - mat.volume = 4/3 * pi * r**3 - surf = openmc.Sphere(r=r, boundary_type='vacuum') + mat.volume = 4 / 3 * pi * r**3 + surf = openmc.Sphere(r=r, boundary_type="vacuum") cell = openmc.Cell(fill=mat, region=-surf) geometry = openmc.Geometry([cell]) settings = openmc.Settings() settings.batches = 10 settings.particles = 1000 - settings.run_mode = 'fixed source' + settings.run_mode = "fixed source" # Create depletion chain with only Sr89 and sample its half-life. Note that # currently at least one reaction has to exist in the depletion chain chain = openmc.deplete.Chain() - sr89 = openmc.deplete.Nuclide('Sr89') + sr89 = openmc.deplete.Nuclide("Sr89") sr89.half_life = normalvariate(4365792.0, 6048.0) - sr89.add_decay_mode('beta-', None, 1.0) - sr89.add_reaction('(n,gamma)', None, 0.0, 1.0) + sr89.add_decay_mode("beta-", None, 1.0) + sr89.add_reaction("(n,gamma)", None, 0.0, 1.0) chain.add_nuclide(sr89) - chain.export_to_xml('test_chain.xml') + chain.export_to_xml("test_chain.xml") model = openmc.Model(geometry=geometry, settings=settings) # Create transport operator op = openmc.deplete.CoupledOperator( - model, 'test_chain.xml', normalization_mode="source-rate" + model, "test_chain.xml", normalization_mode="source-rate" ) # Deplete with two decay steps integrator = openmc.deplete.PredictorIntegrator( - op, [sr89.half_life, 2*sr89.half_life], source_rates=[0.0, 0.0] + op, [sr89.half_life, 2 * sr89.half_life], source_rates=[0.0, 0.0] ) integrator.integrate() # Get resulting number of atoms - results = openmc.deplete.Results('depletion_results.h5') + results = openmc.deplete.Results("depletion_results.h5") _, atoms = results.get_atoms(mat, "Sr89") # Ensure density goes down by a factor of 2 after each half-life @@ -167,24 +178,23 @@ def test_flux_rr_missing_nuclide(run_in_tmpdir, model): # Create two-nuclide depletion chain -- since W184 is not in the model, this # test ensures that FluxCollapseHelper loads missing nuclides appropriately chain = openmc.deplete.Chain() - w184 = openmc.deplete.Nuclide('W184') - w184.add_reaction('(n,gamma)', None, 0.0, 1.0) + w184 = openmc.deplete.Nuclide("W184") + w184.add_reaction("(n,gamma)", None, 0.0, 1.0) chain.add_nuclide(w184) - w186 = openmc.deplete.Nuclide('W186') - w186.add_reaction('(n,gamma)', None, 0.0, 1.0) + w186 = openmc.deplete.Nuclide("W186") + w186.add_reaction("(n,gamma)", None, 0.0, 1.0) chain.add_nuclide(w186) - chain.export_to_xml('test_chain.xml') + chain.export_to_xml("test_chain.xml") # Create transport operator op = openmc.deplete.CoupledOperator( - model, 'test_chain.xml', + model, + "test_chain.xml", normalization_mode="source-rate", reaction_rate_mode="flux", - reaction_rate_opts={'energies': [0.0, 20.0e6]}, + reaction_rate_opts={"energies": [0.0, 20.0e6]}, ) # Deplete with two decay steps - integrator = openmc.deplete.PredictorIntegrator( - op, [100.0], source_rates=[10.0] - ) + integrator = openmc.deplete.PredictorIntegrator(op, [100.0], source_rates=[10.0]) integrator.integrate() diff --git a/tests/unit_tests/test_deplete_atom_number.py b/tests/unit_tests/test_deplete_atom_number.py index 4cc9207ca9e..dc2a6f9c9ae 100644 --- a/tests/unit_tests/test_deplete_atom_number.py +++ b/tests/unit_tests/test_deplete_atom_number.py @@ -9,7 +9,7 @@ def test_indexing(): local_mats = ["10000", "10001"] nuclides = ["U238", "U235", "U234"] - volume = {"10000" : 0.38, "10001" : 0.21} + volume = {"10000": 0.38, "10001": 0.21} number = atom_number.AtomNumber(local_mats, nuclides, volume, 2) @@ -37,10 +37,10 @@ def test_indexing(): def test_properties(): - """Test properties. """ + """Test properties.""" local_mats = ["10000", "10001"] nuclides = ["U238", "U235", "Gd157"] - volume = {"10000" : 0.38, "10001" : 0.21} + volume = {"10000": 0.38, "10001": 0.21} number = atom_number.AtomNumber(local_mats, nuclides, volume, 2) @@ -55,7 +55,7 @@ def test_density_indexing(): local_mats = ["10000", "10001", "10002"] nuclides = ["U238", "U235", "U234"] - volume = {"10000" : 0.38, "10001" : 0.21} + volume = {"10000": 0.38, "10001": 0.21} number = atom_number.AtomNumber(local_mats, nuclides, volume, 2) @@ -91,7 +91,6 @@ def test_density_indexing(): assert number.get_atom_density(1, 2) == 8.0 assert number.get_atom_density(2, 2) == 9.0 - number.set_atom_density(0, 0, 5.0) assert number.get_atom_density(0, 0) == 5.0 @@ -112,7 +111,7 @@ def test_get_mat_slice(): local_mats = ["10000", "10001", "10002"] nuclides = ["U238", "U235", "U234"] - volume = {"10000" : 0.38, "10001" : 0.21} + volume = {"10000": 0.38, "10001": 0.21} number = atom_number.AtomNumber(local_mats, nuclides, volume, 2) @@ -132,7 +131,7 @@ def test_set_mat_slice(): local_mats = ["10000", "10001", "10002"] nuclides = ["U238", "U235", "U234"] - volume = {"10000" : 0.38, "10001" : 0.21} + volume = {"10000": 0.38, "10001": 0.21} number = atom_number.AtomNumber(local_mats, nuclides, volume, 2) diff --git a/tests/unit_tests/test_deplete_chain.py b/tests/unit_tests/test_deplete_chain.py index 9753a53f738..51d23742c27 100644 --- a/tests/unit_tests/test_deplete_chain.py +++ b/tests/unit_tests/test_deplete_chain.py @@ -45,20 +45,20 @@ """ -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def simple_chain(): with cdtemp(): - with open('chain_test.xml', 'w') as fh: + with open("chain_test.xml", "w") as fh: fh.write(_TEST_CHAIN) - yield Chain.from_xml('chain_test.xml') + yield Chain.from_xml("chain_test.xml") -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def endf_chain(): - endf_data = Path(os.environ['OPENMC_ENDF_DATA']) - decay_data = (endf_data / 'decay').glob('*.endf') - fpy_data = (endf_data / 'nfy').glob('*.endf') - neutron_data = (endf_data / 'neutrons').glob('*.endf') + endf_data = Path(os.environ["OPENMC_ENDF_DATA"]) + decay_data = (endf_data / "decay").glob("*.endf") + fpy_data = (endf_data / "nfy").glob("*.endf") + neutron_data = (endf_data / "neutrons").glob("*.endf") return Chain.from_endf(decay_data, fpy_data, neutron_data) @@ -107,7 +107,7 @@ def test_from_xml(simple_chain): nuc = chain["A"] assert nuc.name == "A" - assert nuc.half_life == 2.36520E+04 + assert nuc.half_life == 2.36520e04 assert nuc.n_decay_modes == 2 modes = nuc.decay_modes assert [m.target for m in modes] == ["B", "C"] @@ -122,7 +122,7 @@ def test_from_xml(simple_chain): nuc = chain["B"] assert nuc.name == "B" - assert nuc.half_life == 3.29040E+04 + assert nuc.half_life == 3.29040e04 assert nuc.n_decay_modes == 1 modes = nuc.decay_modes assert [m.target for m in modes] == ["A"] @@ -154,7 +154,7 @@ def test_export_to_xml(run_in_tmpdir): """Test writing a depletion chain to XML.""" # Prevent different MPI ranks from conflicting - filename = 'test{}.xml'.format(comm.rank) + filename = "test{}.xml".format(comm.rank) H1 = nuclide.Nuclide("H1") @@ -162,11 +162,11 @@ def test_export_to_xml(run_in_tmpdir): A.half_life = 2.36520e4 A.decay_modes = [ nuclide.DecayTuple("beta1", "B", 0.6), - nuclide.DecayTuple("beta2", "C", 0.4) + nuclide.DecayTuple("beta2", "C", 0.4), ] A.reactions = [ nuclide.ReactionTuple("(n,gamma)", "C", 0.0, 1.0), - nuclide.ReactionTuple("(n,p)", "B", 0.0, 1.0) + nuclide.ReactionTuple("(n,p)", "B", 0.0, 1.0), ] B = nuclide.Nuclide("B") @@ -174,28 +174,29 @@ def test_export_to_xml(run_in_tmpdir): B.decay_modes = [nuclide.DecayTuple("beta", "A", 1.0)] B.reactions = [ nuclide.ReactionTuple("(n,gamma)", "C", 0.0, 1.0), - nuclide.ReactionTuple("(n,d)", "A", 0.0, 1.0) + nuclide.ReactionTuple("(n,d)", "A", 0.0, 1.0), ] C = nuclide.Nuclide("C") C.reactions = [ nuclide.ReactionTuple("fission", None, 2.0e8, 1.0), nuclide.ReactionTuple("(n,gamma)", "A", 0.0, 0.7), - nuclide.ReactionTuple("(n,gamma)", "B", 0.0, 0.3) + nuclide.ReactionTuple("(n,gamma)", "B", 0.0, 0.3), ] - C.yield_data = nuclide.FissionYieldDistribution({ - 0.0253: {"A": 0.0292737, "B": 0.002566345}}) + C.yield_data = nuclide.FissionYieldDistribution( + {0.0253: {"A": 0.0292737, "B": 0.002566345}} + ) chain = Chain() chain.nuclides = [H1, A, B, C] chain.export_to_xml(filename) - chain_xml = open(filename, 'r').read() + chain_xml = open(filename, "r").read() assert _TEST_CHAIN == chain_xml def test_form_matrix(simple_chain): - """ Using chain_test, and a dummy reaction rate, compute the matrix. """ + """Using chain_test, and a dummy reaction rate, compute the matrix.""" # Relies on test_from_xml passing. chain = simple_chain @@ -211,21 +212,23 @@ def test_form_matrix(simple_chain): react.set("10000", "B", "(n,d)", 0.2) react.set("10000", "C", "(n,gamma)", 4.0) - decay_constant = log(2) / 2.36520E+04 + decay_constant = log(2) / 2.36520e04 mat = np.zeros((4, 4)) - mat[0, 1] = 0.1 # A -> B, (n,p) - mat[1, 1] = -decay_constant - 2.1 # Loss A, decay, (n,gamma), (n,p) - mat[2, 1] = decay_constant*0.6 + 0.1 # A -> B, decay, 0.6 branching ratio + (n,p) - mat[3, 1] = decay_constant*0.4 + 2 # A -> C, decay, 0.4 branching ratio + (n,gamma) - - decay_constant = log(2.0) / 3.29040E+04 - mat[1, 2] = decay_constant + 0.2 # B -> A, decay, 1.0 branching ratio + (n,d) + mat[0, 1] = 0.1 # A -> B, (n,p) + mat[1, 1] = -decay_constant - 2.1 # Loss A, decay, (n,gamma), (n,p) + mat[2, 1] = decay_constant * 0.6 + 0.1 # A -> B, decay, 0.6 branching ratio + (n,p) + mat[3, 1] = ( + decay_constant * 0.4 + 2 + ) # A -> C, decay, 0.4 branching ratio + (n,gamma) + + decay_constant = log(2.0) / 3.29040e04 + mat[1, 2] = decay_constant + 0.2 # B -> A, decay, 1.0 branching ratio + (n,d) mat[2, 2] = -decay_constant - 3.2 # Loss B, decay, (n,gamma), (n,d) - mat[3, 2] = 3 # B -> C, (n,gamma) + mat[3, 2] = 3 # B -> C, (n,gamma) - mat[1, 3] = 0.0292737 * 1.0 + 4.0 * 0.7 # C -> A fission, (n,gamma) - mat[2, 3] = 0.002566345 * 1.0 + 4.0 * 0.3 # C -> B fission, (n,gamma) - mat[3, 3] = -1.0 - 4.0 # Loss C, fission, (n,gamma) + mat[1, 3] = 0.0292737 * 1.0 + 4.0 * 0.7 # C -> A fission, (n,gamma) + mat[2, 3] = 0.002566345 * 1.0 + 4.0 * 0.3 # C -> B fission, (n,gamma) + mat[3, 3] = -1.0 - 4.0 # Loss C, fission, (n,gamma) sp_matrix = chain.form_matrix(react[0]) assert np.allclose(mat, sp_matrix.toarray()) @@ -242,8 +245,7 @@ def test_getitem(): """Test nuc_by_ind converter function.""" chain = Chain() chain.nuclides = ["NucA", "NucB", "NucC"] - chain.nuclide_dict = {nuc: chain.nuclides.index(nuc) - for nuc in chain.nuclides} + chain.nuclide_dict = {nuc: chain.nuclides.index(nuc) for nuc in chain.nuclides} assert "NucA" == chain["NucA"] assert "NucB" == chain["NucB"] @@ -252,13 +254,13 @@ def test_getitem(): def test_set_fiss_q(): """Make sure new fission q values can be set on the chain""" - new_q = {"U235": 2.0E8, "U238": 2.0E8, "U234": 5.0E7} + new_q = {"U235": 2.0e8, "U238": 2.0e8, "U234": 5.0e7} chain_file = Path(__file__).parents[1] / "chain_simple.xml" mod_chain = Chain.from_xml(chain_file, new_q) for name, q in new_q.items(): chain_nuc = mod_chain[name] for rx in chain_nuc.reactions: - if rx.type == 'fission': + if rx.type == "fission": assert rx.Q == q @@ -367,12 +369,9 @@ def test_set_alpha_branches(): chain.nuclide_dict[nuc.name] = ix # add reactions to parent - parent.reactions.append(nuclide.ReactionTuple( - "(n,a)", ground_tgt.name, 1.0, 0.6)) - parent.reactions.append(nuclide.ReactionTuple( - "(n,a)", meta_tgt.name, 1.0, 0.4)) - parent.reactions.append(nuclide.ReactionTuple( - "(n,a)", he4.name, 1.0, 1.0)) + parent.reactions.append(nuclide.ReactionTuple("(n,a)", ground_tgt.name, 1.0, 0.6)) + parent.reactions.append(nuclide.ReactionTuple("(n,a)", meta_tgt.name, 1.0, 0.4)) + parent.reactions.append(nuclide.ReactionTuple("(n,a)", he4.name, 1.0, 1.0)) expected_ref = {"A": {"B": 0.6, "B_m1": 0.4}} @@ -394,8 +393,7 @@ def test_set_alpha_branches(): def test_simple_fission_yields(simple_chain): - """Check the default fission yields that can be used to form the matrix - """ + """Check the default fission yields that can be used to form the matrix""" fission_yields = simple_chain.get_default_fission_yields() assert fission_yields == {"C": {"A": 0.0292737, "B": 0.002566345}} @@ -415,8 +413,7 @@ def test_fission_yield_attribute(simple_chain): # test failure with deplete function # number fission yields != number of materials dummy_conc = [[1, 2]] * (len(empty_chain.fission_yields) + 1) - with pytest.raises( - ValueError, match="fission yield.*not equal.*compositions"): + with pytest.raises(ValueError, match="fission yield.*not equal.*compositions"): pool.deplete(cram.CRAM48, empty_chain, dummy_conc, None, 0.5) @@ -479,10 +476,10 @@ def gnd_simple_chain(): def test_chain_sources(gnd_simple_chain): - i135 = gnd_simple_chain['I135'] + i135 = gnd_simple_chain["I135"] assert isinstance(i135.sources, dict) - assert list(i135.sources.keys()) == ['photon'] - photon_src = i135.sources['photon'] + assert list(i135.sources.keys()) == ["photon"] + photon_src = i135.sources["photon"] assert isinstance(photon_src, Discrete) assert photon_src.integral() == pytest.approx(3.920996223799345e-05) @@ -517,7 +514,7 @@ def test_reduce(gnd_simple_chain, endf_chain): assert u5_round0.yield_data is not None assert u5_round0.yield_data.products == ("I135",) assert u5_round0.yield_data.yield_matrix == ( - ref_U5_yields.yield_matrix[:, ref_U5_yields.products.index("I135")] + ref_U5_yields.yield_matrix[:, ref_U5_yields.products.index("I135")] ) bareI5 = no_depth["I135"] @@ -542,9 +539,7 @@ def test_reduce(gnd_simple_chain, endf_chain): assert u5_round1.decay_modes == ref_U5.decay_modes assert u5_round1.reactions == ref_U5.reactions assert u5_round1.yield_data is not None - assert ( - u5_round1.yield_data.yield_matrix == ref_U5_yields.yield_matrix - ).all() + assert (u5_round1.yield_data.yield_matrix == ref_U5_yields.yield_matrix).all() # Per the chain_simple.xml # I135 -> Xe135 -> Cs135 @@ -554,8 +549,7 @@ def test_reduce(gnd_simple_chain, endf_chain): truncated_iodine = gnd_simple_chain.reduce(["I135"], 1) assert len(iodine_chain) == 4 assert len(truncated_iodine) == 3 - assert set(iodine_chain.nuclide_dict) == { - "I135", "Xe135", "Xe136", "Cs135"} + assert set(iodine_chain.nuclide_dict) == {"I135", "Xe135", "Xe136", "Cs135"} assert set(truncated_iodine.nuclide_dict) == {"I135", "Xe135", "Xe136"} assert iodine_chain.reactions == ["(n,gamma)"] assert iodine_chain["I135"].decay_modes == ref_iodine.decay_modes @@ -578,6 +572,6 @@ def test_reduce(gnd_simple_chain, endf_chain): gnd_simple_chain.reduce(["U235", "Xx999"]) # Make sure reduce preserves light nuclides produced from reactions like (n,p) - reduced_chain = endf_chain.reduce(['U235']) - assert 'H1' in reduced_chain - assert 'H2' in reduced_chain + reduced_chain = endf_chain.reduce(["U235"]) + assert "H1" in reduced_chain + assert "H2" in reduced_chain diff --git a/tests/unit_tests/test_deplete_coupled_operator.py b/tests/unit_tests/test_deplete_coupled_operator.py index fe79d621b12..ad6a7ab6ba2 100644 --- a/tests/unit_tests/test_deplete_coupled_operator.py +++ b/tests/unit_tests/test_deplete_coupled_operator.py @@ -31,15 +31,14 @@ def model(): radii = [0.42, 0.45] fuel.volume = np.pi * radii[0] ** 2 - clad.volume = np.pi * (radii[1]**2 - radii[0]**2) - water.volume = 1.24**2 - (np.pi * radii[1]**2) + clad.volume = np.pi * (radii[1] ** 2 - radii[0] ** 2) + water.volume = 1.24**2 - (np.pi * radii[1] ** 2) materials = openmc.Materials([fuel, clad, water]) pin_surfaces = [openmc.ZCylinder(r=r) for r in radii] pin_univ = openmc.model.pin(pin_surfaces, materials) - bound_box = openmc.model.RectangularPrism( - 1.24, 1.24, boundary_type="reflective") + bound_box = openmc.model.RectangularPrism(1.24, 1.24, boundary_type="reflective") root_cell = openmc.Cell(fill=pin_univ, region=-bound_box) geometry = openmc.Geometry([root_cell]) @@ -92,8 +91,8 @@ def test_diff_volume_method_match_cell(model_with_volumes): operator = openmc.deplete.CoupledOperator( model=model_with_volumes, diff_burnable_mats=True, - diff_volume_method='match cell', - chain_file=CHAIN_PATH + diff_volume_method="match cell", + chain_file=CHAIN_PATH, ) all_cells = list(operator.model.geometry.get_all_cells().values()) @@ -109,8 +108,8 @@ def test_diff_volume_method_divide_equally(model_with_volumes): operator = openmc.deplete.CoupledOperator( model=model_with_volumes, diff_burnable_mats=True, - diff_volume_method='divide equally', - chain_file=CHAIN_PATH + diff_volume_method="divide equally", + chain_file=CHAIN_PATH, ) all_cells = list(operator.model.geometry.get_all_cells().values()) diff --git a/tests/unit_tests/test_deplete_decay.py b/tests/unit_tests/test_deplete_decay.py index 6e7b0b101ec..49a05ff3cb3 100644 --- a/tests/unit_tests/test_deplete_decay.py +++ b/tests/unit_tests/test_deplete_decay.py @@ -7,8 +7,9 @@ def test_deplete_decay_products(run_in_tmpdir): # Create chain file with H1, He4, and Li5 - with open('test_chain.xml', 'w') as chain_file: - chain_file.write(""" + with open("test_chain.xml", "w") as chain_file: + chain_file.write( + """ @@ -17,7 +18,8 @@ def test_deplete_decay_products(run_in_tmpdir): - """) + """ + ) # Create MicroXS object with no cross sections micro_xs = openmc.deplete.MicroXS(np.empty((0, 0, 0)), [], []) @@ -25,21 +27,21 @@ def test_deplete_decay_products(run_in_tmpdir): # Create depletion operator with no reactions op = openmc.deplete.IndependentOperator.from_nuclides( volume=1.0, - nuclides={'Li5': 1.0}, + nuclides={"Li5": 1.0}, flux=0.0, micro_xs=micro_xs, - chain_file='test_chain.xml', - normalization_mode='source-rate' + chain_file="test_chain.xml", + normalization_mode="source-rate", ) # Create time-integrator and integrate integrator = openmc.deplete.PredictorIntegrator( - op, timesteps=[1.0], source_rates=[0.0], timestep_units='d' + op, timesteps=[1.0], source_rates=[0.0], timestep_units="d" ) integrator.integrate(final_step=False) # Get concentration of H1 and He4 - results = openmc.deplete.Results('depletion_results.h5') + results = openmc.deplete.Results("depletion_results.h5") mat_id = op.materials[0].id _, h1 = results.get_atoms(f"{mat_id}", "H1") _, he4 = results.get_atoms(f"{mat_id}", "He4") @@ -61,24 +63,25 @@ def test_deplete_decay_step_fissionable(run_in_tmpdir): # Set up a pure decay operator micro_xs = openmc.deplete.MicroXS(np.empty((0, 0, 0)), [], []) mat = openmc.Material() - mat.name = 'I do not decay.' - mat.add_nuclide('U238', 1.0, 'ao') + mat.name = "I do not decay." + mat.add_nuclide("U238", 1.0, "ao") mat.volume = 10.0 - mat.set_density('g/cc', 1.0) - original_atoms = mat.get_nuclide_atoms()['U238'] + mat.set_density("g/cc", 1.0) + original_atoms = mat.get_nuclide_atoms()["U238"] mats = openmc.Materials([mat]) op = openmc.deplete.IndependentOperator( - mats, [1.0], [micro_xs], Path(__file__).parents[1] / "chain_simple.xml") + mats, [1.0], [micro_xs], Path(__file__).parents[1] / "chain_simple.xml" + ) # Create time integrator and integrate integrator = openmc.deplete.PredictorIntegrator( - op, [1.0], power=[0.0], timestep_units='s' + op, [1.0], power=[0.0], timestep_units="s" ) integrator.integrate() # Get concentration of U238. It should be unchanged since this chain has no U238 decay. - results = openmc.deplete.Results('depletion_results.h5') + results = openmc.deplete.Results("depletion_results.h5") _, u238 = results.get_atoms(f"{mat.id}", "U238") assert u238[1] == pytest.approx(original_atoms) diff --git a/tests/unit_tests/test_deplete_fission_yields.py b/tests/unit_tests/test_deplete_fission_yields.py index 1937e61e333..878124a5bf9 100644 --- a/tests/unit_tests/test_deplete_fission_yields.py +++ b/tests/unit_tests/test_deplete_fission_yields.py @@ -11,8 +11,10 @@ from openmc import lib from openmc.deplete.nuclide import Nuclide, FissionYieldDistribution from openmc.deplete.helpers import ( - FissionYieldCutoffHelper, ConstantFissionYieldHelper, - AveragedFissionYieldHelper) + FissionYieldCutoffHelper, + ConstantFissionYieldHelper, + AveragedFissionYieldHelper, +) @pytest.fixture(scope="module") @@ -43,8 +45,12 @@ def materials(tmpdir_factory): with lib.run_in_memory(): yield [lib.Material(), lib.Material()] finally: - for file_path in ("settings.xml", "geometry.xml", "materials.xml", - "summary.h5"): + for file_path in ( + "settings.xml", + "geometry.xml", + "materials.xml", + "summary.h5", + ): os.remove(tmpdir / file_path) orig.chdir() os.rmdir(tmpdir) @@ -76,7 +82,8 @@ def nuclide_bundle(): u5yield_dict = { 0.0253: {"Xe135": 7.85e-4, "Gd155": 4.08e-12, "Sm149": 1.71e-12}, 5.0e5: {"Xe135": 7.85e-4, "Sm149": 1.71e-12}, - 1.40e7: {"Xe135": 4.54e-3, "Gd155": 5.83e-8}} + 1.40e7: {"Xe135": 4.54e-3, "Gd155": 5.83e-8}, + } u235 = Nuclide("U235") u235.yield_data = FissionYieldDistribution(u5yield_dict) @@ -87,24 +94,28 @@ def nuclide_bundle(): xe135 = Nuclide("Xe135") pu239 = Nuclide("Pu239") - pu239.yield_data = FissionYieldDistribution({ - 5.0e5: {"Xe135": 6.14e-3, "Sm149": 9.429e-10, "Gd155": 5.24e-9}, - 2e6: {"Xe135": 6.15e-3, "Sm149": 9.42e-10, "Gd155": 5.29e-9}}) + pu239.yield_data = FissionYieldDistribution( + { + 5.0e5: {"Xe135": 6.14e-3, "Sm149": 9.429e-10, "Gd155": 5.24e-9}, + 2e6: {"Xe135": 6.15e-3, "Sm149": 9.42e-10, "Gd155": 5.29e-9}, + } + ) NuclideBundle = namedtuple("NuclideBundle", "u235 u238 xe135 pu239") return NuclideBundle(u235, u238, xe135, pu239) @pytest.mark.parametrize( - "input_energy, yield_energy", - ((0.0253, 0.0253), (0.01, 0.0253), (4e5, 5e5))) + "input_energy, yield_energy", ((0.0253, 0.0253), (0.01, 0.0253), (4e5, 5e5)) +) def test_constant_helper(nuclide_bundle, input_energy, yield_energy): helper = ConstantFissionYieldHelper(nuclide_bundle, energy=input_energy) assert helper.energy == input_energy assert helper.constant_yields == { "U235": nuclide_bundle.u235.yield_data[yield_energy], "U238": nuclide_bundle.u238.yield_data[5.00e5], - "Pu239": nuclide_bundle.pu239.yield_data[5e5]} + "Pu239": nuclide_bundle.pu239.yield_data[5e5], + } assert helper.constant_yields == helper.weighted_yields(1) @@ -117,7 +128,8 @@ def test_cutoff_construction(nuclide_bundle): helper = FissionYieldCutoffHelper(nuclide_bundle, 1) assert helper.constant_yields == { "U238": u238.yield_data[5.0e5], - "Pu239": pu239.yield_data[5e5]} + "Pu239": pu239.yield_data[5e5], + } assert helper.thermal_yields == {"U235": u235.yield_data[0.0253]} assert helper.fast_yields == {"U235": u235.yield_data[5e5]} @@ -125,7 +137,8 @@ def test_cutoff_construction(nuclide_bundle): helper = FissionYieldCutoffHelper(nuclide_bundle, 1, fast_energy=14e6) assert helper.constant_yields == { "U238": u238.yield_data[5.0e5], - "Pu239": pu239.yield_data[5e5]} + "Pu239": pu239.yield_data[5e5], + } assert helper.thermal_yields == {"U235": u235.yield_data[0.0253]} assert helper.fast_yields == {"U235": u235.yield_data[14e6]} @@ -140,30 +153,39 @@ def test_cutoff_construction(nuclide_bundle): assert helper.fast_yields == {"U235": u235.yield_data[5e5]} # higher cutoff energy -> obtain fast and "faster" yields - helper = FissionYieldCutoffHelper(nuclide_bundle, 1, cutoff=1e6, - thermal_energy=5e5, fast_energy=14e6) + helper = FissionYieldCutoffHelper( + nuclide_bundle, 1, cutoff=1e6, thermal_energy=5e5, fast_energy=14e6 + ) assert helper.constant_yields == {"U238": u238.yield_data[5e5]} assert helper.thermal_yields == { - "U235": u235.yield_data[5e5], "Pu239": pu239.yield_data[5e5]} + "U235": u235.yield_data[5e5], + "Pu239": pu239.yield_data[5e5], + } assert helper.fast_yields == { - "U235": u235.yield_data[14e6], "Pu239": pu239.yield_data[2e6]} + "U235": u235.yield_data[14e6], + "Pu239": pu239.yield_data[2e6], + } # test super low and super high cutoff energies helper = FissionYieldCutoffHelper( - nuclide_bundle, 1, thermal_energy=0.001, cutoff=0.002) + nuclide_bundle, 1, thermal_energy=0.001, cutoff=0.002 + ) assert helper.fast_yields == {} assert helper.thermal_yields == {} assert helper.constant_yields == { - "U235": u235.yield_data[0.0253], "U238": u238.yield_data[5e5], - "Pu239": pu239.yield_data[5e5]} + "U235": u235.yield_data[0.0253], + "U238": u238.yield_data[5e5], + "Pu239": pu239.yield_data[5e5], + } - helper = FissionYieldCutoffHelper( - nuclide_bundle, 1, cutoff=15e6, fast_energy=17e6) + helper = FissionYieldCutoffHelper(nuclide_bundle, 1, cutoff=15e6, fast_energy=17e6) assert helper.thermal_yields == {} assert helper.fast_yields == {} assert helper.constant_yields == { - "U235": u235.yield_data[14e6], "U238": u238.yield_data[5e5], - "Pu239": pu239.yield_data[2e6]} + "U235": u235.yield_data[14e6], + "U238": u238.yield_data[5e5], + "Pu239": pu239.yield_data[2e6], + } @pytest.mark.parametrize("key", ("cutoff", "thermal_energy", "fast_energy")) @@ -177,8 +199,9 @@ def test_cutoff_failure(key): # emulate some split between fast and thermal U235 fissions @pytest.mark.parametrize("therm_frac", (0.5, 0.2, 0.8)) def test_cutoff_helper(materials, nuclide_bundle, therm_frac): - helper = FissionYieldCutoffHelper(nuclide_bundle, len(materials), - cutoff=1e6, fast_energy=14e6) + helper = FissionYieldCutoffHelper( + nuclide_bundle, len(materials), cutoff=1e6, fast_energy=14e6 + ) helper.generate_tallies(materials, [0]) non_zero_nucs = [n.name for n in nuclide_bundle] @@ -217,15 +240,15 @@ def test_cutoff_helper(materials, nuclide_bundle, therm_frac): for nuc in tally_nucs: assert actual_yields[nuc] == ( helper.thermal_yields[nuc] * therm_frac - + helper.fast_yields[nuc] * (1 - therm_frac)) + + helper.fast_yields[nuc] * (1 - therm_frac) + ) @pytest.mark.parametrize("avg_energy", (0.01, 6e5, 15e6)) def test_averaged_helper(materials, nuclide_bundle, avg_energy): helper = AveragedFissionYieldHelper(nuclide_bundle) helper.generate_tallies(materials, [0]) - tallied_nucs = helper.update_tally_nuclides( - [n.name for n in nuclide_bundle]) + tallied_nucs = helper.update_tally_nuclides([n.name for n in nuclide_bundle]) assert tallied_nucs == ["Pu239", "U235"] # check generated tallies @@ -254,8 +277,7 @@ def test_averaged_helper(materials, nuclide_bundle, avg_energy): helper_flux = 1e16 fission_results = proxy_tally_data(fission_tally, helper_flux) - weighted_results = proxy_tally_data( - weighted_tally, helper_flux * avg_energy) + weighted_results = proxy_tally_data(weighted_tally, helper_flux * avg_energy) helper._fission_rate_tally = Mock() helper._weighted_tally = Mock() @@ -285,7 +307,7 @@ def interp_average_yields(nuc, avg_energy): if avg_energy > energies[-1]: return yields[energies[-1]] thermal_ix = bisect.bisect_left(energies, avg_energy) - thermal_E, fast_E = energies[thermal_ix - 1:thermal_ix + 1] + thermal_E, fast_E = energies[thermal_ix - 1 : thermal_ix + 1] assert thermal_E < avg_energy < fast_E - split = (avg_energy - thermal_E)/(fast_E - thermal_E) - return yields[thermal_E]*(1 - split) + yields[fast_E]*split + split = (avg_energy - thermal_E) / (fast_E - thermal_E) + return yields[thermal_E] * (1 - split) + yields[fast_E] * split diff --git a/tests/unit_tests/test_deplete_independent_operator.py b/tests/unit_tests/test_deplete_independent_operator.py index c765d065009..267d2e0f675 100644 --- a/tests/unit_tests/test_deplete_independent_operator.py +++ b/tests/unit_tests/test_deplete_independent_operator.py @@ -17,16 +17,19 @@ def test_operator_init(): """The test uses a temporary dummy chain. This file will be removed at the end of the test, and only contains a depletion_chain node.""" volume = 1 - nuclides = {'U234': 8.922411359424315e+18, - 'U235': 9.98240191860822e+20, - 'U238': 2.2192386373095893e+22, - 'U236': 4.5724195495061115e+18, - 'O16': 4.639065406771322e+22, - 'O17': 1.7588724018066158e+19} + nuclides = { + "U234": 8.922411359424315e18, + "U235": 9.98240191860822e20, + "U238": 2.2192386373095893e22, + "U236": 4.5724195495061115e18, + "O16": 4.639065406771322e22, + "O17": 1.7588724018066158e19, + } flux = 1.0 micro_xs = MicroXS.from_csv(ONE_GROUP_XS) IndependentOperator.from_nuclides( - volume, nuclides, flux, micro_xs, CHAIN_PATH, nuc_units='atom/cm3') + volume, nuclides, flux, micro_xs, CHAIN_PATH, nuc_units="atom/cm3" + ) fuel = Material(name="uo2") fuel.add_element("U", 1, percent_type="ao", enrichment=4.25) diff --git a/tests/unit_tests/test_deplete_integrator.py b/tests/unit_tests/test_deplete_integrator.py index b1d2cb950eb..db9ef830e7d 100644 --- a/tests/unit_tests/test_deplete_integrator.py +++ b/tests/unit_tests/test_deplete_integrator.py @@ -16,9 +16,20 @@ from openmc.mpi import comm from openmc.deplete import ( - ReactionRates, StepResult, Results, OperatorResult, PredictorIntegrator, - CECMIntegrator, CF4Integrator, CELIIntegrator, EPCRK4Integrator, - LEQIIntegrator, SICELIIntegrator, SILEQIIntegrator, cram) + ReactionRates, + StepResult, + Results, + OperatorResult, + PredictorIntegrator, + CECMIntegrator, + CF4Integrator, + CELIIntegrator, + EPCRK4Integrator, + LEQIIntegrator, + SICELIIntegrator, + SILEQIIntegrator, + cram, +) from tests import dummy_operator @@ -31,7 +42,7 @@ EPCRK4Integrator, LEQIIntegrator, SICELIIntegrator, - SILEQIIntegrator + SILEQIIntegrator, ] @@ -52,16 +63,15 @@ def test_results_save(run_in_tmpdir): full_burn_list = [] for i in range(comm.size): - vol_dict[str(2*i)] = 1.2 - vol_dict[str(2*i + 1)] = 1.2 - full_burn_list.append(str(2*i)) - full_burn_list.append(str(2*i + 1)) + vol_dict[str(2 * i)] = 1.2 + vol_dict[str(2 * i + 1)] = 1.2 + full_burn_list.append(str(2 * i)) + full_burn_list.append(str(2 * i + 1)) - burn_list = full_burn_list[2*comm.rank: 2*comm.rank + 2] + burn_list = full_burn_list[2 * comm.rank : 2 * comm.rank + 2] nuc_list = ["na", "nb"] - op.get_results_info.return_value = ( - vol_dict, nuc_list, burn_list, full_burn_list) + op.get_results_info.return_value = (vol_dict, nuc_list, burn_list, full_burn_list) # Construct x x1 = [] @@ -95,14 +105,12 @@ def test_results_save(run_in_tmpdir): t1 = [0.0, 1.0] t2 = [1.0, 2.0] - op_result1 = [OperatorResult(ufloat(*k), rates) - for k, rates in zip(eigvl1, rate1)] - op_result2 = [OperatorResult(ufloat(*k), rates) - for k, rates in zip(eigvl2, rate2)] + op_result1 = [OperatorResult(ufloat(*k), rates) for k, rates in zip(eigvl1, rate1)] + op_result2 = [OperatorResult(ufloat(*k), rates) for k, rates in zip(eigvl2, rate2)] # saves within a subdirectory - StepResult.save(op, x1, op_result1, t1, 0, 0, path='out/put/depletion.h5') - res = Results('out/put/depletion.h5') + StepResult.save(op, x1, op_result1, t1, 0, 0, path="out/put/depletion.h5") + res = Results("out/put/depletion.h5") # saves with default filename StepResult.save(op, x1, op_result1, t1, 0, 0) @@ -194,7 +202,7 @@ def test_integrator(run_in_tmpdir, scheme): # test structure of depletion time dataset dep_time = res.get_depletion_time() - assert dep_time.shape == (2, ) + assert dep_time.shape == (2,) assert all(dep_time > 0) integrator = bundle.solver(operator, [0.75], 1, solver=cram.CRAM48) @@ -224,47 +232,51 @@ def test_timesteps(integrator): # Reference timesteps in seconds day = 86400.0 - ref_timesteps = [1*day, 2*day, 5*day, 10*day] + ref_timesteps = [1 * day, 2 * day, 5 * day, 10 * day] # Case 1, timesteps in seconds timesteps = ref_timesteps - x = integrator(op, timesteps, power, timestep_units='s') + x = integrator(op, timesteps, power, timestep_units="s") assert np.allclose(x.timesteps, ref_timesteps) # Case 2, timesteps in minutes minute = 60 timesteps = [t / minute for t in ref_timesteps] - x = integrator(op, timesteps, power, timestep_units='min') + x = integrator(op, timesteps, power, timestep_units="min") assert np.allclose(x.timesteps, ref_timesteps) # Case 3, timesteps in hours - hour = 60*60 + hour = 60 * 60 timesteps = [t / hour for t in ref_timesteps] - x = integrator(op, timesteps, power, timestep_units='h') + x = integrator(op, timesteps, power, timestep_units="h") assert np.allclose(x.timesteps, ref_timesteps) # Case 4, timesteps in days timesteps = [t / day for t in ref_timesteps] - x = integrator(op, timesteps, power, timestep_units='d') + x = integrator(op, timesteps, power, timestep_units="d") assert np.allclose(x.timesteps, ref_timesteps) # Case 5, timesteps in MWd/kg kilograms = op.heavy_metal / 1000.0 - days = [t/day for t in ref_timesteps] + days = [t / day for t in ref_timesteps] megawatts = power / 1000000.0 burnup = [t * megawatts / kilograms for t in days] - x = integrator(op, burnup, power, timestep_units='MWd/kg') + x = integrator(op, burnup, power, timestep_units="MWd/kg") assert np.allclose(x.timesteps, ref_timesteps) # Case 6, mixed units - burnup_per_day = (1e-6*power) / kilograms - timesteps = [(burnup_per_day, 'MWd/kg'), (2*day, 's'), (5, 'd'), - (10*burnup_per_day, 'MWd/kg')] + burnup_per_day = (1e-6 * power) / kilograms + timesteps = [ + (burnup_per_day, "MWd/kg"), + (2 * day, "s"), + (5, "d"), + (10 * burnup_per_day, "MWd/kg"), + ] x = integrator(op, timesteps, power) assert np.allclose(x.timesteps, ref_timesteps) # Bad units should raise an exception with pytest.raises(ValueError, match="unit"): - integrator(op, ref_timesteps, power, timestep_units='🐨') + integrator(op, ref_timesteps, power, timestep_units="🐨") with pytest.raises(ValueError, match="unit"): - integrator(op, [(800.0, 'gorillas')], power) + integrator(op, [(800.0, "gorillas")], power) diff --git a/tests/unit_tests/test_deplete_microxs.py b/tests/unit_tests/test_deplete_microxs.py index 073b3f162d1..685cb96c633 100644 --- a/tests/unit_tests/test_deplete_microxs.py +++ b/tests/unit_tests/test_deplete_microxs.py @@ -16,56 +16,61 @@ def test_from_array(): nuclides = [ - 'U234', - 'U235', - 'U238', - 'U236', - 'O16', - 'O17', - 'I135', - 'Xe135', - 'Xe136', - 'Cs135', - 'Gd157', - 'Gd156'] - reactions = ['fission', '(n,gamma)'] + "U234", + "U235", + "U238", + "U236", + "O16", + "O17", + "I135", + "Xe135", + "Xe136", + "Cs135", + "Gd157", + "Gd156", + ] + reactions = ["fission", "(n,gamma)"] # These values are placeholders and are not at all # physically meaningful. - data = np.array([[0.1, 0.], - [0.1, 0.], - [0.9, 0.], - [0.4, 0.], - [0., 0.], - [0., 0.], - [0., 0.1], - [0., 0.9], - [0., 0.], - [0., 0.], - [0., 0.1], - [0., 0.1]]) + data = np.array( + [ + [0.1, 0.0], + [0.1, 0.0], + [0.9, 0.0], + [0.4, 0.0], + [0.0, 0.0], + [0.0, 0.0], + [0.0, 0.1], + [0.0, 0.9], + [0.0, 0.0], + [0.0, 0.0], + [0.0, 0.1], + [0.0, 0.1], + ] + ) data.shape = (12, 2, 1) MicroXS(data, nuclides, reactions) - with pytest.raises(ValueError, match='Data array must be 3D'): + with pytest.raises(ValueError, match="Data array must be 3D"): MicroXS(data[:, 0], nuclides, reactions) def test_csv(): ref_xs = MicroXS.from_csv(ONE_GROUP_XS) - ref_xs.to_csv('temp_xs.csv') - temp_xs = MicroXS.from_csv('temp_xs.csv') + ref_xs.to_csv("temp_xs.csv") + temp_xs = MicroXS.from_csv("temp_xs.csv") assert np.all(ref_xs.data == temp_xs.data) - remove('temp_xs.csv') + remove("temp_xs.csv") def test_from_multigroup_flux(): - energies = [0., 6.25e-1, 5.53e3, 8.21e5, 2.e7] + energies = [0.0, 6.25e-1, 5.53e3, 8.21e5, 2.0e7] flux = [1.1e-7, 1.2e-6, 1.3e-5, 1.4e-4] - chain_file = Path(__file__).parents[1] / 'chain_simple.xml' - kwargs = {'multigroup_flux': flux, 'chain_file': chain_file} + chain_file = Path(__file__).parents[1] / "chain_simple.xml" + kwargs = {"multigroup_flux": flux, "chain_file": chain_file} # test with energy group structure from string - microxs = MicroXS.from_multigroup_flux(energies='CASMO-4', **kwargs) + microxs = MicroXS.from_multigroup_flux(energies="CASMO-4", **kwargs) assert isinstance(microxs, MicroXS) # test with energy group structure as floats @@ -74,29 +79,30 @@ def test_from_multigroup_flux(): # test with nuclides provided microxs = MicroXS.from_multigroup_flux( - energies=energies, nuclides=['Gd157', 'H1'], **kwargs + energies=energies, nuclides=["Gd157", "H1"], **kwargs ) assert isinstance(microxs, MicroXS) - assert microxs.nuclides == ['Gd157', 'H1'] + assert microxs.nuclides == ["Gd157", "H1"] # test with reactions provided microxs = MicroXS.from_multigroup_flux( - energies=energies, reactions=['fission', '(n,2n)'], **kwargs + energies=energies, reactions=["fission", "(n,2n)"], **kwargs ) assert isinstance(microxs, MicroXS) - assert microxs.reactions == ['fission', '(n,2n)'] + assert microxs.reactions == ["fission", "(n,2n)"] def test_multigroup_flux_same(): - chain_file = Path(__file__).parents[1] / 'chain_simple.xml' + chain_file = Path(__file__).parents[1] / "chain_simple.xml" # Generate micro XS based on 4-group flux - energies = [0., 6.25e-1, 5.53e3, 8.21e5, 2.e7] + energies = [0.0, 6.25e-1, 5.53e3, 8.21e5, 2.0e7] flux_per_ev = [0.3, 0.3, 1.0, 1.0] flux = flux_per_ev * np.diff(energies) flux_sum = flux.sum() microxs_4g = MicroXS.from_multigroup_flux( - energies=energies, multigroup_flux=flux, chain_file=chain_file) + energies=energies, multigroup_flux=flux, chain_file=chain_file + ) # from_multigroup_flux should not modify the flux assert flux.sum() == flux_sum @@ -104,10 +110,11 @@ def test_multigroup_flux_same(): # Generate micro XS based on 2-group flux, where the boundaries line up with # the 4 group flux and have the same flux per eV across the full energy # range - energies = [0., 5.53e3, 2.0e7] + energies = [0.0, 5.53e3, 2.0e7] flux_per_ev = [0.3, 1.0] flux = flux_per_ev * np.diff(energies) microxs_2g = MicroXS.from_multigroup_flux( - energies=energies, multigroup_flux=flux, chain_file=chain_file) + energies=energies, multigroup_flux=flux, chain_file=chain_file + ) assert microxs_4g.data == pytest.approx(microxs_2g.data) diff --git a/tests/unit_tests/test_deplete_nuclide.py b/tests/unit_tests/test_deplete_nuclide.py index f2bb7d1b65e..e8609c0a61a 100644 --- a/tests/unit_tests/test_deplete_nuclide.py +++ b/tests/unit_tests/test_deplete_nuclide.py @@ -10,28 +10,28 @@ def test_n_decay_modes(): - """ Test the decay mode count parameter. """ + """Test the decay mode count parameter.""" nuc = nuclide.Nuclide() nuc.decay_modes = [ nuclide.DecayTuple("beta1", "a", 0.5), nuclide.DecayTuple("beta2", "b", 0.3), - nuclide.DecayTuple("beta3", "c", 0.2) + nuclide.DecayTuple("beta3", "c", 0.2), ] assert nuc.n_decay_modes == 3 def test_n_reaction_paths(): - """ Test the reaction path count parameter. """ + """Test the reaction path count parameter.""" nuc = nuclide.Nuclide() nuc.reactions = [ nuclide.ReactionTuple("(n,2n)", "a", 0.0, 1.0), nuclide.ReactionTuple("(n,3n)", "b", 0.0, 1.0), - nuclide.ReactionTuple("(n,4n)", "c", 0.0, 1.0) + nuclide.ReactionTuple("(n,4n)", "c", 0.0, 1.0), ] assert nuc.n_reaction_paths == 3 @@ -63,18 +63,19 @@ def test_from_xml(): u235 = nuclide.Nuclide.from_xml(element) assert u235.decay_modes == [ - nuclide.DecayTuple('sf', 'U235', 7.2e-11), - nuclide.DecayTuple('alpha', 'Th231', 1 - 7.2e-11) + nuclide.DecayTuple("sf", "U235", 7.2e-11), + nuclide.DecayTuple("alpha", "Th231", 1 - 7.2e-11), ] assert u235.reactions == [ - nuclide.ReactionTuple('(n,2n)', 'U234', -5297781.0, 1.0), - nuclide.ReactionTuple('(n,3n)', 'U233', -12142300.0, 1.0), - nuclide.ReactionTuple('(n,4n)', 'U232', -17885600.0, 1.0), - nuclide.ReactionTuple('(n,gamma)', 'U236', 6545200.0, 1.0), - nuclide.ReactionTuple('fission', None, 193405400.0, 1.0), + nuclide.ReactionTuple("(n,2n)", "U234", -5297781.0, 1.0), + nuclide.ReactionTuple("(n,3n)", "U233", -12142300.0, 1.0), + nuclide.ReactionTuple("(n,4n)", "U232", -17885600.0, 1.0), + nuclide.ReactionTuple("(n,gamma)", "U236", 6545200.0, 1.0), + nuclide.ReactionTuple("fission", None, 193405400.0, 1.0), ] - expected_yield_data = nuclide.FissionYieldDistribution({ - 0.0253: {"Xe138": 0.0481413, "Zr100": 0.0497641, "Te134": 0.062155}}) + expected_yield_data = nuclide.FissionYieldDistribution( + {0.0253: {"Xe138": 0.0481413, "Zr100": 0.0497641, "Te134": 0.062155}} + ) assert u235.yield_data == expected_yield_data # test accessing the yield energies through the FissionYieldDistribution assert u235.yield_energies == (0.0253,) @@ -106,7 +107,7 @@ def test_fpy_parent(): """ root = ET.fromstring(data) - elems = root.findall('nuclide') + elems = root.findall("nuclide") u235 = nuclide.Nuclide.from_xml(elems[0], root) u238 = nuclide.Nuclide.from_xml(elems[1], root) @@ -116,8 +117,8 @@ def test_fpy_parent(): # Make sure XML element created has single attribute elem = u238.to_xml_element() - fpy_elem = elem.find('neutron_fission_yields') - assert fpy_elem.get('parent') == 'U235' + fpy_elem = elem.find("neutron_fission_yields") + assert fpy_elem.get("parent") == "U235" assert len(fpy_elem) == 0 data = """ @@ -134,7 +135,7 @@ def test_fpy_parent(): # U235 yields are missing, so we should get an exception root = ET.fromstring(data) - elems = root.findall('nuclide') + elems = root.findall("nuclide") with pytest.raises(ValueError, match="yields"): u238 = nuclide.Nuclide.from_xml(elems[1], root) @@ -145,15 +146,16 @@ def test_to_xml_element(): C = nuclide.Nuclide("C") C.half_life = 0.123 C.decay_modes = [ - nuclide.DecayTuple('beta-', 'B', 0.99), - nuclide.DecayTuple('alpha', 'D', 0.01) + nuclide.DecayTuple("beta-", "B", 0.99), + nuclide.DecayTuple("alpha", "D", 0.01), ] C.reactions = [ - nuclide.ReactionTuple('fission', None, 2.0e8, 1.0), - nuclide.ReactionTuple('(n,gamma)', 'A', 0.0, 1.0) + nuclide.ReactionTuple("fission", None, 2.0e8, 1.0), + nuclide.ReactionTuple("(n,gamma)", "A", 0.0, 1.0), ] C.yield_data = nuclide.FissionYieldDistribution( - {0.0253: {"A": 0.0292737, "B": 0.002566345}}) + {0.0253: {"A": 0.0292737, "B": 0.002566345}} + ) element = C.to_xml_element() assert element.get("half_life") == "0.123" @@ -175,7 +177,7 @@ def test_to_xml_element(): assert rx_elems[1].get("target") == "A" assert float(rx_elems[1].get("Q")) == 0.0 - assert element.find('neutron_fission_yields') is not None + assert element.find("neutron_fission_yields") is not None def test_fission_yield_distribution(): @@ -192,10 +194,13 @@ def test_fission_yield_distribution(): act_dist = yield_dict[exp_ene] for exp_prod, exp_yield in exp_dist.items(): assert act_dist[exp_prod] == exp_yield - exp_yield = np.array([ - [4.08e-12, 1.71e-12, 7.85e-4], - [1.32e-12, 0.0, 1.12e-3], - [5.83e-8, 2.69e-8, 4.54e-3]]) + exp_yield = np.array( + [ + [4.08e-12, 1.71e-12, 7.85e-4], + [1.32e-12, 0.0, 1.12e-3], + [5.83e-8, 2.69e-8, 4.54e-3], + ] + ) assert np.array_equal(yield_dist.yield_matrix, exp_yield) # Test the operations / special methods for fission yield @@ -236,8 +241,7 @@ def test_fission_yield_distribution(): # Test restriction of fission products strict_restrict = yield_dist.restrict_products(["Xe135", "Sm149"]) - with_extras = yield_dist.restrict_products( - ["Xe135", "Sm149", "H1", "U235"]) + with_extras = yield_dist.restrict_products(["Xe135", "Sm149", "H1", "U235"]) assert strict_restrict.products == ("Sm149", "Xe135") assert strict_restrict.energies == yield_dist.energies @@ -250,6 +254,7 @@ def test_fission_yield_distribution(): assert yield_dist.restrict_products(["U235"]) is None + def test_validate(): nuc = nuclide.Nuclide() @@ -341,7 +346,9 @@ def test_validate(): def test_deepcopy(): """Test deepcopying a FissionYield object""" - nuc = nuclide.FissionYield(products=("I129", "Sm149", "Xe135"), yields=np.array((0.001, 0.0003, 0.002))) + nuc = nuclide.FissionYield( + products=("I129", "Sm149", "Xe135"), yields=np.array((0.001, 0.0003, 0.002)) + ) copied_nuc = copy.deepcopy(nuc) # Check the deepcopy equals the original assert copied_nuc == nuc diff --git a/tests/unit_tests/test_deplete_operator.py b/tests/unit_tests/test_deplete_operator.py index 6ea89fc4a5b..e6822fced3a 100644 --- a/tests/unit_tests/test_deplete_operator.py +++ b/tests/unit_tests/test_deplete_operator.py @@ -42,19 +42,24 @@ def test_operator_init(): ref_nuc = ref_chain[name] act_nuc = act_chain[name] for prop in [ - 'name', 'half_life', 'decay_energy', 'reactions', - 'decay_modes', 'yield_data', 'yield_energies', - ]: + "name", + "half_life", + "decay_energy", + "reactions", + "decay_modes", + "yield_data", + "yield_energies", + ]: assert getattr(act_nuc, prop) == getattr(ref_nuc, prop), prop def test_operator_fiss_q(): """Make sure fission q values can be set""" - new_q = {"U235": 2.0E8, "U238": 2.0E8, "U234": 5.0E7} + new_q = {"U235": 2.0e8, "U238": 2.0e8, "U234": 5.0e7} operator = BareDepleteOperator(chain_file=CHAIN_PATH, fission_q=new_q) mod_chain = operator.chain for name, q in new_q.items(): chain_nuc = mod_chain[name] for rx in chain_nuc.reactions: - if rx.type == 'fission': + if rx.type == "fission": assert rx.Q == q diff --git a/tests/unit_tests/test_deplete_restart.py b/tests/unit_tests/test_deplete_restart.py index e8bfc062a08..f298bb35f8d 100644 --- a/tests/unit_tests/test_deplete_restart.py +++ b/tests/unit_tests/test_deplete_restart.py @@ -73,8 +73,7 @@ def test_restart(run_in_tmpdir, scheme): bundle.solver(operator, [0.75], 1.0).integrate() # restart - prev_res = openmc.deplete.Results( - operator.output_dir / "depletion_results.h5") + prev_res = openmc.deplete.Results(operator.output_dir / "depletion_results.h5") operator = dummy_operator.DummyOperator(prev_res) # take second step @@ -82,8 +81,7 @@ def test_restart(run_in_tmpdir, scheme): # compare results - results = openmc.deplete.Results( - operator.output_dir / "depletion_results.h5") + results = openmc.deplete.Results(operator.output_dir / "depletion_results.h5") _t, y1 = results.get_atoms("1", "1") _t, y2 = results.get_atoms("1", "2") diff --git a/tests/unit_tests/test_deplete_resultslist.py b/tests/unit_tests/test_deplete_resultslist.py index b22a786509e..bc8282c1849 100644 --- a/tests/unit_tests/test_deplete_resultslist.py +++ b/tests/unit_tests/test_deplete_resultslist.py @@ -11,24 +11,29 @@ @pytest.fixture def res(): """Load the reference results""" - filename = (Path(__file__).parents[1] / 'regression_tests' / 'deplete_with_transport' - / 'test_reference.h5') + filename = ( + Path(__file__).parents[1] + / "regression_tests" + / "deplete_with_transport" + / "test_reference.h5" + ) return openmc.deplete.Results(filename) + def test_get_activity(res): """Tests evaluating activity""" t, a = res.get_activity("1") t_ref = np.array([0.0, 1296000.0, 2592000.0, 3888000.0]) - a_ref = np.array( - [1.25167956e+06, 3.71938527e+11, 4.43264300e+11, 3.55547176e+11]) + a_ref = np.array([1.25167956e06, 3.71938527e11, 4.43264300e11, 3.55547176e11]) np.testing.assert_allclose(t, t_ref) np.testing.assert_allclose(a, a_ref) # Check by_nuclide a_xe135_ref = np.array( - [2.106574218e+05, 1.227519888e+11, 1.177491828e+11, 1.031986176e+11]) + [2.106574218e05, 1.227519888e11, 1.177491828e11, 1.031986176e11] + ) t_nuc, a_nuc = res.get_activity("1", by_nuclide=True) a_xe135 = np.array([a_nuc_i["Xe135"] for a_nuc_i in a_nuc]) @@ -42,8 +47,7 @@ def test_get_atoms(res): t, n = res.get_atoms("1", "Xe135") t_ref = np.array([0.0, 1296000.0, 2592000.0, 3888000.0]) - n_ref = np.array( - [6.67473282e+08, 3.88942731e+14, 3.73091215e+14, 3.26987387e+14]) + n_ref = np.array([6.67473282e08, 3.88942731e14, 3.73091215e14, 3.26987387e14]) np.testing.assert_allclose(t, t_ref) np.testing.assert_allclose(n, n_ref) @@ -67,11 +71,10 @@ def test_get_atoms(res): def test_get_decay_heat(res): """Tests evaluating decay heat.""" # Set chain file for testing - openmc.config['chain_file'] = Path(__file__).parents[1] / 'chain_simple.xml' + openmc.config["chain_file"] = Path(__file__).parents[1] / "chain_simple.xml" t_ref = np.array([0.0, 1296000.0, 2592000.0, 3888000.0]) - dh_ref = np.array( - [1.27933813e-09, 5.85347232e-03, 7.38773010e-03, 5.79954067e-03]) + dh_ref = np.array([1.27933813e-09, 5.85347232e-03, 7.38773010e-03, 5.79954067e-03]) t, dh = res.get_decay_heat("1") @@ -80,7 +83,8 @@ def test_get_decay_heat(res): # Check by nuclide dh_xe135_ref = np.array( - [1.27933813e-09, 7.45481920e-04, 7.15099509e-04, 6.26732849e-04]) + [1.27933813e-09, 7.45481920e-04, 7.15099509e-04, 6.26732849e-04] + ) t_nuc, dh_nuc = res.get_decay_heat("1", by_nuclide=True) dh_nuc_xe135 = np.array([dh_nuc_i["Xe135"] for dh_nuc_i in dh_nuc]) @@ -94,11 +98,10 @@ def test_get_mass(res): t, n = res.get_mass("1", "Xe135") t_ref = np.array([0.0, 1296000.0, 2592000.0, 3888000.0]) - n_ref = np.array( - [6.67473282e+08, 3.88942731e+14, 3.73091215e+14, 3.26987387e+14]) + n_ref = np.array([6.67473282e08, 3.88942731e14, 3.73091215e14, 3.26987387e14]) # Get g - n_ref *= openmc.data.atomic_mass('Xe135') / openmc.data.AVOGADRO + n_ref *= openmc.data.atomic_mass("Xe135") / openmc.data.AVOGADRO np.testing.assert_allclose(t, t_ref) np.testing.assert_allclose(n, n_ref) @@ -123,7 +126,7 @@ 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.88942731e+14, 3.73091215e+14, 3.26987387e+14] + n_ref = [6.67473282e08, 3.88942731e14, 3.73091215e14, 3.26987387e14] xs_ref = [2.53336104e-05, 4.21747011e-05, 3.48616127e-05, 3.61775563e-05] np.testing.assert_allclose(t, t_ref) @@ -133,11 +136,11 @@ def test_get_reaction_rate(res): def test_get_keff(res): """Tests evaluating keff.""" t, k = res.get_keff() - t_min, k = res.get_keff(time_units='min') + t_min, k = res.get_keff(time_units="min") t_ref = [0.0, 1296000.0, 2592000.0, 3888000.0] k_ref = [1.1596402556, 1.1914183335, 1.2292570871, 1.1797030302] - u_ref = [0.0270680649, 0.0219163444, 0.024268508 , 0.0221401194] + u_ref = [0.0270680649, 0.0219163444, 0.024268508, 0.0221401194] np.testing.assert_allclose(t, t_ref) np.testing.assert_allclose(t_min * 60, t_ref) @@ -165,12 +168,11 @@ def test_get_steps(unit): for ix in range(times.size): res = openmc.deplete.StepResult() - res.time = times[ix:ix + 1] * conversion_to_seconds + res.time = times[ix : ix + 1] * conversion_to_seconds results.append(res) for expected, value in enumerate(times): - actual = results.get_step_where( - value, time_units=unit, atol=0, rtol=0) + actual = results.get_step_where(value, time_units=unit, atol=0, rtol=0) assert actual == expected, (value, results[actual].time[0]) with pytest.raises(ValueError): @@ -182,7 +184,7 @@ def test_get_steps(unit): results.get_step_where(times[-1] + 1, time_units=unit, atol=0, rtol=0) # Grab intermediate points with a small offset - delta = (times[1] - times[0]) + delta = times[1] - times[0] offset = delta * 0.1 for expected, value in enumerate(times[1:-1], start=1): # Shoot a little low and a little high @@ -190,23 +192,25 @@ def test_get_steps(unit): target = value + mult * offset # Compare using absolute and relative tolerances actual = results.get_step_where( - target, time_units=unit, atol=offset * 2, rtol=inf) - assert actual == expected, ( - target, times[actual], times[expected], offset) + target, time_units=unit, atol=offset * 2, rtol=inf + ) + assert actual == expected, (target, times[actual], times[expected], offset) actual = results.get_step_where( - target, time_units=unit, atol=inf, rtol=offset / value) - assert actual == expected, ( - target, times[actual], times[expected], offset) + target, time_units=unit, atol=inf, rtol=offset / value + ) + assert actual == expected, (target, times[actual], times[expected], offset) # Check that the lower index is returned for the exact mid-point target = value + delta * 0.5 actual = results.get_step_where( - target, time_units=unit, atol=delta, rtol=delta / value) + target, time_units=unit, atol=delta, rtol=delta / value + ) assert actual == expected # Shoot way over with no tolerance -> just give closest value actual = results.get_step_where( - times[-1] * 100, time_units=unit, atol=inf, rtol=inf) + times[-1] * 100, time_units=unit, atol=inf, rtol=inf + ) assert actual == times.size - 1 @@ -219,6 +223,6 @@ def test_stepresult_get_material(res): # Spot check number densities densities = mat1.get_nuclide_atom_densities() - assert densities['Xe135'] == pytest.approx(1e-14) - assert densities['I135'] == pytest.approx(1e-21) - assert densities['U234'] == pytest.approx(1.00506e-05) + assert densities["Xe135"] == pytest.approx(1e-14) + assert densities["I135"] == pytest.approx(1e-21) + assert densities["U234"] == pytest.approx(1.00506e-05) diff --git a/tests/unit_tests/test_deplete_transfer_rates.py b/tests/unit_tests/test_deplete_transfer_rates.py index 140777cd6f9..40289ee632b 100644 --- a/tests/unit_tests/test_deplete_transfer_rates.py +++ b/tests/unit_tests/test_deplete_transfer_rates.py @@ -9,11 +9,16 @@ import openmc from openmc.deplete import CoupledOperator from openmc.deplete.transfer_rates import TransferRates -from openmc.deplete.abc import (_SECONDS_PER_MINUTE, _SECONDS_PER_HOUR, - _SECONDS_PER_DAY, _SECONDS_PER_JULIAN_YEAR) +from openmc.deplete.abc import ( + _SECONDS_PER_MINUTE, + _SECONDS_PER_HOUR, + _SECONDS_PER_DAY, + _SECONDS_PER_JULIAN_YEAR, +) CHAIN_PATH = Path(__file__).parents[1] / "chain_simple.xml" + @pytest.fixture def model(): f = openmc.Material(name="f") @@ -35,13 +40,13 @@ def model(): radii = [0.42, 0.45] f.volume = np.pi * radii[0] ** 2 - w.volume = np.pi * (radii[1]**2 - radii[0]**2) + w.volume = np.pi * (radii[1] ** 2 - radii[0] ** 2) h.volume = 1 materials = openmc.Materials([f, w, h]) surf_f = openmc.Sphere(r=radii[0]) - surf_w = openmc.Sphere(r=radii[1], boundary_type='vacuum') - surf_h = openmc.Sphere(x0=10, r=1, boundary_type='vacuum') + surf_w = openmc.Sphere(r=radii[1], boundary_type="vacuum") + surf_h = openmc.Sphere(x0=10, r=1, boundary_type="vacuum") cell_f = openmc.Cell(fill=f, region=-surf_f) cell_w = openmc.Cell(fill=w, region=+surf_f & -surf_w) cell_h = openmc.Cell(fill=h, region=-surf_h) @@ -54,20 +59,30 @@ def model(): return openmc.Model(geometry, materials, settings) -@pytest.mark.parametrize("case_name, transfer_rates", [ - ('elements', {'U': 0.01, 'Xe': 0.1}), - ('nuclides', {'I135': 0.01, 'Gd156': 0.1, 'Gd157': 0.01}), - ('nuclides_elements', {'I135': 0.01, 'Gd156': 0.1, 'Gd157': 0.01, 'U': 0.01, - 'Xe': 0.1}), - ('elements_nuclides', {'U': 0.01, 'Xe': 0.1, 'I135': 0.01, 'Gd156': 0.1, - 'Gd157': 0.01}), - ('multiple_transfer', {'U': 0.01, 'Xe': 0.1, 'I135': 0.01, 'Gd156': 0.1, - 'Gd157': 0.01}), - ('rates_invalid_1', {'Gd': 0.01, 'Gd157': 0.01, 'Gd156': 0.01}), - ('rates_invalid_2', {'Gd156': 0.01, 'Gd157': 0.01, 'Gd': 0.01}), - ('rates_invalid_3', {'Gb156': 0.01}), - ('rates_invalid_4', {'Gb': 0.01}) - ]) + +@pytest.mark.parametrize( + "case_name, transfer_rates", + [ + ("elements", {"U": 0.01, "Xe": 0.1}), + ("nuclides", {"I135": 0.01, "Gd156": 0.1, "Gd157": 0.01}), + ( + "nuclides_elements", + {"I135": 0.01, "Gd156": 0.1, "Gd157": 0.01, "U": 0.01, "Xe": 0.1}, + ), + ( + "elements_nuclides", + {"U": 0.01, "Xe": 0.1, "I135": 0.01, "Gd156": 0.1, "Gd157": 0.01}, + ), + ( + "multiple_transfer", + {"U": 0.01, "Xe": 0.1, "I135": 0.01, "Gd156": 0.1, "Gd157": 0.01}, + ), + ("rates_invalid_1", {"Gd": 0.01, "Gd157": 0.01, "Gd156": 0.01}), + ("rates_invalid_2", {"Gd156": 0.01, "Gd157": 0.01, "Gd": 0.01}), + ("rates_invalid_3", {"Gb156": 0.01}), + ("rates_invalid_4", {"Gb": 0.01}), + ], +) def test_get_set(model, case_name, transfer_rates): """Tests the get/set methods""" @@ -76,95 +91,131 @@ def test_get_set(model, case_name, transfer_rates): transfer = TransferRates(op, model) # Test by Openmc material, material name and material id - material, dest_material, dest_material2 = [m for m in model.materials - if m.depletable] + material, dest_material, dest_material2 = [ + m for m in model.materials if m.depletable + ] for material_input in [material, material.name, material.id]: - for dest_material_input in [dest_material, dest_material.name, - dest_material.id]: - if case_name == 'rates_invalid_1': - with pytest.raises(ValueError, match='Cannot add transfer ' - 'rate for nuclide Gd157 to material 1 ' - 'where element Gd already has a ' - 'transfer rate.'): + for dest_material_input in [ + dest_material, + dest_material.name, + dest_material.id, + ]: + if case_name == "rates_invalid_1": + with pytest.raises( + ValueError, + match="Cannot add transfer " + "rate for nuclide Gd157 to material 1 " + "where element Gd already has a " + "transfer rate.", + ): for component, transfer_rate in transfer_rates.items(): - transfer.set_transfer_rate(material_input, - [component], - transfer_rate) - elif case_name == 'rates_invalid_2': - with pytest.raises(ValueError, match='Cannot add transfer ' - f'rate for element Gd to material 1 with ' - r'transfer rate\(s\) for nuclide\(s\) ' - 'Gd156, Gd157.'): + transfer.set_transfer_rate( + material_input, [component], transfer_rate + ) + elif case_name == "rates_invalid_2": + with pytest.raises( + ValueError, + match="Cannot add transfer " + f"rate for element Gd to material 1 with " + r"transfer rate\(s\) for nuclide\(s\) " + "Gd156, Gd157.", + ): for component, transfer_rate in transfer_rates.items(): - transfer.set_transfer_rate(material_input, - [component], - transfer_rate) - elif case_name == 'rates_invalid_3': - with pytest.raises(ValueError, match='Gb156 is not a valid ' - 'nuclide or element.'): + transfer.set_transfer_rate( + material_input, [component], transfer_rate + ) + elif case_name == "rates_invalid_3": + with pytest.raises( + ValueError, match="Gb156 is not a valid " "nuclide or element." + ): for component, transfer_rate in transfer_rates.items(): - transfer.set_transfer_rate(material_input, - [component], - transfer_rate) - elif case_name == 'rates_invalid_4': - with pytest.raises(ValueError, match='Gb is not a valid ' - 'nuclide or element.'): + transfer.set_transfer_rate( + material_input, [component], transfer_rate + ) + elif case_name == "rates_invalid_4": + with pytest.raises( + ValueError, match="Gb is not a valid " "nuclide or element." + ): for component, transfer_rate in transfer_rates.items(): - transfer.set_transfer_rate(material_input, - [component], - transfer_rate) + transfer.set_transfer_rate( + material_input, [component], transfer_rate + ) else: for component, transfer_rate in transfer_rates.items(): - transfer.set_transfer_rate(material_input, [component], - transfer_rate, - destination_material=\ - dest_material_input) - assert transfer.get_transfer_rate( - material_input, component)[0] == transfer_rate - assert transfer.get_destination_material( - material_input, component)[0] == str(dest_material.id) - assert transfer.get_components(material_input) == \ - transfer_rates.keys() - - if case_name == 'multiple_transfer': - for dest2_material_input in [dest_material2, dest_material2.name, - dest_material2.id]: + transfer.set_transfer_rate( + material_input, + [component], + transfer_rate, + destination_material=dest_material_input, + ) + assert ( + transfer.get_transfer_rate(material_input, component)[0] + == transfer_rate + ) + assert transfer.get_destination_material(material_input, component)[ + 0 + ] == str(dest_material.id) + assert transfer.get_components(material_input) == transfer_rates.keys() + + if case_name == "multiple_transfer": + for dest2_material_input in [ + dest_material2, + dest_material2.name, + dest_material2.id, + ]: for component, transfer_rate in transfer_rates.items(): - transfer.set_transfer_rate(material_input, [component], - transfer_rate, - destination_material=\ - dest2_material_input) - for id, dest_mat in zip([0,1],[dest_material,dest_material2]): - assert transfer.get_transfer_rate( - material_input, component)[id] == transfer_rate + transfer.set_transfer_rate( + material_input, + [component], + transfer_rate, + destination_material=dest2_material_input, + ) + for id, dest_mat in zip( + [0, 1], [dest_material, dest_material2] + ): + assert ( + transfer.get_transfer_rate(material_input, component)[ + id + ] + == transfer_rate + ) assert transfer.get_destination_material( - material_input, component)[id] == str(dest_mat.id) - -@pytest.mark.parametrize("transfer_rate_units, unit_conv", [ - ('1/s', 1), - ('1/sec', 1), - ('1/min', _SECONDS_PER_MINUTE), - ('1/minute', _SECONDS_PER_MINUTE), - ('1/h', _SECONDS_PER_HOUR), - ('1/hr', _SECONDS_PER_HOUR), - ('1/hour', _SECONDS_PER_HOUR), - ('1/d', _SECONDS_PER_DAY), - ('1/day', _SECONDS_PER_DAY), - ('1/a', _SECONDS_PER_JULIAN_YEAR), - ('1/year', _SECONDS_PER_JULIAN_YEAR), - ]) + material_input, component + )[id] == str(dest_mat.id) + + +@pytest.mark.parametrize( + "transfer_rate_units, unit_conv", + [ + ("1/s", 1), + ("1/sec", 1), + ("1/min", _SECONDS_PER_MINUTE), + ("1/minute", _SECONDS_PER_MINUTE), + ("1/h", _SECONDS_PER_HOUR), + ("1/hr", _SECONDS_PER_HOUR), + ("1/hour", _SECONDS_PER_HOUR), + ("1/d", _SECONDS_PER_DAY), + ("1/day", _SECONDS_PER_DAY), + ("1/a", _SECONDS_PER_JULIAN_YEAR), + ("1/year", _SECONDS_PER_JULIAN_YEAR), + ], +) def test_units(transfer_rate_units, unit_conv, model): - """ Units testing""" + """Units testing""" # create transfer rate Xe - components = ['Xe', 'U235'] + components = ["Xe", "U235"] transfer_rate = 1e-5 op = CoupledOperator(model, CHAIN_PATH) transfer = TransferRates(op, model) for component in components: - transfer.set_transfer_rate('f', [component], transfer_rate * unit_conv, - transfer_rate_units=transfer_rate_units) - assert transfer.get_transfer_rate('f', component)[0] == transfer_rate + transfer.set_transfer_rate( + "f", + [component], + transfer_rate * unit_conv, + transfer_rate_units=transfer_rate_units, + ) + assert transfer.get_transfer_rate("f", component)[0] == transfer_rate def test_transfer(run_in_tmpdir, model): @@ -172,18 +223,17 @@ def test_transfer(run_in_tmpdir, model): but only transfer rates""" # create transfer rate for U - element = ['U'] + element = ["U"] transfer_rate = 1e-5 op = CoupledOperator(model, CHAIN_PATH) - integrator = openmc.deplete.PredictorIntegrator( - op, [1,1], 0.0, timestep_units = 'd') - integrator.add_transfer_rate('f', element, transfer_rate) + integrator = openmc.deplete.PredictorIntegrator(op, [1, 1], 0.0, timestep_units="d") + integrator.add_transfer_rate("f", element, transfer_rate) integrator.integrate() # Get number of U238 atoms from results - results = openmc.deplete.Results('depletion_results.h5') + results = openmc.deplete.Results("depletion_results.h5") _, atoms = results.get_atoms(model.materials[0], "U238") # Ensure number of atoms equal transfer decay - assert atoms[1] == pytest.approx(atoms[0]*exp(-transfer_rate*3600*24)) - assert atoms[2] == pytest.approx(atoms[1]*exp(-transfer_rate*3600*24)) + assert atoms[1] == pytest.approx(atoms[0] * exp(-transfer_rate * 3600 * 24)) + assert atoms[2] == pytest.approx(atoms[1] * exp(-transfer_rate * 3600 * 24)) diff --git a/tests/unit_tests/test_element.py b/tests/unit_tests/test_element.py index d3555701e2c..11873d7779b 100644 --- a/tests/unit_tests/test_element.py +++ b/tests/unit_tests/test_element.py @@ -5,84 +5,86 @@ def test_expand_no_enrichment(): - """ Expand Li in natural compositions""" - lithium = openmc.Element('Li') + """Expand Li in natural compositions""" + lithium = openmc.Element("Li") # Verify the expansion into ATOMIC fraction against natural composition - for isotope in lithium.expand(100.0, 'ao'): + for isotope in lithium.expand(100.0, "ao"): assert isotope[1] == approx(NATURAL_ABUNDANCE[isotope[0]] * 100.0) # Verify the expansion into WEIGHT fraction against natural composition - natural = {'Li6': NATURAL_ABUNDANCE['Li6'] * atomic_mass('Li6'), - 'Li7': NATURAL_ABUNDANCE['Li7'] * atomic_mass('Li7')} + natural = { + "Li6": NATURAL_ABUNDANCE["Li6"] * atomic_mass("Li6"), + "Li7": NATURAL_ABUNDANCE["Li7"] * atomic_mass("Li7"), + } li_am = sum(natural.values()) for key in natural: natural[key] /= li_am - for isotope in lithium.expand(100.0, 'wo'): + for isotope in lithium.expand(100.0, "wo"): assert isotope[1] == approx(natural[isotope[0]] * 100.0) def test_expand_enrichment(): - """ Expand and verify enrichment of Li """ - lithium = openmc.Element('Li') + """Expand and verify enrichment of Li""" + lithium = openmc.Element("Li") # Verify the enrichment by atoms - ref = {'Li6': 75.0, 'Li7': 25.0} - for isotope in lithium.expand(100.0, 'ao', 25.0, 'Li7', 'ao'): + ref = {"Li6": 75.0, "Li7": 25.0} + for isotope in lithium.expand(100.0, "ao", 25.0, "Li7", "ao"): assert isotope[1] == approx(ref[isotope[0]]) # Verify the enrichment by weight - for isotope in lithium.expand(100.0, 'wo', 25.0, 'Li7', 'wo'): + for isotope in lithium.expand(100.0, "wo", 25.0, "Li7", "wo"): assert isotope[1] == approx(ref[isotope[0]]) def test_expand_no_isotopes(): """Test that correct warning is raised for elements with no isotopes""" - with warns(UserWarning, match='No naturally occurring'): - element = openmc.Element('Tc') - element.expand(100.0, 'ao') + with warns(UserWarning, match="No naturally occurring"): + element = openmc.Element("Tc") + element.expand(100.0, "ao") def test_expand_exceptions(): - """ Test that correct exceptions are raised for invalid input """ + """Test that correct exceptions are raised for invalid input""" # 1 Isotope Element with raises(ValueError): - element = openmc.Element('Be') - element.expand(70.0, 'ao', 4.0, 'Be9') + element = openmc.Element("Be") + element.expand(70.0, "ao", 4.0, "Be9") # 3 Isotope Element with raises(ValueError): - element = openmc.Element('Cr') - element.expand(70.0, 'ao', 4.0, 'Cr52') + element = openmc.Element("Cr") + element.expand(70.0, "ao", 4.0, "Cr52") # Non-present Enrichment Target with raises(ValueError): - element = openmc.Element('H') - element.expand(70.0, 'ao', 4.0, 'H4') + element = openmc.Element("H") + element.expand(70.0, "ao", 4.0, "H4") # Enrichment Procedure for Uranium if not Uranium with raises(ValueError): - element = openmc.Element('Li') - element.expand(70.0, 'ao', 4.0) + element = openmc.Element("Li") + element.expand(70.0, "ao", 4.0) # Missing Enrichment Target with raises(ValueError): - element = openmc.Element('Li') - element.expand(70.0, 'ao', 4.0, enrichment_type='ao') + element = openmc.Element("Li") + element.expand(70.0, "ao", 4.0, enrichment_type="ao") # Invalid Enrichment Type Entry with raises(ValueError): - element = openmc.Element('Li') - element.expand(70.0, 'ao', 4.0, 'Li7', 'Grand Moff Tarkin') + element = openmc.Element("Li") + element.expand(70.0, "ao", 4.0, "Li7", "Grand Moff Tarkin") # Trying to enrich Uranium with raises(ValueError): - element = openmc.Element('U') - element.expand(70.0, 'ao', 4.0, 'U235', 'wo') + element = openmc.Element("U") + element.expand(70.0, "ao", 4.0, "U235", "wo") # Trying to enrich Uranium with wrong enrichment_target with raises(ValueError): - element = openmc.Element('U') - element.expand(70.0, 'ao', 4.0, enrichment_type='ao') + element = openmc.Element("U") + element.expand(70.0, "ao", 4.0, enrichment_type="ao") diff --git a/tests/unit_tests/test_element_wo.py b/tests/unit_tests/test_element_wo.py index 52fc80a378c..52d4e6a0088 100644 --- a/tests/unit_tests/test_element_wo.py +++ b/tests/unit_tests/test_element_wo.py @@ -11,28 +11,32 @@ def test_element_wo(): # element.expand() method expands elements with the proper nuclide # compositions. - h_am = (NATURAL_ABUNDANCE['H1'] * atomic_mass('H1') + - NATURAL_ABUNDANCE['H2'] * atomic_mass('H2')) - o_am = (NATURAL_ABUNDANCE['O17'] * atomic_mass('O17') + - (NATURAL_ABUNDANCE['O16'] + NATURAL_ABUNDANCE['O18']) - * atomic_mass('O16')) + h_am = NATURAL_ABUNDANCE["H1"] * atomic_mass("H1") + NATURAL_ABUNDANCE[ + "H2" + ] * atomic_mass("H2") + o_am = NATURAL_ABUNDANCE["O17"] * atomic_mass("O17") + ( + NATURAL_ABUNDANCE["O16"] + NATURAL_ABUNDANCE["O18"] + ) * atomic_mass("O16") water_am = 2 * h_am + o_am water = Material() - water.add_element('O', o_am / water_am, 'wo') - water.add_element('H', 2 * h_am / water_am, 'wo') + water.add_element("O", o_am / water_am, "wo") + water.add_element("H", 2 * h_am / water_am, "wo") densities = water.get_nuclide_densities() for nuc in densities.keys(): - assert nuc in ('H1', 'H2', 'O16', 'O17') + assert nuc in ("H1", "H2", "O16", "O17") - if nuc in ('H1', 'H2'): + if nuc in ("H1", "H2"): val = 2 * NATURAL_ABUNDANCE[nuc] * atomic_mass(nuc) / water_am assert densities[nuc][1] == pytest.approx(val) - if nuc == 'O16': - val = (NATURAL_ABUNDANCE[nuc] + NATURAL_ABUNDANCE['O18']) \ - * atomic_mass(nuc) / water_am + if nuc == "O16": + val = ( + (NATURAL_ABUNDANCE[nuc] + NATURAL_ABUNDANCE["O18"]) + * atomic_mass(nuc) + / water_am + ) assert densities[nuc][1] == pytest.approx(val) - if nuc == 'O17': + if nuc == "O17": val = NATURAL_ABUNDANCE[nuc] * atomic_mass(nuc) / water_am assert densities[nuc][1] == pytest.approx(val) diff --git a/tests/unit_tests/test_endf.py b/tests/unit_tests/test_endf.py index 1d4982054c8..923c317aee2 100644 --- a/tests/unit_tests/test_endf.py +++ b/tests/unit_tests/test_endf.py @@ -3,29 +3,29 @@ def test_float_endf(): - assert endf.float_endf('+3.2146') == approx(3.2146) - assert endf.float_endf('.12345') == approx(0.12345) - assert endf.float_endf('6.022+23') == approx(6.022e23) - assert endf.float_endf('6.022-23') == approx(6.022e-23) - assert endf.float_endf(' +1.01+ 2') == approx(101.0) - assert endf.float_endf(' -1.01- 2') == approx(-0.0101) - assert endf.float_endf('+ 2 . 3+ 1') == approx(23.0) - assert endf.float_endf('-7 .8 -1') == approx(-0.78) - assert endf.float_endf('3.14e0') == approx(3.14) - assert endf.float_endf('3.14E0') == approx(3.14) - assert endf.float_endf('3.14e-1') == approx(0.314) - assert endf.float_endf('3.14d0') == approx(3.14) - assert endf.float_endf('3.14D0') == approx(3.14) - assert endf.float_endf('3.14d-1') == approx(0.314) - assert endf.float_endf('1+2') == approx(100.0) - assert endf.float_endf('-1+2') == approx(-100.0) - assert endf.float_endf('1.+2') == approx(100.0) - assert endf.float_endf('-1.+2') == approx(-100.0) - assert endf.float_endf(' ') == 0.0 - assert endf.float_endf('9.876540000000000') == approx(9.87654) - assert endf.float_endf('-2.225002+6') == approx(-2.225002e+6) + assert endf.float_endf("+3.2146") == approx(3.2146) + assert endf.float_endf(".12345") == approx(0.12345) + assert endf.float_endf("6.022+23") == approx(6.022e23) + assert endf.float_endf("6.022-23") == approx(6.022e-23) + assert endf.float_endf(" +1.01+ 2") == approx(101.0) + assert endf.float_endf(" -1.01- 2") == approx(-0.0101) + assert endf.float_endf("+ 2 . 3+ 1") == approx(23.0) + assert endf.float_endf("-7 .8 -1") == approx(-0.78) + assert endf.float_endf("3.14e0") == approx(3.14) + assert endf.float_endf("3.14E0") == approx(3.14) + assert endf.float_endf("3.14e-1") == approx(0.314) + assert endf.float_endf("3.14d0") == approx(3.14) + assert endf.float_endf("3.14D0") == approx(3.14) + assert endf.float_endf("3.14d-1") == approx(0.314) + assert endf.float_endf("1+2") == approx(100.0) + assert endf.float_endf("-1+2") == approx(-100.0) + assert endf.float_endf("1.+2") == approx(100.0) + assert endf.float_endf("-1.+2") == approx(-100.0) + assert endf.float_endf(" ") == 0.0 + assert endf.float_endf("9.876540000000000") == approx(9.87654) + assert endf.float_endf("-2.225002+6") == approx(-2.225002e6) def test_int_endf(): - assert endf.int_endf(' ') == 0 - assert endf.int_endf('+4032') == 4032 + assert endf.int_endf(" ") == 0 + assert endf.int_endf("+4032") == 4032 diff --git a/tests/unit_tests/test_energy_cutoff.py b/tests/unit_tests/test_energy_cutoff.py index 45333f2e1dc..fbf27291544 100644 --- a/tests/unit_tests/test_energy_cutoff.py +++ b/tests/unit_tests/test_energy_cutoff.py @@ -9,30 +9,30 @@ def inf_medium_model(cutoff_energy, source_energy): model = openmc.Model() m = openmc.Material() - m.add_nuclide('Zr90', 1.0) - m.set_density('g/cm3', 1.0) + m.add_nuclide("Zr90", 1.0) + m.set_density("g/cm3", 1.0) - sph = openmc.Sphere(r=100.0, boundary_type='reflective') + sph = openmc.Sphere(r=100.0, boundary_type="reflective") cell = openmc.Cell(fill=m, region=-sph) model.geometry = openmc.Geometry([cell]) - model.settings.run_mode = 'fixed source' + model.settings.run_mode = "fixed source" model.settings.source = openmc.IndependentSource( - particle='photon', + particle="photon", energy=openmc.stats.Discrete([source_energy], [1.0]), ) model.settings.particles = 100 model.settings.batches = 10 - model.settings.cutoff = {'energy_photon': cutoff_energy} + model.settings.cutoff = {"energy_photon": cutoff_energy} - tally_flux = openmc.Tally(name='flux') + tally_flux = openmc.Tally(name="flux") tally_flux.filters = [ openmc.EnergyFilter([0.0, cutoff_energy, source_energy]), - openmc.ParticleFilter(['photon']) + openmc.ParticleFilter(["photon"]), ] - tally_flux.scores = ['flux'] - tally_heating = openmc.Tally(name='heating') - tally_heating.scores = ['heating'] + tally_flux.scores = ["flux"] + tally_heating = openmc.Tally(name="heating") + tally_heating.scores = ["heating"] model.tallies = openmc.Tallies([tally_flux, tally_heating]) return model @@ -51,8 +51,8 @@ def test_energy_cutoff(run_in_tmpdir): # Get resulting flux and heating values with openmc.StatePoint(statepoint_path) as sp: - flux = sp.get_tally(name='flux').mean.ravel() - heating = sp.get_tally(name='heating').mean.ravel() + flux = sp.get_tally(name="flux").mean.ravel() + heating = sp.get_tally(name="heating").mean.ravel() # There should be no flux below the cutoff energy (first bin in the tally) assert flux[0] == 0.0 diff --git a/tests/unit_tests/test_filter_mesh.py b/tests/unit_tests/test_filter_mesh.py index a8bd4996dd7..988674d2264 100644 --- a/tests/unit_tests/test_filter_mesh.py +++ b/tests/unit_tests/test_filter_mesh.py @@ -11,10 +11,10 @@ def test_spherical_mesh_estimators(run_in_tmpdir): """Test that collision/tracklength estimators agree for SphericalMesh""" mat = openmc.Material() - mat.add_nuclide('U235', 1.0) - mat.set_density('g/cm3', 10.0) + mat.add_nuclide("U235", 1.0) + mat.set_density("g/cm3", 10.0) - sphere = openmc.Sphere(r=10.0, boundary_type='vacuum') + sphere = openmc.Sphere(r=10.0, boundary_type="vacuum") cell = openmc.Cell(fill=mat, region=-sphere) model = openmc.Model() model.geometry = openmc.Geometry([cell]) @@ -22,21 +22,17 @@ def test_spherical_mesh_estimators(run_in_tmpdir): model.settings.inactive = 10 model.settings.batches = 20 - sph_mesh = openmc.SphericalMesh( - r_grid=np.linspace(0.0, 5.0**3, 20)**(1/3) - ) + sph_mesh = openmc.SphericalMesh(r_grid=np.linspace(0.0, 5.0**3, 20) ** (1 / 3)) tally1 = openmc.Tally() tally1.filters = [openmc.MeshFilter(sph_mesh)] - tally1.scores = ['flux'] - tally1.estimator = 'collision' + tally1.scores = ["flux"] + tally1.estimator = "collision" - sph_mesh = openmc.SphericalMesh( - r_grid=np.linspace(0.0, 5.0**3, 20)**(1/3) - ) + sph_mesh = openmc.SphericalMesh(r_grid=np.linspace(0.0, 5.0**3, 20) ** (1 / 3)) tally2 = openmc.Tally() tally2.filters = [openmc.MeshFilter(sph_mesh)] - tally2.scores = ['flux'] - tally2.estimator = 'tracklength' + tally2.scores = ["flux"] + tally2.estimator = "tracklength" model.tallies = openmc.Tallies([tally1, tally2]) @@ -58,18 +54,19 @@ def test_spherical_mesh_estimators(run_in_tmpdir): # Check that difference is within uncertainty diff = unumpy.nominal_values(delta) std_dev = unumpy.std_devs(delta) - assert np.all(diff < 3*std_dev) + assert np.all(diff < 3 * std_dev) def test_cylindrical_mesh_estimators(run_in_tmpdir): """Test that collision/tracklength estimators agree for CylindricalMesh""" mat = openmc.Material() - mat.add_nuclide('U235', 1.0) - mat.set_density('g/cm3', 10.0) + mat.add_nuclide("U235", 1.0) + mat.set_density("g/cm3", 10.0) - cyl = openmc.model.RightCircularCylinder((0., 0., -5.), 10., 10.0, - boundary_type='vacuum') + cyl = openmc.model.RightCircularCylinder( + (0.0, 0.0, -5.0), 10.0, 10.0, boundary_type="vacuum" + ) cell = openmc.Cell(fill=mat, region=-cyl) model = openmc.Model() model.geometry = openmc.Geometry([cell]) @@ -78,22 +75,20 @@ def test_cylindrical_mesh_estimators(run_in_tmpdir): model.settings.batches = 20 cyl_mesh = openmc.CylindricalMesh( - r_grid=np.linspace(0.0, 5.0**3, 20)**(1/3), - z_grid=[-5., 5.] + r_grid=np.linspace(0.0, 5.0**3, 20) ** (1 / 3), z_grid=[-5.0, 5.0] ) tally1 = openmc.Tally() tally1.filters = [openmc.MeshFilter(cyl_mesh)] - tally1.scores = ['flux'] - tally1.estimator = 'collision' + tally1.scores = ["flux"] + tally1.estimator = "collision" cyl_mesh = openmc.CylindricalMesh( - r_grid=np.linspace(0.0, 5.0**3, 20)**(1/3), - z_grid=[-5., 5.] + r_grid=np.linspace(0.0, 5.0**3, 20) ** (1 / 3), z_grid=[-5.0, 5.0] ) tally2 = openmc.Tally() tally2.filters = [openmc.MeshFilter(cyl_mesh)] - tally2.scores = ['flux'] - tally2.estimator = 'tracklength' + tally2.scores = ["flux"] + tally2.estimator = "tracklength" model.tallies = openmc.Tallies([tally1, tally2]) @@ -115,7 +110,7 @@ def test_cylindrical_mesh_estimators(run_in_tmpdir): # Check that difference is within uncertainty diff = unumpy.nominal_values(delta) std_dev = unumpy.std_devs(delta) - assert np.all(diff < 3*std_dev) + assert np.all(diff < 3 * std_dev) @pytest.mark.parametrize("scale", [0.1, 1.0, 1e2, 1e4, 1e5]) @@ -123,11 +118,13 @@ def test_cylindrical_mesh_coincident(scale, run_in_tmpdir): """Test for cylindrical mesh boundary being coincident with a cell boundary""" fuel = openmc.Material() - fuel.add_nuclide('U235', 1.) - fuel.set_density('g/cm3', 4.5) + fuel.add_nuclide("U235", 1.0) + fuel.set_density("g/cm3", 4.5) - zcyl = openmc.ZCylinder(r=1.25*scale) - box = openmc.model.RectangularPrism(4*scale, 4*scale, boundary_type='reflective') + zcyl = openmc.ZCylinder(r=1.25 * scale) + box = openmc.model.RectangularPrism( + 4 * scale, 4 * scale, boundary_type="reflective" + ) cell1 = openmc.Cell(fill=fuel, region=-zcyl) cell2 = openmc.Cell(fill=None, region=+zcyl & -box) model = openmc.Model() @@ -138,19 +135,17 @@ def test_cylindrical_mesh_coincident(scale, run_in_tmpdir): model.settings.inactive = 0 cyl_mesh = openmc.CylindricalMesh( - r_grid=[0., 1.25*scale], - phi_grid=[0., 2*math.pi], - z_grid=[-1e10, 1e10] + r_grid=[0.0, 1.25 * scale], phi_grid=[0.0, 2 * math.pi], z_grid=[-1e10, 1e10] ) cyl_mesh_filter = openmc.MeshFilter(cyl_mesh) cell_filter = openmc.CellFilter([cell1]) tally1 = openmc.Tally() tally1.filters = [cyl_mesh_filter] - tally1.scores = ['flux'] + tally1.scores = ["flux"] tally2 = openmc.Tally() tally2.filters = [cell_filter] - tally2.scores = ['flux'] + tally2.scores = ["flux"] model.tallies = openmc.Tallies([tally1, tally2]) # Run OpenMC @@ -172,13 +167,19 @@ def test_spherical_mesh_coincident(scale, run_in_tmpdir): """Test for spherical mesh boundary being coincident with a cell boundary""" fuel = openmc.Material() - fuel.add_nuclide('U235', 1.) - fuel.set_density('g/cm3', 4.5) + fuel.add_nuclide("U235", 1.0) + fuel.set_density("g/cm3", 4.5) - sph = openmc.Sphere(r=1.25*scale) + sph = openmc.Sphere(r=1.25 * scale) rcc = openmc.model.RectangularParallelepiped( - -2*scale, 2*scale, -2*scale, 2*scale, -2*scale, 2*scale, - boundary_type='reflective') + -2 * scale, + 2 * scale, + -2 * scale, + 2 * scale, + -2 * scale, + 2 * scale, + boundary_type="reflective", + ) cell1 = openmc.Cell(fill=fuel, region=-sph) cell2 = openmc.Cell(fill=None, region=+sph & -rcc) model = openmc.Model() @@ -189,9 +190,9 @@ def test_spherical_mesh_coincident(scale, run_in_tmpdir): model.settings.inactive = 0 sph_mesh = openmc.SphericalMesh( - r_grid=[0., 1.25*scale], - phi_grid=[0., 2*math.pi], - theta_grid=[0., math.pi], + r_grid=[0.0, 1.25 * scale], + phi_grid=[0.0, 2 * math.pi], + theta_grid=[0.0, math.pi], ) sph_mesh_filter = openmc.MeshFilter(sph_mesh) @@ -199,10 +200,10 @@ def test_spherical_mesh_coincident(scale, run_in_tmpdir): tally1 = openmc.Tally() tally1.filters = [sph_mesh_filter] - tally1.scores = ['flux'] + tally1.scores = ["flux"] tally2 = openmc.Tally() tally2.filters = [cell_filter] - tally2.scores = ['flux'] + tally2.scores = ["flux"] model.tallies = openmc.Tallies([tally1, tally2]) # Run OpenMC @@ -223,10 +224,10 @@ def test_get_reshaped_data(run_in_tmpdir): """Test that expanding MeshFilter dimensions works as expected""" mat = openmc.Material() - mat.add_nuclide('U235', 1.0) - mat.set_density('g/cm3', 10.0) + mat.add_nuclide("U235", 1.0) + mat.set_density("g/cm3", 10.0) - sphere = openmc.Sphere(r=10.0, boundary_type='vacuum') + sphere = openmc.Sphere(r=10.0, boundary_type="vacuum") cell = openmc.Cell(fill=mat, region=-sphere) model = openmc.Model() model.geometry = openmc.Geometry([cell]) @@ -235,16 +236,16 @@ def test_get_reshaped_data(run_in_tmpdir): model.settings.batches = 20 sph_mesh = openmc.SphericalMesh( - r_grid=np.linspace(0.0, 5.0**3, 20)**(1/3), + r_grid=np.linspace(0.0, 5.0**3, 20) ** (1 / 3), theta_grid=np.linspace(0, math.pi, 4), - phi_grid=np.linspace(0, 2*math.pi, 3) + phi_grid=np.linspace(0, 2 * math.pi, 3), ) tally1 = openmc.Tally() efilter = openmc.EnergyFilter([0, 1e5, 1e8]) meshfilter = openmc.MeshFilter(sph_mesh) assert meshfilter.shape == (19, 3, 2) tally1.filters = [efilter, meshfilter] - tally1.scores = ['flux'] + tally1.scores = ["flux"] model.tallies = openmc.Tallies([tally1]) @@ -257,5 +258,5 @@ def test_get_reshaped_data(run_in_tmpdir): data1 = t1.get_reshaped_data() data2 = t1.get_reshaped_data(expand_dims=True) - assert data1.shape == (2, 19*3*2, 1, 1) + assert data1.shape == (2, 19 * 3 * 2, 1, 1) assert data2.shape == (2, 19, 3, 2, 1, 1) diff --git a/tests/unit_tests/test_filter_meshborn.py b/tests/unit_tests/test_filter_meshborn.py index 62fa1174e75..e0909d77168 100644 --- a/tests/unit_tests/test_filter_meshborn.py +++ b/tests/unit_tests/test_filter_meshborn.py @@ -30,11 +30,11 @@ def model(): model.geometry = openmc.Geometry([core]) # Settings - model.settings.run_mode = 'fixed source' + model.settings.run_mode = "fixed source" model.settings.particles = 2000 model.settings.batches = 8 - distribution = openmc.stats.Box((0., -r, -r), (r, r, r)) + distribution = openmc.stats.Box((0.0, -r, -r), (r, r, r)) model.settings.source = openmc.IndependentSource(space=distribution) # ============================================================================= @@ -73,9 +73,13 @@ def test_estimator_consistency(model, run_in_tmpdir): # Get radial flux distribution with openmc.StatePoint(sp_filename) as sp: scatter_collision = sp.get_tally(name="scatter-collision").mean.ravel() - scatter_collision_std_dev = sp.get_tally(name="scatter-collision").std_dev.ravel() + scatter_collision_std_dev = sp.get_tally( + name="scatter-collision" + ).std_dev.ravel() scatter_tracklength = sp.get_tally(name="scatter-tracklength").mean.ravel() - scatter_tracklength_std_dev = sp.get_tally(name="scatter-tracklength").std_dev.ravel() + scatter_tracklength_std_dev = sp.get_tally( + name="scatter-tracklength" + ).std_dev.ravel() collision = unumpy.uarray(scatter_collision, scatter_collision_std_dev) tracklength = unumpy.uarray(scatter_tracklength, scatter_tracklength_std_dev) @@ -105,8 +109,8 @@ def test_xml_serialization(): repr(filter) elem = filter.to_xml_element() - assert elem.tag == 'filter' - assert elem.attrib['type'] == 'meshborn' + assert elem.tag == "filter" + assert elem.attrib["type"] == "meshborn" assert elem[0].text == "1" assert elem.get("translation") == "2.0 2.0 2.0" diff --git a/tests/unit_tests/test_filter_musurface.py b/tests/unit_tests/test_filter_musurface.py index ca0db71f0c6..8c9dcb69a09 100644 --- a/tests/unit_tests/test_filter_musurface.py +++ b/tests/unit_tests/test_filter_musurface.py @@ -2,7 +2,7 @@ def test_musurface(run_in_tmpdir): - sphere = openmc.Sphere(r=1.0, boundary_type='vacuum') + sphere = openmc.Sphere(r=1.0, boundary_type="vacuum") cell = openmc.Cell(region=-sphere) model = openmc.Model() model.geometry = openmc.Geometry([cell]) @@ -20,7 +20,7 @@ def test_musurface(run_in_tmpdir): filter2 = openmc.SurfaceFilter(sphere) tally = openmc.Tally() tally.filters = [filter1, filter2] - tally.scores = ['current'] + tally.scores = ["current"] model.tallies = openmc.Tallies([tally]) # Run OpenMC @@ -34,5 +34,3 @@ def test_musurface(run_in_tmpdir): assert current_mu[-1] == 1.0 for element in current_mu[:-1]: assert element == 0.0 - - diff --git a/tests/unit_tests/test_filters.py b/tests/unit_tests/test_filters.py index 55bb62075bd..8de2a5f7a68 100644 --- a/tests/unit_tests/test_filters.py +++ b/tests/unit_tests/test_filters.py @@ -3,14 +3,14 @@ from pytest import fixture, approx, raises -@fixture(scope='module') +@fixture(scope="module") def box_model(): model = openmc.model.Model() m = openmc.Material() - m.add_nuclide('U235', 1.0) - m.set_density('g/cm3', 1.0) + m.add_nuclide("U235", 1.0) + m.set_density("g/cm3", 1.0) - box = openmc.model.RectangularPrism(10., 10., boundary_type='vacuum') + box = openmc.model.RectangularPrism(10.0, 10.0, boundary_type="vacuum") c = openmc.Cell(fill=m, region=-box) model.geometry.root_universe = openmc.Universe(cells=[c]) @@ -31,9 +31,9 @@ def test_cell_instance(): # to_xml_element() elem = f.to_xml_element() - assert elem.tag == 'filter' - assert elem.attrib['type'] == 'cellinstance' - bins = [int(x) for x in elem.find('bins').text.split()] + assert elem.tag == "filter" + assert elem.attrib["type"] == "cellinstance" + bins = [int(x) for x in elem.find("bins").text.split()] assert all(x == c1.id for x in bins[:6:2]) assert all(x == c2.id for x in bins[6::2]) @@ -44,8 +44,8 @@ def test_cell_instance(): # get_pandas_dataframe() df = f.get_pandas_dataframe(f.num_bins, 1) - cells = df['cellinstance', 'cell'] - instances = df['cellinstance', 'instance'] + cells = df["cellinstance", "cell"] + instances = df["cellinstance", "instance"] assert cells.apply(lambda x: x in (c1.id, c2.id)).all() assert instances.apply(lambda x: x in (0, 1, 2)).all() @@ -62,8 +62,8 @@ def test_collision(): # to_xml_element() elem = f.to_xml_element() - assert elem.tag == 'filter' - assert elem.attrib['type'] == 'collision' + assert elem.tag == "filter" + assert elem.attrib["type"] == "collision" # from_xml_element() new_f = openmc.Filter.from_xml_element(elem) @@ -75,8 +75,8 @@ def test_legendre(): n = 5 f = openmc.LegendreFilter(n) assert f.order == n - assert f.bins[0] == 'P0' - assert f.bins[-1] == 'P5' + assert f.bins[0] == "P0" + assert f.bins[-1] == "P5" assert len(f.bins) == n + 1 # Make sure __repr__ works @@ -84,9 +84,9 @@ def test_legendre(): # to_xml_element() elem = f.to_xml_element() - assert elem.tag == 'filter' - assert elem.attrib['type'] == 'legendre' - assert elem.find('order').text == str(n) + assert elem.tag == "filter" + assert elem.attrib["type"] == "legendre" + assert elem.find("order").text == str(n) # from_xml_element() new_f = openmc.Filter.from_xml_element(elem) @@ -96,14 +96,14 @@ def test_legendre(): def test_spatial_legendre(): n = 5 - axis = 'x' - f = openmc.SpatialLegendreFilter(n, axis, -10., 10.) + axis = "x" + f = openmc.SpatialLegendreFilter(n, axis, -10.0, 10.0) assert f.order == n assert f.axis == axis - assert f.minimum == -10. - assert f.maximum == 10. - assert f.bins[0] == 'P0' - assert f.bins[-1] == 'P5' + assert f.minimum == -10.0 + assert f.maximum == 10.0 + assert f.bins[0] == "P0" + assert f.bins[-1] == "P5" assert len(f.bins) == n + 1 # Make sure __repr__ works @@ -111,10 +111,10 @@ def test_spatial_legendre(): # to_xml_element() elem = f.to_xml_element() - assert elem.tag == 'filter' - assert elem.attrib['type'] == 'spatiallegendre' - assert elem.find('order').text == str(n) - assert elem.find('axis').text == str(axis) + assert elem.tag == "filter" + assert elem.attrib["type"] == "spatiallegendre" + assert elem.find("order").text == str(n) + assert elem.find("axis").text == str(axis) # from_xml_element() new_f = openmc.Filter.from_xml_element(elem) @@ -126,21 +126,21 @@ def test_spatial_legendre(): def test_spherical_harmonics(): n = 3 f = openmc.SphericalHarmonicsFilter(n) - f.cosine = 'particle' + f.cosine = "particle" assert f.order == n - assert f.bins[0] == 'Y0,0' - assert f.bins[-1] == 'Y{0},{0}'.format(n) - assert len(f.bins) == (n + 1)**2 + assert f.bins[0] == "Y0,0" + assert f.bins[-1] == "Y{0},{0}".format(n) + assert len(f.bins) == (n + 1) ** 2 # Make sure __repr__ works repr(f) # to_xml_element() elem = f.to_xml_element() - assert elem.tag == 'filter' - assert elem.attrib['type'] == 'sphericalharmonics' - assert elem.attrib['cosine'] == f.cosine - assert elem.find('order').text == str(n) + assert elem.tag == "filter" + assert elem.attrib["type"] == "sphericalharmonics" + assert elem.attrib["cosine"] == f.cosine + assert elem.find("order").text == str(n) # from_xml_element() new_f = openmc.Filter.from_xml_element(elem) @@ -151,80 +151,86 @@ def test_spherical_harmonics(): def test_zernike(): n = 4 - f = openmc.ZernikeFilter(n, 0., 0., 1.) + f = openmc.ZernikeFilter(n, 0.0, 0.0, 1.0) assert f.order == n - assert f.bins[0] == 'Z0,0' - assert f.bins[-1] == 'Z{0},{0}'.format(n) - assert len(f.bins) == (n + 1)*(n + 2)//2 + assert f.bins[0] == "Z0,0" + assert f.bins[-1] == "Z{0},{0}".format(n) + assert len(f.bins) == (n + 1) * (n + 2) // 2 # Make sure __repr__ works repr(f) # to_xml_element() elem = f.to_xml_element() - assert elem.tag == 'filter' - assert elem.attrib['type'] == 'zernike' - assert elem.find('order').text == str(n) + assert elem.tag == "filter" + assert elem.attrib["type"] == "zernike" + assert elem.find("order").text == str(n) # from_xml_element() new_f = openmc.Filter.from_xml_element(elem) - for attr in ('id', 'order', 'x', 'y', 'r'): + for attr in ("id", "order", "x", "y", "r"): assert getattr(new_f, attr) == getattr(f, attr) def test_zernike_radial(): n = 4 - f = openmc.ZernikeRadialFilter(n, 0., 0., 1.) + f = openmc.ZernikeRadialFilter(n, 0.0, 0.0, 1.0) assert f.order == n - assert f.bins[0] == 'Z0,0' - assert f.bins[-1] == 'Z{},0'.format(n) - assert len(f.bins) == n//2 + 1 + assert f.bins[0] == "Z0,0" + assert f.bins[-1] == "Z{},0".format(n) + assert len(f.bins) == n // 2 + 1 # Make sure __repr__ works repr(f) # to_xml_element() elem = f.to_xml_element() - assert elem.tag == 'filter' - assert elem.attrib['type'] == 'zernikeradial' - assert elem.find('order').text == str(n) + assert elem.tag == "filter" + assert elem.attrib["type"] == "zernikeradial" + assert elem.find("order").text == str(n) # from_xml_element() new_f = openmc.Filter.from_xml_element(elem) - for attr in ('id', 'order', 'x', 'y', 'r'): + for attr in ("id", "order", "x", "y", "r"): assert getattr(new_f, attr) == getattr(f, attr) def test_first_moment(run_in_tmpdir, box_model): plain_tally = openmc.Tally() - plain_tally.scores = ['flux', 'scatter'] + plain_tally.scores = ["flux", "scatter"] # Create tallies with expansion filters leg_tally = openmc.Tally() leg_tally.filters = [openmc.LegendreFilter(3)] - leg_tally.scores = ['scatter'] + leg_tally.scores = ["scatter"] leg_sptl_tally = openmc.Tally() - leg_sptl_tally.filters = [openmc.SpatialLegendreFilter(3, 'x', -5., 5.)] - leg_sptl_tally.scores = ['scatter'] + leg_sptl_tally.filters = [openmc.SpatialLegendreFilter(3, "x", -5.0, 5.0)] + leg_sptl_tally.scores = ["scatter"] sph_scat_filter = openmc.SphericalHarmonicsFilter(5) - sph_scat_filter.cosine = 'scatter' + sph_scat_filter.cosine = "scatter" sph_scat_tally = openmc.Tally() sph_scat_tally.filters = [sph_scat_filter] - sph_scat_tally.scores = ['scatter'] + sph_scat_tally.scores = ["scatter"] sph_flux_filter = openmc.SphericalHarmonicsFilter(5) - sph_flux_filter.cosine = 'particle' + sph_flux_filter.cosine = "particle" sph_flux_tally = openmc.Tally() sph_flux_tally.filters = [sph_flux_filter] - sph_flux_tally.scores = ['flux'] + sph_flux_tally.scores = ["flux"] zernike_tally = openmc.Tally() - zernike_tally.filters = [openmc.ZernikeFilter(3, r=10.)] - zernike_tally.scores = ['scatter'] + zernike_tally.filters = [openmc.ZernikeFilter(3, r=10.0)] + zernike_tally.scores = ["scatter"] # Add tallies to model and ensure they all use the same estimator - box_model.tallies = [plain_tally, leg_tally, leg_sptl_tally, - sph_scat_tally, sph_flux_tally, zernike_tally] + box_model.tallies = [ + plain_tally, + leg_tally, + leg_sptl_tally, + sph_scat_tally, + sph_flux_tally, + zernike_tally, + ] for t in box_model.tallies: - t.estimator = 'analog' + t.estimator = "analog" sp_name = box_model.run() @@ -243,7 +249,7 @@ def test_first_moment(run_in_tmpdir, box_model): def test_energy(): - f = openmc.EnergyFilter.from_group_structure('CCFE-709') + f = openmc.EnergyFilter.from_group_structure("CCFE-709") assert f.bins.shape == (709, 2) assert len(f.values) == 710 @@ -254,18 +260,16 @@ def test_energyfilter_error_handling(): def test_lethargy_bin_width(): - f = openmc.EnergyFilter.from_group_structure('VITAMIN-J-175') + f = openmc.EnergyFilter.from_group_structure("VITAMIN-J-175") assert len(f.lethargy_bin_width) == 175 - energy_bins = openmc.mgxs.GROUP_STRUCTURES['VITAMIN-J-175'] - assert f.lethargy_bin_width[0] == np.log10(energy_bins[1]/energy_bins[0]) - assert f.lethargy_bin_width[-1] == np.log10(energy_bins[-1]/energy_bins[-2]) + energy_bins = openmc.mgxs.GROUP_STRUCTURES["VITAMIN-J-175"] + assert f.lethargy_bin_width[0] == np.log10(energy_bins[1] / energy_bins[0]) + assert f.lethargy_bin_width[-1] == np.log10(energy_bins[-1] / energy_bins[-2]) def test_energyfunc(): f = openmc.EnergyFunctionFilter( - [0.0, 10.0, 2.0e3, 1.0e6, 20.0e6], - [1.0, 0.9, 0.8, 0.7, 0.6], - 'histogram' + [0.0, 10.0, 2.0e3, 1.0e6, 20.0e6], [1.0, 0.9, 0.8, 0.7, 0.6], "histogram" ) # Make sure XML roundtrip works @@ -290,7 +294,9 @@ def test_tabular_from_energyfilter(): assert tab.integral() == approx(1.0) # 'histogram' is the default - assert tab.interpolation == 'histogram' + assert tab.interpolation == "histogram" - tab = efilter.get_tabular(values=np.array([10, 10, 5]), interpolation='linear-linear') - assert tab.interpolation == 'linear-linear' + tab = efilter.get_tabular( + values=np.array([10, 10, 5]), interpolation="linear-linear" + ) + assert tab.interpolation == "linear-linear" diff --git a/tests/unit_tests/test_geometry.py b/tests/unit_tests/test_geometry.py index 6cc577c820c..dc085fc5da8 100644 --- a/tests/unit_tests/test_geometry.py +++ b/tests/unit_tests/test_geometry.py @@ -11,15 +11,15 @@ def test_volume(run_in_tmpdir, uo2): # Create model with nested spheres model = openmc.model.Model() model.materials.append(uo2) - inner = openmc.Sphere(r=1.) - outer = openmc.Sphere(r=2., boundary_type='vacuum') + inner = openmc.Sphere(r=1.0) + outer = openmc.Sphere(r=2.0, boundary_type="vacuum") c1 = openmc.Cell(fill=uo2, region=-inner) c2 = openmc.Cell(region=+inner & -outer) u = openmc.Universe(cells=[c1, c2]) model.geometry.root_universe = u model.settings.particles = 100 model.settings.batches = 10 - model.settings.run_mode = 'fixed source' + model.settings.run_mode = "fixed source" model.settings.source = openmc.IndependentSource(space=openmc.stats.Point()) ll, ur = model.geometry.bounding_box @@ -30,35 +30,36 @@ def test_volume(run_in_tmpdir, uo2): for domain in (c1, uo2, u): # Run stochastic volume calculation volume_calc = openmc.VolumeCalculation( - domains=[domain], samples=1000, lower_left=ll, upper_right=ur) + domains=[domain], samples=1000, lower_left=ll, upper_right=ur + ) model.settings.volume_calculations = [volume_calc] model.export_to_xml() openmc.calculate_volumes() # Load results and add volume information - volume_calc.load_results('volume_1.h5') + volume_calc.load_results("volume_1.h5") model.geometry.add_volume_information(volume_calc) # get_nuclide_densities relies on volume information nucs = set(domain.get_nuclide_densities()) - assert not nucs ^ {'U235', 'O16'} + assert not nucs ^ {"U235", "O16"} def test_export_xml(run_in_tmpdir, uo2): - s1 = openmc.Sphere(r=1.) - s2 = openmc.Sphere(r=2., boundary_type='reflective') + s1 = openmc.Sphere(r=1.0) + s2 = openmc.Sphere(r=2.0, boundary_type="reflective") c1 = openmc.Cell(fill=uo2, region=-s1) c2 = openmc.Cell(fill=uo2, region=+s1 & -s2) geom = openmc.Geometry([c1, c2]) geom.export_to_xml() - doc = ET.parse('geometry.xml') + doc = ET.parse("geometry.xml") root = doc.getroot() - assert root.tag == 'geometry' - cells = root.findall('cell') - assert [int(c.get('id')) for c in cells] == [c1.id, c2.id] - surfs = root.findall('surface') - assert [int(s.get('id')) for s in surfs] == [s1.id, s2.id] + assert root.tag == "geometry" + cells = root.findall("cell") + assert [int(c.get("id")) for c in cells] == [c1.id, c2.id] + surfs = root.findall("surface") + assert [int(s.get("id")) for s in surfs] == [s1.id, s2.id] def test_find(uo2): @@ -72,11 +73,11 @@ def test_find(uo2): c4 = openmc.Cell(region=+cyl) geom = openmc.Geometry((c3, c4)) - seq = geom.find((0.5, 0., 0.)) + seq = geom.find((0.5, 0.0, 0.0)) assert seq[-1] == c1 - seq = geom.find((-0.5, 0., 0.)) + seq = geom.find((-0.5, 0.0, 0.0)) assert seq[-1] == c2 - seq = geom.find((-1.5, 0., 0.)) + seq = geom.find((-1.5, 0.0, 0.0)) assert seq[-1] == c4 @@ -144,7 +145,7 @@ def test_get_all_lattices(cell_with_lattice): def test_get_all_surfaces(uo2): - planes = [openmc.ZPlane(z0=z) for z in np.linspace(-100., 100.)] + planes = [openmc.ZPlane(z0=z) for z in np.linspace(-100.0, 100.0)] slabs = [] for region in openmc.model.subdivide(planes): slabs.append(openmc.Cell(fill=uo2, region=region)) @@ -155,67 +156,67 @@ def test_get_all_surfaces(uo2): def test_get_by_name(): - m1 = openmc.Material(name='zircaloy') - m1.add_element('Zr', 1.0) - m2 = openmc.Material(name='Zirconium') - m2.add_element('Zr', 1.0) - - s1 = openmc.Sphere(name='surface1') - c1 = openmc.Cell(fill=m1, region=-s1, name='cell1') - u1 = openmc.Universe(name='Zircaloy universe', cells=[c1]) - - s2 = openmc.ZCylinder(name='surface2') - c2 = openmc.Cell(fill=u1, region=-s2, name='cell2') - c3 = openmc.Cell(fill=m2, region=+s2, name='Cell3') - root = openmc.Universe(name='root Universe', cells=[c2, c3]) + m1 = openmc.Material(name="zircaloy") + m1.add_element("Zr", 1.0) + m2 = openmc.Material(name="Zirconium") + m2.add_element("Zr", 1.0) + + s1 = openmc.Sphere(name="surface1") + c1 = openmc.Cell(fill=m1, region=-s1, name="cell1") + u1 = openmc.Universe(name="Zircaloy universe", cells=[c1]) + + s2 = openmc.ZCylinder(name="surface2") + c2 = openmc.Cell(fill=u1, region=-s2, name="cell2") + c3 = openmc.Cell(fill=m2, region=+s2, name="Cell3") + root = openmc.Universe(name="root Universe", cells=[c2, c3]) geom = openmc.Geometry(root) - mats = set(geom.get_materials_by_name('zirc')) + mats = set(geom.get_materials_by_name("zirc")) assert not mats ^ {m1, m2} - mats = set(geom.get_materials_by_name('zirc', True)) + mats = set(geom.get_materials_by_name("zirc", True)) assert not mats ^ {m1} - mats = set(geom.get_materials_by_name('zirconium', False, True)) + mats = set(geom.get_materials_by_name("zirconium", False, True)) assert not mats ^ {m2} - mats = geom.get_materials_by_name('zirconium', True, True) + mats = geom.get_materials_by_name("zirconium", True, True) assert not mats - surfaces = set(geom.get_surfaces_by_name('surface')) + surfaces = set(geom.get_surfaces_by_name("surface")) assert not surfaces ^ {s1, s2} - surfaces = set(geom.get_surfaces_by_name('Surface2', False, True)) + surfaces = set(geom.get_surfaces_by_name("Surface2", False, True)) assert not surfaces ^ {s2} - surfaces = geom.get_surfaces_by_name('Surface2', True, True) + surfaces = geom.get_surfaces_by_name("Surface2", True, True) assert not surfaces - cells = set(geom.get_cells_by_name('cell')) + cells = set(geom.get_cells_by_name("cell")) assert not cells ^ {c1, c2, c3} - cells = set(geom.get_cells_by_name('cell', True)) + cells = set(geom.get_cells_by_name("cell", True)) assert not cells ^ {c1, c2} - cells = set(geom.get_cells_by_name('cell3', False, True)) + cells = set(geom.get_cells_by_name("cell3", False, True)) assert not cells ^ {c3} - cells = geom.get_cells_by_name('cell3', True, True) + cells = geom.get_cells_by_name("cell3", True, True) assert not cells - cells = set(geom.get_cells_by_fill_name('Zircaloy')) + cells = set(geom.get_cells_by_fill_name("Zircaloy")) assert not cells ^ {c1, c2} - cells = set(geom.get_cells_by_fill_name('Zircaloy', True)) + cells = set(geom.get_cells_by_fill_name("Zircaloy", True)) assert not cells ^ {c2} - cells = set(geom.get_cells_by_fill_name('Zircaloy', False, True)) + cells = set(geom.get_cells_by_fill_name("Zircaloy", False, True)) assert not cells ^ {c1} - cells = geom.get_cells_by_fill_name('Zircaloy', True, True) + cells = geom.get_cells_by_fill_name("Zircaloy", True, True) assert not cells - univs = set(geom.get_universes_by_name('universe')) + univs = set(geom.get_universes_by_name("universe")) assert not univs ^ {u1, root} - univs = set(geom.get_universes_by_name('universe', True)) + univs = set(geom.get_universes_by_name("universe", True)) assert not univs ^ {u1} - univs = set(geom.get_universes_by_name('universe', True, True)) + univs = set(geom.get_universes_by_name("universe", True, True)) assert not univs def test_hex_prism(): - hex_prism = openmc.model.HexagonalPrism(edge_length=5.0, - origin=(0.0, 0.0), - orientation='y') + hex_prism = openmc.model.HexagonalPrism( + edge_length=5.0, origin=(0.0, 0.0), orientation="y" + ) # clear checks assert (0.0, 0.0, 0.0) in -hex_prism assert (10.0, 10.0, 10.0) not in -hex_prism @@ -223,10 +224,9 @@ def test_hex_prism(): assert (0.0, 5.01, 0.0) not in -hex_prism assert (0.0, 4.99, 0.0) in -hex_prism - rounded_hex_prism = openmc.model.HexagonalPrism(edge_length=5.0, - origin=(0.0, 0.0), - orientation='y', - corner_radius=1.0) + rounded_hex_prism = openmc.model.HexagonalPrism( + edge_length=5.0, origin=(0.0, 0.0), orientation="y", corner_radius=1.0 + ) # clear checks assert (0.0, 0.0, 0.0) in -rounded_hex_prism @@ -241,11 +241,11 @@ def test_get_lattice_by_name(cell_with_lattice): geom = openmc.Geometry([cells[-1]]) f = geom.get_lattices_by_name - assert f('lattice') == [lattice] - assert f('lattice', True) == [] - assert f('Lattice', True) == [lattice] - assert f('my lattice', False, True) == [lattice] - assert f('my lattice', True, True) == [] + assert f("lattice") == [lattice] + assert f("lattice", True) == [] + assert f("Lattice", True) == [lattice] + assert f("my lattice", False, True) == [lattice] + assert f("my lattice", True, True) == [] def test_clone(): @@ -283,10 +283,10 @@ def test_from_xml(run_in_tmpdir, mixed_lattice_model): # Export model mixed_lattice_model.export_to_xml() - mats_from_xml = openmc.Materials.from_xml('materials.xml') + mats_from_xml = openmc.Materials.from_xml("materials.xml") # checking string a Path are both acceptable - for path in ['geometry.xml', Path('geometry.xml')]: - for materials in [mats_from_xml, 'materials.xml']: + for path in ["geometry.xml", Path("geometry.xml")]: + for materials in [mats_from_xml, "materials.xml"]: # Import geometry from file geom = openmc.Geometry.from_xml(path=path, materials=materials) assert isinstance(geom, openmc.Geometry) @@ -295,7 +295,7 @@ def test_from_xml(run_in_tmpdir, mixed_lattice_model): assert ur == pytest.approx((6.0, 6.0, np.inf)) with pytest.raises(TypeError) as excinfo: - geom = openmc.Geometry.from_xml(path='geometry.xml', materials=None) + geom = openmc.Geometry.from_xml(path="geometry.xml", materials=None) assert 'Unable to set "materials" to "None"' in str(excinfo.value) # checking that the default args also work @@ -310,7 +310,7 @@ def test_rotation_matrix(): """Test ability to set a rotation matrix directly""" y = openmc.YPlane() cyl1 = openmc.ZCylinder(r=1.0) - cyl2 = openmc.ZCylinder(r=2.0, boundary_type='vacuum') + cyl2 = openmc.ZCylinder(r=2.0, boundary_type="vacuum") # Create a universe and then reflect in the y-direction c1 = openmc.Cell(region=-cyl1 & +y) @@ -326,22 +326,23 @@ def test_rotation_matrix(): assert geom.find((0.0, -0.5, 0.0))[-1] == c1 assert geom.find((0.0, -1.5, 0.0))[-1] == c2 + def test_remove_redundant_surfaces(): """Test ability to remove redundant surfaces""" m1 = openmc.Material() - m1.add_nuclide('U235', 1.0, 'wo') - m1.add_nuclide('O16', 2.0, 'wo') - m1.set_density('g/cm3', 10.0) + m1.add_nuclide("U235", 1.0, "wo") + m1.add_nuclide("O16", 2.0, "wo") + m1.set_density("g/cm3", 10.0) m2 = openmc.Material() - m2.add_element('Zr', 1.0) - m2.set_density('g/cm3', 2.0) + m2.add_element("Zr", 1.0) + m2.set_density("g/cm3", 2.0) m3 = openmc.Material() - m3.add_nuclide('H1', 2.0) - m3.add_nuclide('O16', 1.0) - m3.set_density('g/cm3', 1.0) + m3.add_nuclide("H1", 2.0) + m3.add_nuclide("O16", 1.0) + m3.set_density("g/cm3", 1.0) def get_cyl_cell(r1, r2, z1, z2, fill): """Create a finite height cylindrical cell with a fill""" @@ -356,8 +357,8 @@ def get_cyl_cell(r1, r2, z1, z2, fill): region = +cyl1 & -cyl2 & +zplane1 & -zplane2 return openmc.Cell(region=region, fill=fill) - r1, r2, r3 = 1., 2., 3. - z1, z2 = -2., 2. + r1, r2, r3 = 1.0, 2.0, 3.0 + z1, z2 = -2.0, 2.0 fuel = get_cyl_cell(0, r1, z1, z2, m1) clad = get_cyl_cell(r1, r2, z1, z2, m2) water = get_cyl_cell(r2, r3, z1, z2, m3) @@ -369,8 +370,7 @@ def get_cyl_cell(r1, r2, z1, z2, fill): assert geom.surface_precision == 11 geom.surface_precision = 10 assert geom.surface_precision == 10 - model = openmc.model.Model(geometry=geom, - materials=openmc.Materials([m1, m2, m3])) + model = openmc.model.Model(geometry=geom, materials=openmc.Materials([m1, m2, m3])) # There should be 6 redundant surfaces in this geometry n_redundant_surfs = len(geom.remove_redundant_surfaces().keys()) @@ -379,17 +379,18 @@ def get_cyl_cell(r1, r2, z1, z2, fill): n_redundant_surfs = len(geom.remove_redundant_surfaces().keys()) assert n_redundant_surfs == 0 + def test_get_all_nuclides(): m1 = openmc.Material() - m1.add_nuclide('Fe56', 1) - m1.add_nuclide('Be9', 1) + m1.add_nuclide("Fe56", 1) + m1.add_nuclide("Be9", 1) m2 = openmc.Material() - m2.add_nuclide('Be9', 1) + m2.add_nuclide("Be9", 1) s = openmc.Sphere() c1 = openmc.Cell(fill=m1, region=-s) c2 = openmc.Cell(fill=m2, region=+s) geom = openmc.Geometry([c1, c2]) - assert geom.get_all_nuclides() == ['Be9', 'Fe56'] + assert geom.get_all_nuclides() == ["Be9", "Fe56"] def test_redundant_surfaces(): diff --git a/tests/unit_tests/test_heating_by_nuclide.py b/tests/unit_tests/test_heating_by_nuclide.py index b34d7dd9431..b2231a69aca 100644 --- a/tests/unit_tests/test_heating_by_nuclide.py +++ b/tests/unit_tests/test_heating_by_nuclide.py @@ -9,10 +9,10 @@ def model(): # Create simple sphere model model = openmc.Model() mat = openmc.Material() - mat.add_nuclide('U235', 1.0) - mat.add_nuclide('H1', 1.0) - mat.set_density('g/cm3', 5.0) - sph = openmc.Sphere(r=10.0, boundary_type='vacuum') + mat.add_nuclide("U235", 1.0) + mat.add_nuclide("H1", 1.0) + mat.set_density("g/cm3", 5.0) + sph = openmc.Sphere(r=10.0, boundary_type="vacuum") cell = openmc.Cell(fill=mat, region=-sph) model.geometry = openmc.Geometry([cell]) model.settings.particles = 1000 @@ -21,15 +21,15 @@ def model(): model.settings.photon_transport = True # Add two tallies, one with heating by nuclide and one with total heating - particle_filter = openmc.ParticleFilter(['neutron', 'photon']) + particle_filter = openmc.ParticleFilter(["neutron", "photon"]) heating_by_nuclide = openmc.Tally() heating_by_nuclide.filters = [particle_filter] - heating_by_nuclide.nuclides = ['U235', 'H1'] - heating_by_nuclide.scores = ['heating'] + heating_by_nuclide.nuclides = ["U235", "H1"] + heating_by_nuclide.scores = ["heating"] heating_total = openmc.Tally() heating_total.filters = [particle_filter] - heating_total.scores = ['heating'] + heating_total.scores = ["heating"] model.tallies.extend([heating_by_nuclide, heating_total]) return model @@ -37,9 +37,9 @@ def model(): def test_heating_by_nuclide(model, run_in_tmpdir): # If running in MPI mode, setup proper keyword arguments for run() - kwargs = {'openmc_exec': config['exe']} - if config['mpi']: - kwargs['mpi_args'] = [config['mpiexec'], '-n', config['mpi_np']] + kwargs = {"openmc_exec": config["exe"]} + if config["mpi"]: + kwargs["mpi_args"] = [config["mpiexec"], "-n", config["mpi_np"]] sp_path = model.run(**kwargs) # Get tallies from resulting statepoint @@ -49,9 +49,11 @@ def test_heating_by_nuclide(model, run_in_tmpdir): for particle in heating_by_nuclide.filters[0].bins: # Get slice of each tally corresponding to a single particle - kwargs = {'filters': [openmc.ParticleFilter], 'filter_bins': [(particle,)]} + kwargs = {"filters": [openmc.ParticleFilter], "filter_bins": [(particle,)]} particle_slice_by_nuclide = heating_by_nuclide.get_values(**kwargs) particle_slice_total = heating_total.get_values(**kwargs) # Summing over nuclides should equal total - assert particle_slice_by_nuclide.sum() == pytest.approx(particle_slice_total.sum()) + assert particle_slice_by_nuclide.sum() == pytest.approx( + particle_slice_total.sum() + ) diff --git a/tests/unit_tests/test_lattice.py b/tests/unit_tests/test_lattice.py index d72d9c5c3e6..5ced198eca6 100644 --- a/tests/unit_tests/test_lattice.py +++ b/tests/unit_tests/test_lattice.py @@ -5,7 +5,7 @@ import pytest -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def pincell1(uo2, water): cyl = openmc.ZCylinder(r=0.35) fuel = openmc.Cell(fill=uo2, region=-cyl) @@ -17,7 +17,7 @@ def pincell1(uo2, water): return univ -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def pincell2(uo2, water): cyl = openmc.ZCylinder(r=0.4) fuel = openmc.Cell(fill=uo2, region=-cyl) @@ -29,15 +29,15 @@ def pincell2(uo2, water): return univ -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def zr(): zr = openmc.Material() - zr.add_element('Zr', 1.0) - zr.set_density('g/cm3', 1.0) + zr.add_element("Zr", 1.0) + zr.set_density("g/cm3", 1.0) return zr -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def rlat2(pincell1, pincell2, uo2, water, zr): """2D Rectangular lattice for testing.""" all_zr = openmc.Cell(fill=zr) @@ -45,14 +45,10 @@ def rlat2(pincell1, pincell2, uo2, water, zr): n = 3 u1, u2 = pincell1, pincell2 lattice = openmc.RectLattice() - lattice.lower_left = (-pitch*n/2, -pitch*n/2) + lattice.lower_left = (-pitch * n / 2, -pitch * n / 2) lattice.pitch = (pitch, pitch) lattice.outer = openmc.Universe(cells=[all_zr]) - lattice.universes = [ - [u1, u2, u1], - [u2, u1, u2], - [u2, u1, u1] - ] + lattice.universes = [[u1, u2, u1], [u2, u1, u2], [u2, u1, u1]] # Add extra attributes for comparison purpose lattice.cells = [u1.fuel, u1.moderator, u2.fuel, u2.moderator, all_zr] @@ -61,14 +57,14 @@ def rlat2(pincell1, pincell2, uo2, water, zr): return lattice -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def rlat3(pincell1, pincell2, uo2, water, zr): """3D Rectangular lattice for testing.""" # Create another universe for top layer hydrogen = openmc.Material() - hydrogen.add_element('H', 1.0) - hydrogen.set_density('g/cm3', 0.09) + hydrogen.add_element("H", 1.0) + hydrogen.set_density("g/cm3", 0.09) h_cell = openmc.Cell(fill=hydrogen) u3 = openmc.Universe(cells=[h_cell]) @@ -77,40 +73,35 @@ def rlat3(pincell1, pincell2, uo2, water, zr): n = 3 u1, u2 = pincell1, pincell2 lattice = openmc.RectLattice() - lattice.lower_left = (-pitch*n/2, -pitch*n/2, -10.0) + lattice.lower_left = (-pitch * n / 2, -pitch * n / 2, -10.0) lattice.pitch = (pitch, pitch, 10.0) lattice.outer = openmc.Universe(cells=[all_zr]) lattice.universes = [ - [[u1, u2, u1], - [u2, u1, u2], - [u2, u1, u1]], - [[u3, u1, u2], - [u1, u3, u2], - [u2, u1, u1]] + [[u1, u2, u1], [u2, u1, u2], [u2, u1, u1]], + [[u3, u1, u2], [u1, u3, u2], [u2, u1, u1]], ] # Add extra attributes for comparison purpose - lattice.cells = [u1.fuel, u1.moderator, u2.fuel, u2.moderator, - h_cell, all_zr] + lattice.cells = [u1.fuel, u1.moderator, u2.fuel, u2.moderator, h_cell, all_zr] lattice.mats = [uo2, water, zr, hydrogen] lattice.univs = [u1, u2, u3, lattice.outer] return lattice -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def hlat2(pincell1, pincell2, uo2, water, zr): """2D Hexagonal lattice for testing.""" all_zr = openmc.Cell(fill=zr) pitch = 1.2 u1, u2 = pincell1, pincell2 lattice = openmc.HexLattice() - lattice.center = (0., 0.) + lattice.center = (0.0, 0.0) lattice.pitch = (pitch,) lattice.outer = openmc.Universe(cells=[all_zr]) lattice.universes = [ [u2, u1, u1, u1, u1, u1, u1, u1, u1, u1, u1, u1], [u2, u1, u1, u1, u1, u1], - [u2] + [u2], ] # Add extra attributes for comparison purpose @@ -120,14 +111,14 @@ def hlat2(pincell1, pincell2, uo2, water, zr): return lattice -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def hlat3(pincell1, pincell2, uo2, water, zr): """3D Hexagonal lattice for testing.""" # Create another universe for top layer hydrogen = openmc.Material() - hydrogen.add_element('H', 1.0) - hydrogen.set_density('g/cm3', 0.09) + hydrogen.add_element("H", 1.0) + hydrogen.set_density("g/cm3", 0.09) h_cell = openmc.Cell(fill=hydrogen) u3 = openmc.Universe(cells=[h_cell]) @@ -135,21 +126,24 @@ def hlat3(pincell1, pincell2, uo2, water, zr): pitch = 1.2 u1, u2 = pincell1, pincell2 lattice = openmc.HexLattice() - lattice.center = (0., 0., 0.) + lattice.center = (0.0, 0.0, 0.0) lattice.pitch = (pitch, 10.0) lattice.outer = openmc.Universe(cells=[all_zr]) lattice.universes = [ - [[u2, u1, u1, u1, u1, u1, u1, u1, u1, u1, u1, u1], - [u2, u1, u1, u1, u1, u1], - [u2]], - [[u1, u1, u1, u1, u1, u1, u3, u1, u1, u1, u1, u1], - [u1, u1, u1, u3, u1, u1], - [u3]] + [ + [u2, u1, u1, u1, u1, u1, u1, u1, u1, u1, u1, u1], + [u2, u1, u1, u1, u1, u1], + [u2], + ], + [ + [u1, u1, u1, u1, u1, u1, u3, u1, u1, u1, u1, u1], + [u1, u1, u1, u3, u1, u1], + [u3], + ], ] # Add extra attributes for comparison purpose - lattice.cells = [u1.fuel, u1.moderator, u2.fuel, u2.moderator, - h_cell, all_zr] + lattice.cells = [u1.fuel, u1.moderator, u2.fuel, u2.moderator, h_cell, all_zr] lattice.mats = [uo2, water, zr, hydrogen] lattice.univs = [u1, u2, u3, lattice.outer] return lattice @@ -158,12 +152,29 @@ def hlat3(pincell1, pincell2, uo2, water, zr): def test_get_nuclides(rlat2, rlat3, hlat2, hlat3): for lat in (rlat2, hlat2): nucs = lat.get_nuclides() - assert sorted(nucs) == ['H1', 'O16', 'U235', - 'Zr90', 'Zr91', 'Zr92', 'Zr94', 'Zr96'] + assert sorted(nucs) == [ + "H1", + "O16", + "U235", + "Zr90", + "Zr91", + "Zr92", + "Zr94", + "Zr96", + ] for lat in (rlat3, hlat3): nucs = lat.get_nuclides() - assert sorted(nucs) == ['H1', 'H2', 'O16', 'U235', - 'Zr90', 'Zr91', 'Zr92', 'Zr94', 'Zr96'] + assert sorted(nucs) == [ + "H1", + "H2", + "O16", + "U235", + "Zr90", + "Zr91", + "Zr92", + "Zr94", + "Zr96", + ] def test_get_all_cells(rlat2, rlat3, hlat2, hlat3): @@ -202,12 +213,12 @@ def test_get_universe(rlat2, rlat3, hlat2, hlat3): assert hlat2.get_universe((1, 0)) == u1 assert hlat2.get_universe((-2, 2)) == u1 - hlat2.orientation = 'x' + hlat2.orientation = "x" assert hlat2.get_universe((2, 0)) == u2 assert hlat2.get_universe((1, 0)) == u2 assert hlat2.get_universe((1, 1)) == u1 assert hlat2.get_universe((-1, 1)) == u1 - hlat2.orientation = 'y' + hlat2.orientation = "y" u1, u2, u3, outer = hlat3.univs assert hlat3.get_universe((0, 0, 0)) == u2 @@ -220,45 +231,45 @@ def test_get_universe(rlat2, rlat3, hlat2, hlat3): def test_find(rlat2, rlat3, hlat2, hlat3): pitch = rlat2.pitch[0] - seq = rlat2.find((0., 0., 0.)) + seq = rlat2.find((0.0, 0.0, 0.0)) assert seq[-1] == rlat2.cells[0] - seq = rlat2.find((pitch, 0., 0.)) + seq = rlat2.find((pitch, 0.0, 0.0)) assert seq[-1] == rlat2.cells[2] - seq = rlat2.find((0., -pitch, 0.)) + seq = rlat2.find((0.0, -pitch, 0.0)) assert seq[-1] == rlat2.cells[0] - seq = rlat2.find((pitch*100, 0., 0.)) + seq = rlat2.find((pitch * 100, 0.0, 0.0)) assert seq[-1] == rlat2.cells[-1] seq = rlat3.find((-pitch, pitch, 5.0)) assert seq[-1] == rlat3.cells[-2] pitch = hlat2.pitch[0] - seq = hlat2.find((0., 0., 0.)) + seq = hlat2.find((0.0, 0.0, 0.0)) assert seq[-1] == hlat2.cells[2] - seq = hlat2.find((0.5, 0., 0.)) + seq = hlat2.find((0.5, 0.0, 0.0)) assert seq[-1] == hlat2.cells[3] - seq = hlat2.find((sqrt(3)*pitch, 0., 0.)) + seq = hlat2.find((sqrt(3) * pitch, 0.0, 0.0)) assert seq[-1] == hlat2.cells[0] - seq = hlat2.find((0., pitch, 0.)) + seq = hlat2.find((0.0, pitch, 0.0)) assert seq[-1] == hlat2.cells[2] # bottom of 3D lattice - seq = hlat3.find((0., 0., -5.)) + seq = hlat3.find((0.0, 0.0, -5.0)) assert seq[-1] == hlat3.cells[2] - seq = hlat3.find((0., pitch, -5.)) + seq = hlat3.find((0.0, pitch, -5.0)) assert seq[-1] == hlat3.cells[2] - seq = hlat3.find((0., -pitch, -5.)) + seq = hlat3.find((0.0, -pitch, -5.0)) assert seq[-1] == hlat3.cells[0] - seq = hlat3.find((sqrt(3)*pitch, 0., -5.)) + seq = hlat3.find((sqrt(3) * pitch, 0.0, -5.0)) assert seq[-1] == hlat3.cells[0] # top of 3D lattice - seq = hlat3.find((0., 0., 5.)) + seq = hlat3.find((0.0, 0.0, 5.0)) assert seq[-1] == hlat3.cells[-2] - seq = hlat3.find((0., pitch, 5.)) + seq = hlat3.find((0.0, pitch, 5.0)) assert seq[-1] == hlat3.cells[0] - seq = hlat3.find((0., -pitch, 5.)) + seq = hlat3.find((0.0, -pitch, 5.0)) assert seq[-1] == hlat3.cells[-2] - seq = hlat3.find((sqrt(3)*pitch, 0., 5.)) + seq = hlat3.find((sqrt(3) * pitch, 0.0, 5.0)) assert seq[-1] == hlat3.cells[0] @@ -298,88 +309,106 @@ def test_repr(rlat2, rlat3, hlat2, hlat3): def test_indices_rect(rlat2, rlat3): # (y, x) indices - assert rlat2.indices == [(0, 0), (0, 1), (0, 2), - (1, 0), (1, 1), (1, 2), - (2, 0), (2, 1), (2, 2)] + assert rlat2.indices == [ + (0, 0), + (0, 1), + (0, 2), + (1, 0), + (1, 1), + (1, 2), + (2, 0), + (2, 1), + (2, 2), + ] # (z, y, x) indices assert rlat3.indices == [ - (0, 0, 0), (0, 0, 1), (0, 0, 2), - (0, 1, 0), (0, 1, 1), (0, 1, 2), - (0, 2, 0), (0, 2, 1), (0, 2, 2), - (1, 0, 0), (1, 0, 1), (1, 0, 2), - (1, 1, 0), (1, 1, 1), (1, 1, 2), - (1, 2, 0), (1, 2, 1), (1, 2, 2) + (0, 0, 0), + (0, 0, 1), + (0, 0, 2), + (0, 1, 0), + (0, 1, 1), + (0, 1, 2), + (0, 2, 0), + (0, 2, 1), + (0, 2, 2), + (1, 0, 0), + (1, 0, 1), + (1, 0, 2), + (1, 1, 0), + (1, 1, 1), + (1, 1, 2), + (1, 2, 0), + (1, 2, 1), + (1, 2, 2), ] def test_indices_hex(hlat2, hlat3): # (r, i) indices assert hlat2.indices == ( - [(0, i) for i in range(12)] + - [(1, i) for i in range(6)] + - [(2, 0)] + [(0, i) for i in range(12)] + [(1, i) for i in range(6)] + [(2, 0)] ) # (z, r, i) indices assert hlat3.indices == ( - [(0, 0, i) for i in range(12)] + - [(0, 1, i) for i in range(6)] + - [(0, 2, 0)] + - [(1, 0, i) for i in range(12)] + - [(1, 1, i) for i in range(6)] + - [(1, 2, 0)] + [(0, 0, i) for i in range(12)] + + [(0, 1, i) for i in range(6)] + + [(0, 2, 0)] + + [(1, 0, i) for i in range(12)] + + [(1, 1, i) for i in range(6)] + + [(1, 2, 0)] ) def test_xml_rect(rlat2, rlat3): for lat in (rlat2, rlat3): - geom = ET.Element('geometry') + geom = ET.Element("geometry") lat.create_xml_subelement(geom) - elem = geom.find('lattice') - assert elem.tag == 'lattice' - assert elem.get('id') == str(lat.id) - assert len(elem.find('pitch').text.split()) == lat.ndim - assert len(elem.find('lower_left').text.split()) == lat.ndim - assert len(elem.find('universes').text.split()) == len(lat.indices) + elem = geom.find("lattice") + assert elem.tag == "lattice" + assert elem.get("id") == str(lat.id) + assert len(elem.find("pitch").text.split()) == lat.ndim + assert len(elem.find("lower_left").text.split()) == lat.ndim + assert len(elem.find("universes").text.split()) == len(lat.indices) def test_xml_hex(hlat2, hlat3): for lat in (hlat2, hlat3): - geom = ET.Element('geometry') + geom = ET.Element("geometry") lat.create_xml_subelement(geom) - elem = geom.find('hex_lattice') - assert elem.tag == 'hex_lattice' - assert elem.get('id') == str(lat.id) - assert len(elem.find('center').text.split()) == lat.ndim - assert len(elem.find('pitch').text.split()) == lat.ndim - 1 - assert len(elem.find('universes').text.split()) == len(lat.indices) + elem = geom.find("hex_lattice") + assert elem.tag == "hex_lattice" + assert elem.get("id") == str(lat.id) + assert len(elem.find("center").text.split()) == lat.ndim + assert len(elem.find("pitch").text.split()) == lat.ndim - 1 + assert len(elem.find("universes").text.split()) == len(lat.indices) def test_show_indices(): for i in range(1, 11): - lines = openmc.HexLattice.show_indices(i).split('\n') - assert len(lines) == 4*i - 3 - lines_x = openmc.HexLattice.show_indices(i, 'x').split('\n') - assert len(lines_x) == 4*i - 3 + lines = openmc.HexLattice.show_indices(i).split("\n") + assert len(lines) == 4 * i - 3 + lines_x = openmc.HexLattice.show_indices(i, "x").split("\n") + assert len(lines_x) == 4 * i - 3 def test_unset_universes(): elem = ET.Element("dummy") lattice = openmc.RectLattice() - lattice.lower_left = (-1., -1.) - lattice.pitch = (1., 1.) + lattice.lower_left = (-1.0, -1.0) + lattice.pitch = (1.0, 1.0) with pytest.raises(ValueError): lattice.create_xml_subelement(elem) hex_lattice = openmc.HexLattice() - hex_lattice.center = (0., 0.) - hex_lattice.pitch = (1.,) + hex_lattice.center = (0.0, 0.0) + hex_lattice.pitch = (1.0,) with pytest.raises(ValueError): hex_lattice.create_xml_subelement(elem) -@pytest.mark.parametrize("orientation", ['x', 'y']) +@pytest.mark.parametrize("orientation", ["x", "y"]) def test_hex_lattice_roundtrip(orientation): openmc.reset_auto_ids() diff --git a/tests/unit_tests/test_lattice_discretization.py b/tests/unit_tests/test_lattice_discretization.py index c7779ea8bf5..cefc449e146 100644 --- a/tests/unit_tests/test_lattice_discretization.py +++ b/tests/unit_tests/test_lattice_discretization.py @@ -1,4 +1,3 @@ - from tests.unit_tests.test_lattice import zr, pincell1, pincell2, rlat2 @@ -7,37 +6,38 @@ def test_discretization_degenerate(rlat2): rlat_clone = rlat2.clone() rlat_clone.discretize() - assert rlat_clone.get_universe((0, 0)).id !=\ - rlat_clone.get_universe((1, 0)).id - assert rlat_clone.get_universe((1, 0)).id !=\ - rlat_clone.get_universe((0, 1)).id - assert rlat_clone.get_universe((0, 1)).id !=\ - rlat_clone.get_universe((0, 0)).id - assert rlat_clone.get_universe((0, 2)).id !=\ - rlat_clone.get_universe((2, 0)).id - assert rlat_clone.get_universe((2, 1)).id !=\ - rlat_clone.get_universe((1, 2)).id + assert rlat_clone.get_universe((0, 0)).id != rlat_clone.get_universe((1, 0)).id + assert rlat_clone.get_universe((1, 0)).id != rlat_clone.get_universe((0, 1)).id + assert rlat_clone.get_universe((0, 1)).id != rlat_clone.get_universe((0, 0)).id + assert rlat_clone.get_universe((0, 2)).id != rlat_clone.get_universe((2, 0)).id + assert rlat_clone.get_universe((2, 1)).id != rlat_clone.get_universe((1, 2)).id + def test_discretization_skip_universe(rlat2): rlat_clone = rlat2.clone() - rlat_clone.discretize( - universes_to_ignore=[rlat_clone.get_universe((0, 0))]) + rlat_clone.discretize(universes_to_ignore=[rlat_clone.get_universe((0, 0))]) assert rlat_clone.get_universe((0, 0)) == rlat_clone.get_universe((0, 1)) assert rlat_clone.get_universe((0, 1)) == rlat_clone.get_universe((2, 1)) assert rlat_clone.get_universe((0, 1)) == rlat_clone.get_universe((1, 2)) + def test_discretization_clone_only_some_materials(rlat2): rlat_clone = rlat2.clone() fuel1 = next(iter(rlat_clone.get_universe((0, 0)).cells.values())).fill rlat_clone.discretize(materials_to_clone=[fuel1]) - assert next(reversed(list(rlat_clone.get_universe((0, 0)).cells.values()))).fill\ - == next(reversed(list(rlat_clone.get_universe((1, 0)).cells.values()))).fill - assert next(iter(rlat_clone.get_universe((0, 0)).cells.values())).fill\ - != next(iter(rlat_clone.get_universe((1, 0)).cells.values())).fill + assert ( + next(reversed(list(rlat_clone.get_universe((0, 0)).cells.values()))).fill + == next(reversed(list(rlat_clone.get_universe((1, 0)).cells.values()))).fill + ) + assert ( + next(iter(rlat_clone.get_universe((0, 0)).cells.values())).fill + != next(iter(rlat_clone.get_universe((1, 0)).cells.values())).fill + ) + def test_discretization_lns(rlat2): @@ -53,6 +53,7 @@ def test_discretization_lns(rlat2): assert rlat_clone.get_universe((0, 2)) != rlat_clone.get_universe((2, 0)) assert rlat_clone.get_universe((0, 2)) != rlat_clone.get_universe((2, 1)) + def test_discretization_lns_using_names(rlat2): rlat_clone = rlat2.clone() @@ -61,8 +62,8 @@ def test_discretization_lns_using_names(rlat2): assert rlat_clone.get_universe((0, 2)) == rlat_clone.get_universe((2, 2)) rlat_clone = rlat2.clone() - rlat_clone.get_universe((0, 1)).name="u1" - rlat_clone.get_universe((1, 0)).name="u1" + rlat_clone.get_universe((0, 1)).name = "u1" + rlat_clone.get_universe((1, 0)).name = "u1" rlat_clone.discretize(strategy="lns", key=lambda univ: univ.name) assert rlat_clone.get_universe((0, 2)) == rlat_clone.get_universe((2, 0)) @@ -73,14 +74,15 @@ def test_discretization_lns_using_names(rlat2): assert rlat_clone.get_universe((1, 2)) == rlat_clone.get_universe((0, 1)) assert rlat_clone.get_universe((0, 0)) != rlat_clone.get_universe((0, 1)) + def test_discretization_lns_with_neighbor_list(rlat2): rlat_clone = rlat2.clone() u1 = rlat_clone.get_universe((1, 0)) u2 = rlat_clone.get_universe((0, 0)) - rlat_clone.discretize(strategy="lns", - lattice_neighbors=[u1,u1,u2,u1,u2,u2,u1,u2]) + rlat_clone.discretize( + strategy="lns", lattice_neighbors=[u1, u1, u2, u1, u2, u2, u1, u2] + ) assert rlat_clone.get_universe((0, 1)) == rlat_clone.get_universe((0, 0)) assert rlat_clone.get_universe((1, 1)) != rlat_clone.get_universe((0, 0)) - diff --git a/tests/unit_tests/test_lib.py b/tests/unit_tests/test_lib.py index 64c16c238ea..38fcd8729c3 100644 --- a/tests/unit_tests/test_lib.py +++ b/tests/unit_tests/test_lib.py @@ -11,7 +11,7 @@ from tests import cdtemp -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def pincell_model(): """Set up a model to test with and delete files when done""" openmc.reset_auto_ids() @@ -23,24 +23,23 @@ def pincell_model(): filter2 = openmc.EnergyFilter([0.0, 1.0, 1.0e3, 20.0e6]) mat_tally = openmc.Tally() mat_tally.filters = [filter1, filter2] - mat_tally.nuclides = ['U235', 'U238'] - mat_tally.scores = ['total', 'elastic', '(n,gamma)'] + mat_tally.nuclides = ["U235", "U238"] + mat_tally.scores = ["total", "elastic", "(n,gamma)"] pincell.tallies.append(mat_tally) # Add an expansion tally zernike_tally = openmc.Tally() - filter3 = openmc.ZernikeFilter(5, r=.63) + filter3 = openmc.ZernikeFilter(5, r=0.63) cells = pincell.geometry.root_universe.cells filter4 = openmc.CellFilter(list(cells.values())) zernike_tally.filters = [filter3, filter4] - zernike_tally.scores = ['fission'] + zernike_tally.scores = ["fission"] pincell.tallies.append(zernike_tally) # Add an energy function tally energyfunc_tally = openmc.Tally() - energyfunc_filter = openmc.EnergyFunctionFilter( - [0.0, 20e6], [0.0, 20e6]) - energyfunc_tally.scores = ['fission'] + energyfunc_filter = openmc.EnergyFunctionFilter([0.0, 20e6], [0.0, 20e6]) + energyfunc_tally.scores = ["fission"] energyfunc_tally.filters = [energyfunc_filter] pincell.tallies.append(energyfunc_tally) @@ -50,17 +49,17 @@ def pincell_model(): yield -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def uo2_trigger_model(): """Set up a simple UO2 model with k-eff trigger""" model = openmc.model.Model() - m = openmc.Material(name='UO2') - m.add_nuclide('U235', 1.0) - m.add_nuclide('O16', 2.0) - m.set_density('g/cm3', 10.0) + m = openmc.Material(name="UO2") + m.add_nuclide("U235", 1.0) + m.add_nuclide("O16", 2.0) + m.set_density("g/cm3", 10.0) model.materials.append(m) - cyl = openmc.ZCylinder(r=1.0, boundary_type='vacuum') + cyl = openmc.ZCylinder(r=1.0, boundary_type="vacuum") c = openmc.Cell(fill=m, region=-cyl) model.geometry.root_universe = openmc.Universe(cells=[c]) @@ -69,10 +68,10 @@ def uo2_trigger_model(): model.settings.particles = 100 model.settings.source = openmc.IndependentSource( space=openmc.stats.Box([-0.5, -0.5, -1], [0.5, 0.5, 1]), - constraints={'fissionable': True}, + constraints={"fissionable": True}, ) model.settings.verbosity = 1 - model.settings.keff_trigger = {'type': 'std_dev', 'threshold': 0.001} + model.settings.keff_trigger = {"type": "std_dev", "threshold": 0.001} model.settings.trigger_active = True model.settings.trigger_max_batches = 10 model.settings.trigger_batch_interval = 1 @@ -83,25 +82,25 @@ def uo2_trigger_model(): yield -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def lib_init(pincell_model, mpi_intracomm): openmc.lib.init(intracomm=mpi_intracomm) yield openmc.lib.finalize() -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def lib_simulation_init(lib_init): openmc.lib.simulation_init() yield -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def lib_run(lib_simulation_init): openmc.lib.run() -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def pincell_model_w_univ(): """Set up a model to test with and delete files when done""" openmc.reset_auto_ids() @@ -129,7 +128,7 @@ def test_cell(lib_init): cell = openmc.lib.cells[1] assert isinstance(cell.fill, openmc.lib.Material) cell.fill = openmc.lib.materials[1] - assert str(cell) == '' + assert str(cell) == "" assert cell.name == "Fuel" cell.name = "Not fuel" assert cell.name == "Not fuel" @@ -150,12 +149,12 @@ def test_properties_temperature(lib_init): assert cell.get_temperature() == pytest.approx(200.0) # Export properties and change temperature - openmc.lib.export_properties('properties.h5') + openmc.lib.export_properties("properties.h5") cell.set_temperature(300.0) assert cell.get_temperature() == pytest.approx(300.0) # Import properties and check that temperature is restored - openmc.lib.import_properties('properties.h5') + openmc.lib.import_properties("properties.h5") assert cell.get_temperature() == pytest.approx(200.0) @@ -185,7 +184,7 @@ def test_material_mapping(lib_init): def test_material(lib_init): m = openmc.lib.materials[3] - assert m.nuclides == ['H1', 'O16', 'B10', 'B11'] + assert m.nuclides == ["H1", "O16", "B10", "B11"] old_dens = m.densities test_dens = [1.0e-1, 2.0e-1, 2.5e-1, 1.0e-3] @@ -197,14 +196,14 @@ def test_material(lib_init): assert m.volume == 10.0 with pytest.raises(exc.OpenMCError): - m.set_density(1.0, 'goblins') + m.set_density(1.0, "goblins") rho = 2.25e-2 m.set_density(rho) assert sum(m.densities) == pytest.approx(rho) - m.set_density(0.1, 'g/cm3') - assert m.get_density('g/cm3') == pytest.approx(0.1) + m.set_density(0.1, "g/cm3") + assert m.get_density("g/cm3") == pytest.approx(0.1) assert m.name == "Hot borated water" m.name = "Not hot borated water" assert m.name == "Not hot borated water" @@ -216,26 +215,26 @@ def test_material(lib_init): def test_properties_density(lib_init): m = openmc.lib.materials[1] - orig_density = m.get_density('atom/b-cm') - orig_density_gpcc = m.get_density('g/cm3') + orig_density = m.get_density("atom/b-cm") + orig_density_gpcc = m.get_density("g/cm3") # Export properties and change density - openmc.lib.export_properties('properties.h5') - m.set_density(orig_density_gpcc*2, 'g/cm3') - assert m.get_density() == pytest.approx(orig_density*2) + openmc.lib.export_properties("properties.h5") + m.set_density(orig_density_gpcc * 2, "g/cm3") + assert m.get_density() == pytest.approx(orig_density * 2) # Import properties and check that density was restored - openmc.lib.import_properties('properties.h5') + openmc.lib.import_properties("properties.h5") assert m.get_density() == pytest.approx(orig_density) with pytest.raises(ValueError): - m.get_density('šŸ„') + m.get_density("šŸ„") def test_material_add_nuclide(lib_init): m = openmc.lib.materials[3] - m.add_nuclide('Xe135', 1e-12) - assert m.nuclides[-1] == 'Xe135' + m.add_nuclide("Xe135", 1e-12) + assert m.nuclides[-1] == "Xe135" assert m.densities[-1] == 1e-12 @@ -291,14 +290,14 @@ def test_energy_function_filter(lib_init): assert (efunc.y == [0.0, 2.0]).all() # Default should be lin-lin - assert efunc.interpolation == 'linear-linear' - efunc.interpolation = 'histogram' - assert efunc.interpolation == 'histogram' + assert efunc.interpolation == "linear-linear" + efunc.interpolation = "histogram" + assert efunc.interpolation == "histogram" def test_tally(lib_init): t = openmc.lib.tallies[1] - assert t.type == 'volume' + assert t.type == "volume" assert len(t.filters) == 2 assert isinstance(t.filters[0], openmc.lib.MaterialFilter) assert isinstance(t.filters[1], openmc.lib.EnergyFilter) @@ -313,14 +312,14 @@ def test_tally(lib_init): t.filters = [f] assert t.filters == [f] - assert t.nuclides == ['U235', 'U238'] + assert t.nuclides == ["U235", "U238"] with pytest.raises(exc.DataError): - t.nuclides = ['Zr2'] - t.nuclides = ['U234', 'Zr90'] - assert t.nuclides == ['U234', 'Zr90'] + t.nuclides = ["Zr2"] + t.nuclides = ["U234", "Zr90"] + assert t.nuclides == ["U234", "Zr90"] - assert t.scores == ['total', '(n,elastic)', '(n,gamma)'] - new_scores = ['scatter', 'fission', 'nu-fission', '(n,2n)'] + assert t.scores == ["total", "(n,elastic)", "(n,gamma)"] + new_scores = ["scatter", "fission", "nu-fission", "(n,2n)"] t.scores = new_scores assert t.scores == new_scores @@ -346,9 +345,9 @@ def test_new_tally(lib_init): with pytest.raises(exc.AllocationError): openmc.lib.Material(1) new_tally = openmc.lib.Tally() - new_tally.scores = ['flux'] + new_tally.scores = ["flux"] new_tally_with_id = openmc.lib.Tally(10) - new_tally_with_id.scores = ['flux'] + new_tally_with_id.scores = ["flux"] assert len(openmc.lib.tallies) == 5 @@ -363,7 +362,7 @@ def test_invalid_tally_id(lib_init): # attempt to access a tally that is guaranteed not to have a valid index max_id = max(openmc.lib.tallies.keys()) with pytest.raises(KeyError): - openmc.lib.tallies[max_id+1] + openmc.lib.tallies[max_id + 1] def test_tally_activate(lib_simulation_init): @@ -399,13 +398,15 @@ def test_tally_results(lib_run): t = openmc.lib.tallies[1] assert t.num_realizations == 10 # t was made active in test_tally_active assert np.all(t.mean >= 0) - nonzero = (t.mean > 0.0) + nonzero = t.mean > 0.0 assert np.all(t.std_dev[nonzero] >= 0) - assert np.all(t.ci_width()[nonzero] >= 1.95*t.std_dev[nonzero]) + assert np.all(t.ci_width()[nonzero] >= 1.95 * t.std_dev[nonzero]) t2 = openmc.lib.tallies[2] n = 5 - assert t2.mean.size == (n + 1) * (n + 2) // 2 * 3 # Number of Zernike coeffs * 3 cells + assert ( + t2.mean.size == (n + 1) * (n + 2) // 2 * 3 + ) # Number of Zernike coeffs * 3 cells def test_global_tallies(lib_run): @@ -416,15 +417,15 @@ def test_global_tallies(lib_run): def test_statepoint(lib_run): - openmc.lib.statepoint_write('test_sp.h5') - assert os.path.exists('test_sp.h5') + openmc.lib.statepoint_write("test_sp.h5") + assert os.path.exists("test_sp.h5") def test_source_bank(lib_run): source = openmc.lib.source_bank() - assert np.all(source['E'] > 0.0) - assert np.all(source['wgt'] == 1.0) - assert np.allclose(np.linalg.norm(source['u'], axis=1), 1.0) + assert np.all(source["E"] > 0.0) + assert np.all(source["wgt"] == 1.0) + assert np.allclose(np.linalg.norm(source["u"], axis=1), 1.0) def test_by_batch(lib_run): @@ -484,7 +485,7 @@ def test_set_n_batches(lib_run): assert openmc.lib.num_realizations() == 15 # Ensure statepoint created at new value of n_batches - assert os.path.exists('statepoint.20.h5') + assert os.path.exists("statepoint.20.h5") def test_reset(lib_run): @@ -532,18 +533,18 @@ def test_reproduce_keff(lib_init): def test_find_cell(lib_init): - cell, instance = openmc.lib.find_cell((0., 0., 0.)) + cell, instance = openmc.lib.find_cell((0.0, 0.0, 0.0)) assert cell is openmc.lib.cells[1] - cell, instance = openmc.lib.find_cell((0.4, 0., 0.)) + cell, instance = openmc.lib.find_cell((0.4, 0.0, 0.0)) assert cell is openmc.lib.cells[2] with pytest.raises(exc.GeometryError): - openmc.lib.find_cell((100., 100., 100.)) + openmc.lib.find_cell((100.0, 100.0, 100.0)) def test_find_material(lib_init): - mat = openmc.lib.find_material((0., 0., 0.)) + mat = openmc.lib.find_material((0.0, 0.0, 0.0)) assert mat is openmc.lib.materials[1] - mat = openmc.lib.find_material((0.4, 0., 0.)) + mat = openmc.lib.find_material((0.4, 0.0, 0.0)) assert mat is openmc.lib.materials[2] @@ -555,9 +556,9 @@ def test_regular_mesh(lib_init): mesh2 = openmc.lib.RegularMesh(mesh.id) # Make sure each combination of parameters works - ll = (0., 0., 0.) - ur = (10., 10., 10.) - width = (1., 1., 1.) + ll = (0.0, 0.0, 0.0) + ur = (10.0, 10.0, 10.0) + width = (1.0, 1.0, 1.0) mesh.set_parameters(lower_left=ll, upper_right=ur) assert mesh.lower_left == pytest.approx(ll) assert mesh.upper_right == pytest.approx(ur) @@ -598,8 +599,7 @@ def test_regular_mesh(lib_init): # Test material volumes mesh = openmc.lib.RegularMesh() mesh.dimension = (2, 2, 1) - mesh.set_parameters(lower_left=(-0.63, -0.63, -0.5), - upper_right=(0.63, 0.63, 0.5)) + mesh.set_parameters(lower_left=(-0.63, -0.63, -0.5), upper_right=(0.63, 0.63, 0.5)) vols = mesh.material_volumes() assert len(vols) == 4 for elem_vols in vols: @@ -608,8 +608,7 @@ def test_regular_mesh(lib_init): # If the mesh extends beyond the boundaries of the model, the volumes should # still be reported correctly mesh.dimension = (1, 1, 1) - mesh.set_parameters(lower_left=(-1.0, -1.0, -0.5), - upper_right=(1.0, 1.0, 0.5)) + mesh.set_parameters(lower_left=(-1.0, -1.0, -0.5), upper_right=(1.0, 1.0, 0.5)) vols = mesh.material_volumes(100_000) for elem_vols in vols: assert sum(f[1] for f in elem_vols) == pytest.approx(1.26 * 1.26, 1e-2) @@ -618,36 +617,35 @@ def test_regular_mesh(lib_init): def test_regular_mesh_get_plot_bins(lib_init): mesh: openmc.lib.RegularMesh = openmc.lib.meshes[2] mesh.dimension = (2, 2, 1) - mesh.set_parameters(lower_left=(-1.0, -1.0, -0.5), - upper_right=(1.0, 1.0, 0.5)) + mesh.set_parameters(lower_left=(-1.0, -1.0, -0.5), upper_right=(1.0, 1.0, 0.5)) # Get bins for a plot view covering only a single mesh bin - mesh_bins = mesh.get_plot_bins((-0.5, -0.5, 0.), (0.1, 0.1), 'xy', (20, 20)) + mesh_bins = mesh.get_plot_bins((-0.5, -0.5, 0.0), (0.1, 0.1), "xy", (20, 20)) assert (mesh_bins == 0).all() - mesh_bins = mesh.get_plot_bins((0.5, 0.5, 0.), (0.1, 0.1), 'xy', (20, 20)) + mesh_bins = mesh.get_plot_bins((0.5, 0.5, 0.0), (0.1, 0.1), "xy", (20, 20)) assert (mesh_bins == 3).all() # Get bins for a plot view covering all mesh bins. Note that the y direction # (first dimension) is flipped for plotting purposes - mesh_bins = mesh.get_plot_bins((0., 0., 0.), (2., 2.), 'xy', (20, 20)) + mesh_bins = mesh.get_plot_bins((0.0, 0.0, 0.0), (2.0, 2.0), "xy", (20, 20)) assert (mesh_bins[:10, :10] == 2).all() assert (mesh_bins[:10, 10:] == 3).all() assert (mesh_bins[10:, :10] == 0).all() assert (mesh_bins[10:, 10:] == 1).all() # Get bins for a plot view outside of the mesh - mesh_bins = mesh.get_plot_bins((100., 100., 0.), (2., 2.), 'xy', (20, 20)) + mesh_bins = mesh.get_plot_bins((100.0, 100.0, 0.0), (2.0, 2.0), "xy", (20, 20)) assert (mesh_bins == -1).all() 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.] + x_grid = [-10.0, 0.0, 10.0] + y_grid = [0.0, 10.0, 20.0] + z_grid = [10.0, 20.0, 30.0] 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.lower_left == (-10.0, 0.0, 10.0)) + assert np.all(mesh.upper_right == (10.0, 20.0, 30.0)) 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)): @@ -658,8 +656,8 @@ def test_rectilinear_mesh(lib_init): # bounding box bbox = mesh.bounding_box - np.testing.assert_allclose(bbox.lower_left, (-10., 0., 10.)) - np.testing.assert_allclose(bbox.upper_right, (10., 20., 30.)) + np.testing.assert_allclose(bbox.lower_left, (-10.0, 0.0, 10.0)) + np.testing.assert_allclose(bbox.upper_right, (10.0, 20.0, 30.0)) with pytest.raises(exc.AllocationError): mesh2 = openmc.lib.RectilinearMesh(mesh.id) @@ -680,38 +678,38 @@ def test_rectilinear_mesh(lib_init): # Test material volumes mesh = openmc.lib.RectilinearMesh() w = 1.26 - mesh.set_grid([-w/2, -w/4, w/2], [-w/2, -w/4, w/2], [-0.5, 0.5]) + mesh.set_grid([-w / 2, -w / 4, w / 2], [-w / 2, -w / 4, w / 2], [-0.5, 0.5]) vols = mesh.material_volumes() assert len(vols) == 4 - assert sum(f[1] for f in vols[0]) == pytest.approx(w/4 * w/4) - assert sum(f[1] for f in vols[1]) == pytest.approx(w/4 * 3*w/4) - assert sum(f[1] for f in vols[2]) == pytest.approx(3*w/4 * w/4) - assert sum(f[1] for f in vols[3]) == pytest.approx(3*w/4 * 3*w/4) + assert sum(f[1] for f in vols[0]) == pytest.approx(w / 4 * w / 4) + assert sum(f[1] for f in vols[1]) == pytest.approx(w / 4 * 3 * w / 4) + assert sum(f[1] for f in vols[2]) == pytest.approx(3 * w / 4 * w / 4) + assert sum(f[1] for f in vols[3]) == pytest.approx(3 * w / 4 * 3 * w / 4) def test_cylindrical_mesh(lib_init): - deg2rad = lambda deg: deg*pi/180 + deg2rad = lambda deg: deg * pi / 180 mesh = openmc.lib.CylindricalMesh() - r_grid = [0., 5., 10.] - phi_grid = np.radians([0., 10., 20.]) - z_grid = [10., 20., 30.] + r_grid = [0.0, 5.0, 10.0] + phi_grid = np.radians([0.0, 10.0, 20.0]) + z_grid = [10.0, 20.0, 30.0] mesh.set_grid(r_grid, phi_grid, z_grid) - assert np.all(mesh.lower_left == (0., 0., 10.)) - assert np.all(mesh.upper_right == (10., deg2rad(20.), 30.)) + assert np.all(mesh.lower_left == (0.0, 0.0, 10.0)) + assert np.all(mesh.upper_right == (10.0, deg2rad(20.0), 30.0)) assert np.all(mesh.dimension == (2, 2, 2)) for i, _ in enumerate(np.diff(r_grid)): for j, _ in enumerate(np.diff(phi_grid)): for k, _ in enumerate(np.diff(z_grid)): assert np.allclose(mesh.width[i, j, k, :], (5, deg2rad(10), 10)) - np.testing.assert_allclose(mesh.volumes[::2], 10/360 * pi * 5**2 * 10) - np.testing.assert_allclose(mesh.volumes[1::2], 10/360 * pi * (10**2 - 5**2) * 10) + np.testing.assert_allclose(mesh.volumes[::2], 10 / 360 * pi * 5**2 * 10) + np.testing.assert_allclose(mesh.volumes[1::2], 10 / 360 * pi * (10**2 - 5**2) * 10) # bounding box bbox = mesh.bounding_box - np.testing.assert_allclose(bbox.lower_left, (-10., -10., 10.)) - np.testing.assert_allclose(bbox.upper_right, (10., 10., 30.)) + np.testing.assert_allclose(bbox.lower_left, (-10.0, -10.0, 10.0)) + np.testing.assert_allclose(bbox.upper_right, (10.0, 10.0, 30.0)) with pytest.raises(exc.AllocationError): mesh2 = openmc.lib.CylindricalMesh(mesh.id) @@ -731,8 +729,8 @@ def test_cylindrical_mesh(lib_init): # Test material volumes mesh = openmc.lib.CylindricalMesh() - r_grid = (0., 0.25, 0.5) - phi_grid = np.linspace(0., 2.0*pi, 4) + r_grid = (0.0, 0.25, 0.5) + phi_grid = np.linspace(0.0, 2.0 * pi, 4) z_grid = (-0.5, 0.5) mesh.set_grid(r_grid, phi_grid, z_grid) @@ -745,31 +743,37 @@ def test_cylindrical_mesh(lib_init): def test_spherical_mesh(lib_init): - deg2rad = lambda deg: deg*np.pi/180 + deg2rad = lambda deg: deg * np.pi / 180 mesh = openmc.lib.SphericalMesh() - r_grid = [0., 5., 10.] - theta_grid = np.radians([0., 10., 20.]) - phi_grid = np.radians([10., 20., 30.]) + r_grid = [0.0, 5.0, 10.0] + theta_grid = np.radians([0.0, 10.0, 20.0]) + phi_grid = np.radians([10.0, 20.0, 30.0]) mesh.set_grid(r_grid, theta_grid, phi_grid) - assert np.all(mesh.lower_left == (0., 0., deg2rad(10.))) - assert np.all(mesh.upper_right == (10., deg2rad(20.), deg2rad(30.))) + assert np.all(mesh.lower_left == (0.0, 0.0, deg2rad(10.0))) + assert np.all(mesh.upper_right == (10.0, deg2rad(20.0), deg2rad(30.0))) assert np.all(mesh.dimension == (2, 2, 2)) for i, _ in enumerate(np.diff(r_grid)): for j, _ in enumerate(np.diff(theta_grid)): for k, _ in enumerate(np.diff(phi_grid)): - assert np.allclose(mesh.width[i, j, k, :], (5, deg2rad(10), deg2rad(10))) + assert np.allclose( + mesh.width[i, j, k, :], (5, deg2rad(10), deg2rad(10)) + ) dtheta = lambda d1, d2: np.cos(deg2rad(d1)) - np.cos(deg2rad(d2)) - f = 1/3 * deg2rad(10.) - np.testing.assert_allclose(mesh.volumes[::4], f * 5**3 * dtheta(0., 10.)) - np.testing.assert_allclose(mesh.volumes[1::4], f * (10**3 - 5**3) * dtheta(0., 10.)) - np.testing.assert_allclose(mesh.volumes[2::4], f * 5**3 * dtheta(10., 20.)) - np.testing.assert_allclose(mesh.volumes[3::4], f * (10**3 - 5**3) * dtheta(10., 20.)) + f = 1 / 3 * deg2rad(10.0) + np.testing.assert_allclose(mesh.volumes[::4], f * 5**3 * dtheta(0.0, 10.0)) + np.testing.assert_allclose( + mesh.volumes[1::4], f * (10**3 - 5**3) * dtheta(0.0, 10.0) + ) + np.testing.assert_allclose(mesh.volumes[2::4], f * 5**3 * dtheta(10.0, 20.0)) + np.testing.assert_allclose( + mesh.volumes[3::4], f * (10**3 - 5**3) * dtheta(10.0, 20.0) + ) # bounding box bbox = mesh.bounding_box - np.testing.assert_allclose(bbox.lower_left, (-10., -10., -10.)) - np.testing.assert_allclose(bbox.upper_right, (10., 10., 10.)) + np.testing.assert_allclose(bbox.lower_left, (-10.0, -10.0, -10.0)) + np.testing.assert_allclose(bbox.upper_right, (10.0, 10.0, 10.0)) with pytest.raises(exc.AllocationError): mesh2 = openmc.lib.SphericalMesh(mesh.id) @@ -789,9 +793,9 @@ def test_spherical_mesh(lib_init): # Test material volumes mesh = openmc.lib.SphericalMesh() - r_grid = (0., 0.25, 0.5) - theta_grid = np.linspace(0., pi, 3) - phi_grid = np.linspace(0., 2.0*pi, 4) + r_grid = (0.0, 0.25, 0.5) + theta_grid = np.linspace(0.0, pi, 3) + phi_grid = np.linspace(0.0, 2.0 * pi, 4) mesh.set_grid(r_grid, theta_grid, phi_grid) vols = mesh.material_volumes() @@ -800,10 +804,12 @@ def test_spherical_mesh(lib_init): d_phi = phi_grid[1] - phi_grid[0] for i in range(0, 12, 2): assert sum(f[1] for f in vols[i]) == pytest.approx( - 0.25**3 / 3 * d_theta * d_phi * 2/pi) + 0.25**3 / 3 * d_theta * d_phi * 2 / pi + ) for i in range(1, 12, 2): assert sum(f[1] for f in vols[i]) == pytest.approx( - (0.5**3 - 0.25**3) / 3 * d_theta * d_phi * 2/pi) + (0.5**3 - 0.25**3) / 3 * d_theta * d_phi * 2 / pi + ) def test_restart(lib_init, mpi_intracomm): @@ -816,7 +822,7 @@ def test_restart(lib_init, mpi_intracomm): # Run for 7 batches then write a statepoint. for i in range(7): openmc.lib.next_batch() - openmc.lib.statepoint_write('restart_test.h5', True) + openmc.lib.statepoint_write("restart_test.h5", True) # Run 3 more batches and copy the keff. for i in range(3): @@ -827,7 +833,7 @@ def test_restart(lib_init, mpi_intracomm): openmc.lib.simulation_finalize() openmc.lib.hard_reset() openmc.lib.finalize() - openmc.lib.init(args=('-r', 'restart_test.h5')) + openmc.lib.init(args=("-r", "restart_test.h5")) openmc.lib.simulation_init() for i in range(3): openmc.lib.next_batch() @@ -840,19 +846,24 @@ def test_restart(lib_init, mpi_intracomm): def test_load_nuclide(lib_init): # load multiple nuclides - openmc.lib.load_nuclide('H3') - assert 'H3' in openmc.lib.nuclides - openmc.lib.load_nuclide('Pu239') - assert 'Pu239' in openmc.lib.nuclides + openmc.lib.load_nuclide("H3") + assert "H3" in openmc.lib.nuclides + openmc.lib.load_nuclide("Pu239") + assert "Pu239" in openmc.lib.nuclides # load non-existent nuclide with pytest.raises(exc.DataError): - openmc.lib.load_nuclide('Pu3') + openmc.lib.load_nuclide("Pu3") def test_id_map(lib_init): - expected_ids = np.array([[(3, 0, 3), (2, 0, 2), (3, 0, 3)], - [(2, 0, 2), (1, 0, 1), (2, 0, 2)], - [(3, 0, 3), (2, 0, 2), (3, 0, 3)]], dtype='int32') + expected_ids = np.array( + [ + [(3, 0, 3), (2, 0, 2), (3, 0, 3)], + [(2, 0, 2), (1, 0, 1), (2, 0, 2)], + [(3, 0, 3), (2, 0, 2), (3, 0, 3)], + ], + dtype="int32", + ) # create a plot object s = openmc.lib.plot._PlotBase() @@ -861,7 +872,7 @@ def test_id_map(lib_init): s.v_res = 3 s.h_res = 3 s.origin = (0.0, 0.0, 0.0) - s.basis = 'xy' + s.basis = "xy" s.level = -1 ids = openmc.lib.plot.id_map(s) @@ -870,9 +881,13 @@ def test_id_map(lib_init): def test_property_map(lib_init): expected_properties = np.array( - [[(293.6, 0.740582), (293.6, 6.55), (293.6, 0.740582)], - [ (293.6, 6.55), (293.6, 10.29769), (293.6, 6.55)], - [(293.6, 0.740582), (293.6, 6.55), (293.6, 0.740582)]], dtype='float') + [ + [(293.6, 0.740582), (293.6, 6.55), (293.6, 0.740582)], + [(293.6, 6.55), (293.6, 10.29769), (293.6, 6.55)], + [(293.6, 0.740582), (293.6, 6.55), (293.6, 0.740582)], + ], + dtype="float", + ) # create a plot object s = openmc.lib.plot._PlotBase() @@ -881,7 +896,7 @@ def test_property_map(lib_init): s.v_res = 3 s.h_res = 3 s.origin = (0.0, 0.0, 0.0) - s.basis = 'xy' + s.basis = "xy" s.level = -1 properties = openmc.lib.plot.property_map(s) @@ -932,8 +947,8 @@ def test_trigger_set_n_batches(uo2_trigger_model, mpi_intracomm): assert openmc.lib.num_realizations() == 15 # Ensure statepoint was created only at batch 20 when calling set_batches - assert not os.path.exists('statepoint.12.h5') - assert os.path.exists('statepoint.20.h5') + assert not os.path.exists("statepoint.12.h5") + assert os.path.exists("statepoint.20.h5") def test_cell_translation(pincell_model_w_univ, mpi_intracomm): @@ -942,16 +957,16 @@ def test_cell_translation(pincell_model_w_univ, mpi_intracomm): # Cell 1 is filled with a material so it has a translation, but we can't # set it. cell = openmc.lib.cells[1] - assert cell.translation == pytest.approx([0., 0., 0.]) - with pytest.raises(exc.GeometryError, match='not filled with'): - cell.translation = (1., 0., -1.) + assert cell.translation == pytest.approx([0.0, 0.0, 0.0]) + with pytest.raises(exc.GeometryError, match="not filled with"): + cell.translation = (1.0, 0.0, -1.0) # Cell 2 was given a universe, so we can assign it a translation vector cell = openmc.lib.cells[2] - assert cell.translation == pytest.approx([0., 0., 0.]) + assert cell.translation == pytest.approx([0.0, 0.0, 0.0]) # This time we *can* set it - cell.translation = (1., 0., -1.) - assert cell.translation == pytest.approx([1., 0., -1.]) + cell.translation = (1.0, 0.0, -1.0) + assert cell.translation == pytest.approx([1.0, 0.0, -1.0]) openmc.lib.finalize() @@ -961,31 +976,31 @@ def test_cell_rotation(pincell_model_w_univ, mpi_intracomm): # Cell 1 is filled with a material so we cannot rotate it, but we can get # its rotation matrix (which will be the identity matrix) cell = openmc.lib.cells[1] - assert cell.rotation == pytest.approx([0., 0., 0.]) - with pytest.raises(exc.GeometryError, match='not filled with'): - cell.rotation = (180., 0., 0.) + assert cell.rotation == pytest.approx([0.0, 0.0, 0.0]) + with pytest.raises(exc.GeometryError, match="not filled with"): + cell.rotation = (180.0, 0.0, 0.0) # Now repeat with Cell 2 and we will be allowed to do it cell = openmc.lib.cells[2] - assert cell.rotation == pytest.approx([0., 0., 0.]) - cell.rotation = (180., 0., 0.) - assert cell.rotation == pytest.approx([180., 0., 0.]) + assert cell.rotation == pytest.approx([0.0, 0.0, 0.0]) + cell.rotation = (180.0, 0.0, 0.0) + assert cell.rotation == pytest.approx([180.0, 0.0, 0.0]) openmc.lib.finalize() def test_sample_external_source(run_in_tmpdir, mpi_intracomm): # Define a simple model and export mat = openmc.Material() - mat.add_nuclide('U235', 1.0e-2) - sph = openmc.Sphere(r=100.0, boundary_type='vacuum') + mat.add_nuclide("U235", 1.0e-2) + sph = openmc.Sphere(r=100.0, boundary_type="vacuum") cell = openmc.Cell(fill=mat, region=-sph) model = openmc.Model() model.geometry = openmc.Geometry([cell]) model.settings.source = openmc.IndependentSource( - space=openmc.stats.Box([-5., -5., -5.], [5., 5., 5.]), - angle=openmc.stats.Monodirectional((0., 0., 1.)), + space=openmc.stats.Box([-5.0, -5.0, -5.0], [5.0, 5.0, 5.0]), + angle=openmc.stats.Monodirectional((0.0, 0.0, 1.0)), energy=openmc.stats.Discrete([1.0e5], [1.0]), - constraints={'fissionable': True} + constraints={"fissionable": True}, ) model.settings.particles = 1000 model.settings.batches = 10 @@ -996,9 +1011,9 @@ def test_sample_external_source(run_in_tmpdir, mpi_intracomm): particles = openmc.lib.sample_external_source(10, prn_seed=3) assert len(particles) == 10 for p in particles: - assert -5. < p.r[0] < 5. - assert -5. < p.r[1] < 5. - assert -5. < p.r[2] < 5. + assert -5.0 < p.r[0] < 5.0 + assert -5.0 < p.r[1] < 5.0 + assert -5.0 < p.r[2] < 5.0 assert p.u[0] == 0.0 assert p.u[1] == 0.0 assert p.u[2] == 1.0 diff --git a/tests/unit_tests/test_lost_particles.py b/tests/unit_tests/test_lost_particles.py index 478ddbaf0f7..ed2aa15d2ca 100644 --- a/tests/unit_tests/test_lost_particles.py +++ b/tests/unit_tests/test_lost_particles.py @@ -9,18 +9,18 @@ @pytest.fixture def model(): mat = openmc.Material() - mat.add_nuclide('N14', 1.0) - mat.set_density('g/cm3', 1e-5) + mat.add_nuclide("N14", 1.0) + mat.set_density("g/cm3", 1e-5) s1 = openmc.Sphere(r=80.0) s2 = openmc.Sphere(r=90.0) - s3 = openmc.Sphere(r=100.0, boundary_type='vacuum') + s3 = openmc.Sphere(r=100.0, boundary_type="vacuum") cell1 = openmc.Cell(fill=mat, region=-s1) cell2 = openmc.Cell(fill=mat, region=+s2 & -s3) model = openmc.Model() model.geometry = openmc.Geometry([cell1, cell2]) - model.settings.run_mode = 'fixed source' + model.settings.run_mode = "fixed source" model.settings.batches = 10 model.settings.inactive = 5 model.settings.particles = 50 @@ -37,13 +37,12 @@ def test_max_write_lost_particles(model: openmc.Model, run_in_tmpdir): # Run OpenMC to generate lost particle files. Use one thread so that we know # exactly how much will be produced. If running in MPI mode, setup proper # keyword arguments for run() - kwargs = {'openmc_exec': config['exe']} - if config['mpi']: - kwargs['mpi_args'] = [config['mpiexec'], '-n', config['mpi_np']] + kwargs = {"openmc_exec": config["exe"]} + if config["mpi"]: + kwargs["mpi_args"] = [config["mpiexec"], "-n", config["mpi_np"]] model.run(threads=1, **kwargs) # Make sure number of lost particle files is as expected - lost_particle_files = list(Path.cwd().glob('particle*.h5')) - n_procs = int(config['mpi_np']) if config['mpi'] else 1 + lost_particle_files = list(Path.cwd().glob("particle*.h5")) + n_procs = int(config["mpi_np"]) if config["mpi"] else 1 assert len(lost_particle_files) == model.settings.max_write_lost_particles * n_procs - diff --git a/tests/unit_tests/test_material.py b/tests/unit_tests/test_material.py index c6a07cff977..3498a7e13f8 100644 --- a/tests/unit_tests/test_material.py +++ b/tests/unit_tests/test_material.py @@ -11,7 +11,7 @@ def test_attributes(uo2): - assert uo2.name == 'UO2' + assert uo2.name == "UO2" assert uo2.id == 100 assert uo2.depletable @@ -19,82 +19,84 @@ def test_attributes(uo2): def test_add_nuclide(): """Test adding nuclides.""" m = openmc.Material() - m.add_nuclide('U235', 1.0) + m.add_nuclide("U235", 1.0) with pytest.raises(TypeError): - m.add_nuclide('H1', '1.0') + m.add_nuclide("H1", "1.0") with pytest.raises(TypeError): - m.add_nuclide(1.0, 'H1') + m.add_nuclide(1.0, "H1") with pytest.raises(ValueError): - m.add_nuclide('H1', 1.0, 'oa') + m.add_nuclide("H1", 1.0, "oa") + def test_add_components(): """Test adding multipe elements or nuclides at once""" m = openmc.Material() - components = {'H1': 2.0, - 'O16': 1.0, - 'Zr': 1.0, - 'O': 1.0, - 'Ag110_m1': 1.0, - 'U': {'percent': 1.0, - 'enrichment': 4.5}, - 'Li': {'percent': 1.0, - 'enrichment': 60.0, - 'enrichment_target': 'Li7'}, - 'H': {'percent': 1.0, - 'enrichment': 50.0, - 'enrichment_target': 'H2', - 'enrichment_type': 'wo'}} + components = { + "H1": 2.0, + "O16": 1.0, + "Zr": 1.0, + "O": 1.0, + "Ag110_m1": 1.0, + "U": {"percent": 1.0, "enrichment": 4.5}, + "Li": {"percent": 1.0, "enrichment": 60.0, "enrichment_target": "Li7"}, + "H": { + "percent": 1.0, + "enrichment": 50.0, + "enrichment_target": "H2", + "enrichment_type": "wo", + }, + } m.add_components(components) with pytest.raises(ValueError): - m.add_components({'U': {'percent': 1.0, - 'enrichment': 100.0}}) + m.add_components({"U": {"percent": 1.0, "enrichment": 100.0}}) with pytest.raises(ValueError): - m.add_components({'Pu': {'percent': 1.0, - 'enrichment': 3.0}}) + m.add_components({"Pu": {"percent": 1.0, "enrichment": 3.0}}) with pytest.raises(ValueError): - m.add_components({'U': {'percent': 1.0, - 'enrichment': 70.0, - 'enrichment_target':'U235'}}) + m.add_components( + {"U": {"percent": 1.0, "enrichment": 70.0, "enrichment_target": "U235"}} + ) with pytest.raises(ValueError): - m.add_components({'He': {'percent': 1.0, - 'enrichment': 17.0, - 'enrichment_target': 'He6'}}) + m.add_components( + {"He": {"percent": 1.0, "enrichment": 17.0, "enrichment_target": "He6"}} + ) with pytest.raises(ValueError): - m.add_components({'li': 1.0}) # should fail as 1st char is lowercase + m.add_components({"li": 1.0}) # should fail as 1st char is lowercase with pytest.raises(ValueError): - m.add_components({'LI': 1.0}) # should fail as 2nd char is uppercase + m.add_components({"LI": 1.0}) # should fail as 2nd char is uppercase with pytest.raises(ValueError): - m.add_components({'Xx': 1.0}) # should fail as Xx is not an element + m.add_components({"Xx": 1.0}) # should fail as Xx is not an element with pytest.raises(ValueError): - m.add_components({'n': 1.0}) # check to avoid n for neutron being accepted + m.add_components({"n": 1.0}) # check to avoid n for neutron being accepted with pytest.raises(TypeError): - m.add_components({'H1': '1.0'}) + m.add_components({"H1": "1.0"}) with pytest.raises(TypeError): - m.add_components({1.0: 'H1'}, percent_type = 'wo') + m.add_components({1.0: "H1"}, percent_type="wo") with pytest.raises(ValueError): - m.add_components({'H1': 1.0}, percent_type = 'oa') + m.add_components({"H1": 1.0}, percent_type="oa") + def test_nuclides_to_ignore(run_in_tmpdir): """Test nuclides_to_ignore when exporting a material to XML""" m = openmc.Material() - m.add_nuclide('U235', 1.0) - m.add_nuclide('H1', 1.0) - m.add_nuclide('O16', 1.0) + m.add_nuclide("U235", 1.0) + m.add_nuclide("H1", 1.0) + m.add_nuclide("O16", 1.0) mats = openmc.Materials([m]) - mats.export_to_xml(nuclides_to_ignore=['H1']) + mats.export_to_xml(nuclides_to_ignore=["H1"]) test_mats = openmc.Materials.from_xml() - assert 'H1' not in test_mats[0].get_nuclides() + assert "H1" not in test_mats[0].get_nuclides() + def test_remove_nuclide(): """Test removing nuclides.""" m = openmc.Material() - for nuc, percent in [('H1', 1.0), ('H2', 1.0), ('H1', 2.0), ('H2', 2.0)]: + for nuc, percent in [("H1", 1.0), ("H2", 1.0), ("H1", 2.0), ("H2", 2.0)]: m.add_nuclide(nuc, percent) - m.remove_nuclide('H1') + m.remove_nuclide("H1") assert len(m.nuclides) == 2 - assert all(nuc.name == 'H2' for nuc in m.nuclides) + assert all(nuc.name == "H2" for nuc in m.nuclides) assert m.nuclides[0].percent == 1.0 assert m.nuclides[1].percent == 2.0 @@ -102,53 +104,55 @@ def test_remove_nuclide(): def test_remove_elements(): """Test removing elements.""" m = openmc.Material() - for elem, percent in [('Li', 1.0), ('Be', 1.0)]: + for elem, percent in [("Li", 1.0), ("Be", 1.0)]: m.add_element(elem, percent) - m.remove_element('Li') + m.remove_element("Li") assert len(m.nuclides) == 1 - assert m.nuclides[0].name == 'Be9' + assert m.nuclides[0].name == "Be9" assert m.nuclides[0].percent == 1.0 def test_add_element(): """Test adding elements.""" m = openmc.Material() - m.add_element('Zr', 1.0) - m.add_element('U', 1.0, enrichment=4.5) - m.add_element('Li', 1.0, enrichment=60.0, enrichment_target='Li7') - m.add_element('H', 1.0, enrichment=50.0, enrichment_target='H2', - enrichment_type='wo') + m.add_element("Zr", 1.0) + m.add_element("U", 1.0, enrichment=4.5) + m.add_element("Li", 1.0, enrichment=60.0, enrichment_target="Li7") + m.add_element( + "H", 1.0, enrichment=50.0, enrichment_target="H2", enrichment_type="wo" + ) with pytest.raises(ValueError): - m.add_element('U', 1.0, enrichment=100.0) + m.add_element("U", 1.0, enrichment=100.0) with pytest.raises(ValueError): - m.add_element('Pu', 1.0, enrichment=3.0) + m.add_element("Pu", 1.0, enrichment=3.0) with pytest.raises(ValueError): - m.add_element('U', 1.0, enrichment=70.0, enrichment_target='U235') + m.add_element("U", 1.0, enrichment=70.0, enrichment_target="U235") with pytest.raises(ValueError): - m.add_element('He', 1.0, enrichment=17.0, enrichment_target='He6') + m.add_element("He", 1.0, enrichment=17.0, enrichment_target="He6") with pytest.raises(ValueError): - m.add_element('li', 1.0) # should fail as 1st char is lowercase + m.add_element("li", 1.0) # should fail as 1st char is lowercase with pytest.raises(ValueError): - m.add_element('LI', 1.0) # should fail as 2nd char is uppercase + m.add_element("LI", 1.0) # should fail as 2nd char is uppercase with pytest.raises(ValueError): - m.add_element('Xx', 1.0) # should fail as Xx is not an element + m.add_element("Xx", 1.0) # should fail as Xx is not an element with pytest.raises(ValueError): - m.add_element('n', 1.0) # check to avoid n for neutron being accepted + m.add_element("n", 1.0) # check to avoid n for neutron being accepted + def test_elements_by_name(): """Test adding elements by name""" m = openmc.Material() - m.add_element('woLfrAm', 1.0) + m.add_element("woLfrAm", 1.0) with pytest.raises(ValueError): - m.add_element('uranum', 1.0) - m.add_element('uRaNiUm', 1.0) - m.add_element('Aluminium', 1.0) + m.add_element("uranum", 1.0) + m.add_element("uRaNiUm", 1.0) + m.add_element("Aluminium", 1.0) a = openmc.Material() b = openmc.Material() c = openmc.Material() - a.add_element('sulfur', 1.0) - b.add_element('SulPhUR', 1.0) - c.add_element('S', 1.0) + a.add_element("sulfur", 1.0) + b.add_element("SulPhUR", 1.0) + c.add_element("S", 1.0) assert a._nuclides == b._nuclides assert b._nuclides == c._nuclides @@ -157,7 +161,7 @@ def test_add_elements_by_formula(): """Test adding elements from a formula""" # testing the correct nuclides and elements are added to a material m = openmc.Material() - m.add_elements_from_formula('Li4SiO4') + m.add_elements_from_formula("Li4SiO4") # checking the ratio of elements is 4:1:4 for Li:Si:O elem = defaultdict(float) for nuclide, adens in m.get_nuclide_atom_densities().items(): @@ -168,32 +172,42 @@ def test_add_elements_by_formula(): if nuclide.startswith("O"): elem["O"] += adens total_number_of_atoms = 9 - assert elem["Li"] == pytest.approx(4./total_number_of_atoms) - assert elem["Si"] == pytest.approx(1./total_number_of_atoms) - assert elem["O"] == pytest.approx(4/total_number_of_atoms) + assert elem["Li"] == pytest.approx(4.0 / total_number_of_atoms) + assert elem["Si"] == pytest.approx(1.0 / total_number_of_atoms) + assert elem["O"] == pytest.approx(4 / total_number_of_atoms) # testing the correct nuclides are added to the Material - ref_dens = {'Li6': 0.033728, 'Li7': 0.410715, - 'Si28': 0.102477, 'Si29': 0.0052035, 'Si30': 0.0034301, - 'O16': 0.443386, 'O17': 0.000168} + ref_dens = { + "Li6": 0.033728, + "Li7": 0.410715, + "Si28": 0.102477, + "Si29": 0.0052035, + "Si30": 0.0034301, + "O16": 0.443386, + "O17": 0.000168, + } nuc_dens = m.get_nuclide_atom_densities() for nuclide in ref_dens: assert nuc_dens[nuclide] == pytest.approx(ref_dens[nuclide], 1e-2) # testing the correct nuclides are added to the Material when enriched m = openmc.Material() - m.add_elements_from_formula('Li4SiO4', - enrichment=60., - enrichment_target='Li6') - ref_dens = {'Li6': 0.2666, 'Li7': 0.1777, - 'Si28': 0.102477, 'Si29': 0.0052035, 'Si30': 0.0034301, - 'O16': 0.443386, 'O17': 0.000168} + m.add_elements_from_formula("Li4SiO4", enrichment=60.0, enrichment_target="Li6") + ref_dens = { + "Li6": 0.2666, + "Li7": 0.1777, + "Si28": 0.102477, + "Si29": 0.0052035, + "Si30": 0.0034301, + "O16": 0.443386, + "O17": 0.000168, + } nuc_dens = m.get_nuclide_atom_densities() for nuclide in ref_dens: assert nuc_dens[nuclide] == pytest.approx(ref_dens[nuclide], 1e-2) # testing the use of brackets m = openmc.Material() - m.add_elements_from_formula('Mg2(NO3)2') + m.add_elements_from_formula("Mg2(NO3)2") # checking the ratio of elements is 2:2:6 for Mg:N:O elem = defaultdict(float) @@ -205,14 +219,20 @@ def test_add_elements_by_formula(): if nuclide.startswith("O"): elem["O"] += adens total_number_of_atoms = 10 - assert elem["Mg"] == pytest.approx(2./total_number_of_atoms) - assert elem["N"] == pytest.approx(2./total_number_of_atoms) - assert elem["O"] == pytest.approx(6/total_number_of_atoms) + assert elem["Mg"] == pytest.approx(2.0 / total_number_of_atoms) + assert elem["N"] == pytest.approx(2.0 / total_number_of_atoms) + assert elem["O"] == pytest.approx(6 / total_number_of_atoms) # testing the correct nuclides are added when brackets are used - ref_dens = {'Mg24': 0.157902, 'Mg25': 0.02004, 'Mg26': 0.022058, - 'N14': 0.199267, 'N15': 0.000732, - 'O16': 0.599772, 'O17': 0.000227} + ref_dens = { + "Mg24": 0.157902, + "Mg25": 0.02004, + "Mg26": 0.022058, + "N14": 0.199267, + "N15": 0.000732, + "O16": 0.599772, + "O17": 0.000227, + } nuc_dens = m.get_nuclide_atom_densities() for nuclide in ref_dens: assert nuc_dens[nuclide] == pytest.approx(ref_dens[nuclide], 1e-2) @@ -220,70 +240,70 @@ def test_add_elements_by_formula(): # testing non integer multiplier results in a value error m = openmc.Material() with pytest.raises(ValueError): - m.add_elements_from_formula('Li4.2SiO4') + m.add_elements_from_formula("Li4.2SiO4") # testing lowercase elements results in a value error m = openmc.Material() with pytest.raises(ValueError): - m.add_elements_from_formula('li4SiO4') + m.add_elements_from_formula("li4SiO4") # testing lowercase elements results in a value error m = openmc.Material() with pytest.raises(ValueError): - m.add_elements_from_formula('Li4Sio4') + m.add_elements_from_formula("Li4Sio4") # testing incorrect character in formula results in a value error m = openmc.Material() with pytest.raises(ValueError): - m.add_elements_from_formula('Li4$SiO4') + m.add_elements_from_formula("Li4$SiO4") # testing unequal opening and closing brackets m = openmc.Material() with pytest.raises(ValueError): - m.add_elements_from_formula('Fe(H2O)4(OH)2)') + m.add_elements_from_formula("Fe(H2O)4(OH)2)") def test_density(): m = openmc.Material() - for unit in ['g/cm3', 'g/cc', 'kg/m3', 'atom/b-cm', 'atom/cm3']: + for unit in ["g/cm3", "g/cc", "kg/m3", "atom/b-cm", "atom/cm3"]: m.set_density(unit, 1.0) with pytest.raises(ValueError): - m.set_density('g/litre', 1.0) + m.set_density("g/litre", 1.0) def test_salphabeta(): m = openmc.Material() - m.add_s_alpha_beta('c_H_in_H2O', 0.5) + m.add_s_alpha_beta("c_H_in_H2O", 0.5) def test_repr(): m = openmc.Material() - m.add_nuclide('Zr90', 1.0) - m.add_nuclide('H2', 0.5) - m.add_s_alpha_beta('c_D_in_D2O') - m.set_density('sum') + m.add_nuclide("Zr90", 1.0) + m.add_nuclide("H2", 0.5) + m.add_s_alpha_beta("c_D_in_D2O") + m.set_density("sum") m.temperature = 600.0 repr(m) def test_macroscopic(run_in_tmpdir): - m = openmc.Material(name='UO2') - m.add_macroscopic('UO2') + m = openmc.Material(name="UO2") + m.add_macroscopic("UO2") with pytest.raises(ValueError): - m.add_nuclide('H1', 1.0) + m.add_nuclide("H1", 1.0) with pytest.raises(ValueError): - m.add_element('O', 1.0) + m.add_element("O", 1.0) with pytest.raises(ValueError): - m.add_macroscopic('Other') + m.add_macroscopic("Other") m2 = openmc.Material() - m2.add_nuclide('He4', 1.0) + m2.add_nuclide("He4", 1.0) with pytest.raises(ValueError): - m2.add_macroscopic('UO2') + m2.add_macroscopic("UO2") # Make sure we can remove/add macroscopic - m.remove_macroscopic('UO2') - m.add_macroscopic('UO2') + m.remove_macroscopic("UO2") + m.add_macroscopic("UO2") repr(m) # Make sure we can export a material with macroscopic data @@ -301,34 +321,34 @@ def test_paths(): def test_isotropic(): m1 = openmc.Material() - m1.add_nuclide('U235', 1.0) - m1.add_nuclide('O16', 2.0) - m1.isotropic = ['O16'] - assert m1.isotropic == ['O16'] + m1.add_nuclide("U235", 1.0) + m1.add_nuclide("O16", 2.0) + m1.isotropic = ["O16"] + assert m1.isotropic == ["O16"] m2 = openmc.Material() - m2.add_nuclide('H1', 1.0) + m2.add_nuclide("H1", 1.0) mats = openmc.Materials([m1, m2]) mats.make_isotropic_in_lab() - assert m1.isotropic == ['U235', 'O16'] - assert m2.isotropic == ['H1'] + assert m1.isotropic == ["U235", "O16"] + assert m2.isotropic == ["H1"] def test_get_nuclides(): mat = openmc.Material() - mat.add_nuclide('Li6', 1.0) - assert mat.get_nuclides() == ['Li6'] - assert mat.get_nuclides(element='Li') == ['Li6'] - assert mat.get_nuclides(element='Be') == [] + mat.add_nuclide("Li6", 1.0) + assert mat.get_nuclides() == ["Li6"] + assert mat.get_nuclides(element="Li") == ["Li6"] + assert mat.get_nuclides(element="Be") == [] - mat.add_element('Li', 1.0) - assert mat.get_nuclides() == ['Li6', 'Li7'] - assert mat.get_nuclides(element='Be') == [] + mat.add_element("Li", 1.0) + assert mat.get_nuclides() == ["Li6", "Li7"] + assert mat.get_nuclides(element="Be") == [] - mat.add_element('Be', 1.0) - assert mat.get_nuclides() == ['Li6', 'Li7', 'Be9'] - assert mat.get_nuclides(element='Be') == ['Be9'] + mat.add_element("Be", 1.0) + assert mat.get_nuclides() == ["Li6", "Li7", "Be9"] + assert mat.get_nuclides(element="Be") == ["Be9"] def test_get_elements(): @@ -337,120 +357,120 @@ def test_get_elements(): assert len(m.get_elements()) == 0 # test addition of a single element - m.add_element('Li', 0.2) + m.add_element("Li", 0.2) assert m.get_elements() == ["Li"] # test that adding the same element - m.add_element('Li', 0.3) + m.add_element("Li", 0.3) assert m.get_elements() == ["Li"] # test adding another element - m.add_element('Si', 0.3) + m.add_element("Si", 0.3) assert m.get_elements() == ["Li", "Si"] # test adding a third element - m.add_element('O', 0.4) + m.add_element("O", 0.4) assert m.get_elements() == ["Li", "O", "Si"] # test removal of nuclides - m.remove_nuclide('O16') - m.remove_nuclide('O17') + m.remove_nuclide("O16") + m.remove_nuclide("O17") assert m.get_elements() == ["Li", "Si"] def test_get_nuclide_densities(uo2): nucs = uo2.get_nuclide_densities() for nuc, density, density_type in nucs.values(): - assert nuc in ('U235', 'O16') + assert nuc in ("U235", "O16") assert density > 0 - assert density_type in ('ao', 'wo') + assert density_type in ("ao", "wo") def test_get_nuclide_atom_densities(uo2): for nuc, density in uo2.get_nuclide_atom_densities().items(): - assert nuc in ('U235', 'O16') + assert nuc in ("U235", "O16") assert density > 0 def test_get_nuclide_atom_densities_specific(uo2): - one_nuc = uo2.get_nuclide_atom_densities(nuclide='O16') - assert list(one_nuc.keys()) == ['O16'] + one_nuc = uo2.get_nuclide_atom_densities(nuclide="O16") + assert list(one_nuc.keys()) == ["O16"] assert list(one_nuc.values())[0] > 0 all_nuc = uo2.get_nuclide_atom_densities() - assert all_nuc['O16'] == one_nuc['O16'] + assert all_nuc["O16"] == one_nuc["O16"] def test_get_element_atom_densities(uo2): for element, density in uo2.get_element_atom_densities().items(): - assert element in ('U', 'O') + assert element in ("U", "O") assert density > 0 def test_get_element_atom_densities_specific(uo2): - one_nuc = uo2.get_element_atom_densities('O') - assert list(one_nuc.keys()) == ['O'] + one_nuc = uo2.get_element_atom_densities("O") + assert list(one_nuc.keys()) == ["O"] assert list(one_nuc.values())[0] > 0 - one_nuc = uo2.get_element_atom_densities('uranium') - assert list(one_nuc.keys()) == ['U'] + one_nuc = uo2.get_element_atom_densities("uranium") + assert list(one_nuc.keys()) == ["U"] assert list(one_nuc.values())[0] > 0 - with pytest.raises(ValueError, match='not found'): - uo2.get_element_atom_densities('Li') + with pytest.raises(ValueError, match="not found"): + uo2.get_element_atom_densities("Li") - with pytest.raises(ValueError, match='not recognized'): - uo2.get_element_atom_densities('proximium') + with pytest.raises(ValueError, match="not recognized"): + uo2.get_element_atom_densities("proximium") def test_get_nuclide_atoms(): mat = openmc.Material() - mat.add_nuclide('Li6', 1.0) - mat.set_density('atom/cm3', 3.26e20) + mat.add_nuclide("Li6", 1.0) + mat.set_density("atom/cm3", 3.26e20) mat.volume = 100.0 atoms = mat.get_nuclide_atoms() - assert atoms['Li6'] == pytest.approx(mat.density * mat.volume) + assert atoms["Li6"] == pytest.approx(mat.density * mat.volume) atoms = mat.get_nuclide_atoms(volume=10.0) - assert atoms['Li6'] == pytest.approx(mat.density * 10.0) + assert atoms["Li6"] == pytest.approx(mat.density * 10.0) def test_mass(): m = openmc.Material() - m.add_nuclide('Zr90', 1.0, 'wo') - m.add_nuclide('U235', 1.0, 'wo') - m.set_density('g/cm3', 2.0) + m.add_nuclide("Zr90", 1.0, "wo") + m.add_nuclide("U235", 1.0, "wo") + m.set_density("g/cm3", 2.0) m.volume = 10.0 - assert m.get_mass_density('Zr90') == pytest.approx(1.0) - assert m.get_mass_density('U235') == pytest.approx(1.0) + assert m.get_mass_density("Zr90") == pytest.approx(1.0) + assert m.get_mass_density("U235") == pytest.approx(1.0) assert m.get_mass_density() == pytest.approx(2.0) - assert m.get_mass('Zr90') == pytest.approx(10.0) - assert m.get_mass('U235') == pytest.approx(10.0) + assert m.get_mass("Zr90") == pytest.approx(10.0) + assert m.get_mass("U235") == pytest.approx(10.0) assert m.get_mass() == pytest.approx(20.0) assert m.fissionable_mass == pytest.approx(10.0) # Test with volume specified as argument - assert m.get_mass('Zr90', volume=1.0) == pytest.approx(1.0) + assert m.get_mass("Zr90", volume=1.0) == pytest.approx(1.0) def test_materials(run_in_tmpdir): m1 = openmc.Material() - m1.add_nuclide('U235', 1.0, 'wo') - m1.add_nuclide('O16', 2.0, 'wo') - m1.set_density('g/cm3', 10.0) + m1.add_nuclide("U235", 1.0, "wo") + m1.add_nuclide("O16", 2.0, "wo") + m1.set_density("g/cm3", 10.0) m1.depletable = True m1.temperature = 900.0 m2 = openmc.Material() - m2.add_nuclide('H1', 2.0) - m2.add_nuclide('O16', 1.0) - m2.add_s_alpha_beta('c_H_in_H2O') - m2.set_density('kg/m3', 1000.0) + m2.add_nuclide("H1", 2.0) + m2.add_nuclide("O16", 1.0) + m2.add_s_alpha_beta("c_H_in_H2O") + m2.set_density("kg/m3", 1000.0) mats = openmc.Materials([m1, m2]) - mats.cross_sections = '/some/fake/cross_sections.xml' + mats.cross_sections = "/some/fake/cross_sections.xml" mats.export_to_xml() @@ -459,20 +479,24 @@ def test_borated_water(): m = openmc.model.borated_water(975, 566.5, 15.51, material_id=50) assert m.density == pytest.approx(0.7405, 1e-3) assert m.temperature == pytest.approx(566.5) - assert m._sab[0][0] == 'c_H_in_H2O' - ref_dens = {'B10':8.0023e-06, 'B11':3.2210e-05, 'H1':4.9458e-02, - 'O16':2.4672e-02} + assert m._sab[0][0] == "c_H_in_H2O" + ref_dens = { + "B10": 8.0023e-06, + "B11": 3.2210e-05, + "H1": 4.9458e-02, + "O16": 2.4672e-02, + } nuc_dens = m.get_nuclide_atom_densities() for nuclide in ref_dens: assert nuc_dens[nuclide] == pytest.approx(ref_dens[nuclide], 1e-2) assert m.id == 50 # Test the Celsius conversion. - m = openmc.model.borated_water(975, 293.35, 15.51, 'C') + m = openmc.model.borated_water(975, 293.35, 15.51, "C") assert m.density == pytest.approx(0.7405, 1e-3) # Test Fahrenheit and psi conversions. - m = openmc.model.borated_water(975, 560.0, 2250.0, 'F', 'psi') + m = openmc.model.borated_water(975, 560.0, 2250.0, "F", "psi") assert m.density == pytest.approx(0.7405, 1e-3) # Test the density override @@ -482,22 +506,22 @@ def test_borated_water(): def test_from_xml(run_in_tmpdir): # Create a materials.xml file - m1 = openmc.Material(1, 'water') - m1.add_nuclide('H1', 1.0) - m1.add_nuclide('O16', 2.0) - m1.add_s_alpha_beta('c_H_in_H2O') + m1 = openmc.Material(1, "water") + m1.add_nuclide("H1", 1.0) + m1.add_nuclide("O16", 2.0) + m1.add_s_alpha_beta("c_H_in_H2O") m1.temperature = 300 m1.volume = 100 - m1.set_density('g/cm3', 0.9) - m1.isotropic = ['H1'] - m2 = openmc.Material(2, 'zirc') - m2.add_nuclide('Zr90', 1.0, 'wo') - m2.set_density('kg/m3', 10.0) + m1.set_density("g/cm3", 0.9) + m1.isotropic = ["H1"] + m2 = openmc.Material(2, "zirc") + m2.add_nuclide("Zr90", 1.0, "wo") + m2.set_density("kg/m3", 10.0) m3 = openmc.Material(3) - m3.add_nuclide('N14', 0.02) + m3.add_nuclide("N14", 0.02) mats = openmc.Materials([m1, m2, m3]) - mats.cross_sections = 'fake_path.xml' + mats.cross_sections = "fake_path.xml" mats.export_to_xml() # Regenerate materials from XML @@ -505,36 +529,36 @@ def test_from_xml(run_in_tmpdir): assert len(mats) == 3 m1 = mats[0] assert m1.id == 1 - assert m1.name == 'water' - assert m1.nuclides == [('H1', 1.0, 'ao'), ('O16', 2.0, 'ao')] - assert m1.isotropic == ['H1'] + assert m1.name == "water" + assert m1.nuclides == [("H1", 1.0, "ao"), ("O16", 2.0, "ao")] + assert m1.isotropic == ["H1"] assert m1.temperature == 300 assert m1.volume == 100 m2 = mats[1] - assert m2.nuclides == [('Zr90', 1.0, 'wo')] + assert m2.nuclides == [("Zr90", 1.0, "wo")] assert m2.density == 10.0 - assert m2.density_units == 'kg/m3' - assert mats[2].density_units == 'sum' + assert m2.density_units == "kg/m3" + assert mats[2].density_units == "sum" def test_mix_materials(): m1 = openmc.Material() - m1.add_nuclide('U235', 1.) + m1.add_nuclide("U235", 1.0) m1dens = 10.0 m1amm = m1.average_molar_mass - m1.set_density('g/cm3', m1dens) + m1.set_density("g/cm3", m1dens) m2 = openmc.Material() - m2.add_nuclide('Zr90', 1.) + m2.add_nuclide("Zr90", 1.0) m2dens = 2.0 m2amm = m2.average_molar_mass - m2.set_density('g/cm3', m2dens) + m2.set_density("g/cm3", m2dens) f0, f1 = 0.6, 0.4 - dens3 = (f0*m1amm + f1*m2amm) / (f0*m1amm/m1dens + f1*m2amm/m2dens) - dens4 = 1. / (f0 / m1dens + f1 / m2dens) - dens5 = f0*m1dens + f1*m2dens - m3 = openmc.Material.mix_materials([m1, m2], [f0, f1], percent_type='ao') - m4 = openmc.Material.mix_materials([m1, m2], [f0, f1], percent_type='wo') - m5 = openmc.Material.mix_materials([m1, m2], [f0, f1], percent_type='vo') + dens3 = (f0 * m1amm + f1 * m2amm) / (f0 * m1amm / m1dens + f1 * m2amm / m2dens) + dens4 = 1.0 / (f0 / m1dens + f1 / m2dens) + dens5 = f0 * m1dens + f1 * m2dens + m3 = openmc.Material.mix_materials([m1, m2], [f0, f1], percent_type="ao") + m4 = openmc.Material.mix_materials([m1, m2], [f0, f1], percent_type="wo") + m5 = openmc.Material.mix_materials([m1, m2], [f0, f1], percent_type="vo") assert m3.density == pytest.approx(dens3) assert m4.density == pytest.approx(dens4) assert m5.density == pytest.approx(dens5) @@ -547,104 +571,130 @@ def test_get_activity(): m1 = openmc.Material() m1.add_element("Fe", 0.7) m1.add_element("Li", 0.3) - m1.set_density('g/cm3', 1.5) + m1.set_density("g/cm3", 1.5) # activity in Bq/cc and Bq/g should not require volume setting - assert m1.get_activity(units='Bq/cm3') == 0 - assert m1.get_activity(units='Bq/g') == 0 + assert m1.get_activity(units="Bq/cm3") == 0 + assert m1.get_activity(units="Bq/g") == 0 m1.volume = 1 - assert m1.get_activity(units='Bq') == 0 + assert m1.get_activity(units="Bq") == 0 # Checks that 1g of tritium has the correct activity scaling m2 = openmc.Material() m2.add_nuclide("H3", 1) - m2.set_density('g/cm3', 1) + m2.set_density("g/cm3", 1) m2.volume = 1 - assert pytest.approx(m2.get_activity(units='Bq')) == 3.559778e14 - m2.set_density('g/cm3', 2) - assert pytest.approx(m2.get_activity(units='Bq')) == 3.559778e14*2 + assert pytest.approx(m2.get_activity(units="Bq")) == 3.559778e14 + m2.set_density("g/cm3", 2) + assert pytest.approx(m2.get_activity(units="Bq")) == 3.559778e14 * 2 m2.volume = 3 - assert pytest.approx(m2.get_activity(units='Bq')) == 3.559778e14*2*3 + assert pytest.approx(m2.get_activity(units="Bq")) == 3.559778e14 * 2 * 3 # Checks that 1 mol of a metastable nuclides has the correct activity m3 = openmc.Material() m3.add_nuclide("Tc99_m1", 1) - m3.set_density('g/cm3', 1) + m3.set_density("g/cm3", 1) m3.volume = 98.9 - assert pytest.approx(m3.get_activity(units='Bq'), rel=0.001) == 1.93e19 + assert pytest.approx(m3.get_activity(units="Bq"), rel=0.001) == 1.93e19 # Checks that specific and volumetric activity of tritium are correct m4 = openmc.Material() m4.add_nuclide("H3", 1) - m4.set_density('g/cm3', 1.5) - assert pytest.approx(m4.get_activity(units='Bq/g')) == 355978108155965.94 # [Bq/g] - assert pytest.approx(m4.get_activity(units='Bq/g', by_nuclide=True)["H3"]) == 355978108155965.94 # [Bq/g] - assert pytest.approx(m4.get_activity(units='Bq/cm3')) == 355978108155965.94*3/2 # [Bq/cc] - assert pytest.approx(m4.get_activity(units='Bq/cm3', by_nuclide=True)["H3"]) == 355978108155965.94*3/2 # [Bq/cc] + m4.set_density("g/cm3", 1.5) + assert pytest.approx(m4.get_activity(units="Bq/g")) == 355978108155965.94 # [Bq/g] + assert ( + pytest.approx(m4.get_activity(units="Bq/g", by_nuclide=True)["H3"]) + == 355978108155965.94 + ) # [Bq/g] + assert ( + pytest.approx(m4.get_activity(units="Bq/cm3")) == 355978108155965.94 * 3 / 2 + ) # [Bq/cc] + assert ( + pytest.approx(m4.get_activity(units="Bq/cm3", by_nuclide=True)["H3"]) + == 355978108155965.94 * 3 / 2 + ) # [Bq/cc] # volume is required to calculate total activity - m4.volume = 10. - assert pytest.approx(m4.get_activity(units='Bq')) == 355978108155965.94*3/2*10 # [Bq] + m4.volume = 10.0 + assert ( + pytest.approx(m4.get_activity(units="Bq")) == 355978108155965.94 * 3 / 2 * 10 + ) # [Bq] # Test with volume specified as argument - assert pytest.approx(m4.get_activity(units='Bq', volume=1.0)) == 355978108155965.94*3/2 + assert ( + pytest.approx(m4.get_activity(units="Bq", volume=1.0)) + == 355978108155965.94 * 3 / 2 + ) def test_get_decay_heat(): # Set chain file for testing - openmc.config['chain_file'] = Path(__file__).parents[1] / 'chain_simple.xml' + openmc.config["chain_file"] = Path(__file__).parents[1] / "chain_simple.xml" """Tests the decay heat of stable, metastable and active materials""" m1 = openmc.Material() m1.add_nuclide("U235", 0.2) m1.add_nuclide("U238", 0.8) - m1.set_density('g/cm3', 10.5) + m1.set_density("g/cm3", 10.5) # decay heat in W/cc and W/g should not require volume setting - assert m1.get_decay_heat(units='W/cm3') == 0 - assert m1.get_decay_heat(units='W/g') == 0 + assert m1.get_decay_heat(units="W/cm3") == 0 + assert m1.get_decay_heat(units="W/g") == 0 m1.volume = 1 - assert m1.get_decay_heat(units='W') == 0 + assert m1.get_decay_heat(units="W") == 0 # Checks that 1g of tritium has the correct decay heat scaling m2 = openmc.Material() m2.add_nuclide("I135", 1) - m2.set_density('g/cm3', 1) + m2.set_density("g/cm3", 1) m2.volume = 1 - assert pytest.approx(m2.get_decay_heat(units='W')) == 40175.15720273193 - m2.set_density('g/cm3', 2) - assert pytest.approx(m2.get_decay_heat(units='W')) == 40175.15720273193*2 + assert pytest.approx(m2.get_decay_heat(units="W")) == 40175.15720273193 + m2.set_density("g/cm3", 2) + assert pytest.approx(m2.get_decay_heat(units="W")) == 40175.15720273193 * 2 m2.volume = 3 - assert pytest.approx(m2.get_decay_heat(units='W')) == 40175.15720273193*2*3 + assert pytest.approx(m2.get_decay_heat(units="W")) == 40175.15720273193 * 2 * 3 # Checks that 1 mol of a metastable nuclides has the correct decay heat m3 = openmc.Material() m3.add_nuclide("Xe135", 1) - m3.set_density('g/cm3', 1) + m3.set_density("g/cm3", 1) m3.volume = 98.9 - assert pytest.approx(m3.get_decay_heat(units='W'), rel=0.001) == 846181.2921143445 + assert pytest.approx(m3.get_decay_heat(units="W"), rel=0.001) == 846181.2921143445 # Checks that specific and volumetric decay heat of tritium are correct m4 = openmc.Material() m4.add_nuclide("I135", 1) - m4.set_density('g/cm3', 1.5) - assert pytest.approx(m4.get_decay_heat(units='W/g')) == 40175.15720273193 # [W/g] - assert pytest.approx(m4.get_decay_heat(units='W/g', by_nuclide=True)["I135"]) == 40175.15720273193 # [W/g] - assert pytest.approx(m4.get_decay_heat(units='W/cm3')) == 40175.15720273193*3/2 # [W/cc] - assert pytest.approx(m4.get_decay_heat(units='W/cm3', by_nuclide=True)["I135"]) == 40175.15720273193*3/2 #[W/cc] + m4.set_density("g/cm3", 1.5) + assert pytest.approx(m4.get_decay_heat(units="W/g")) == 40175.15720273193 # [W/g] + assert ( + pytest.approx(m4.get_decay_heat(units="W/g", by_nuclide=True)["I135"]) + == 40175.15720273193 + ) # [W/g] + assert ( + pytest.approx(m4.get_decay_heat(units="W/cm3")) == 40175.15720273193 * 3 / 2 + ) # [W/cc] + assert ( + pytest.approx(m4.get_decay_heat(units="W/cm3", by_nuclide=True)["I135"]) + == 40175.15720273193 * 3 / 2 + ) # [W/cc] # volume is required to calculate total decay heat - m4.volume = 10. - assert pytest.approx(m4.get_decay_heat(units='W')) == 40175.15720273193*3/2*10 # [W] + m4.volume = 10.0 + assert ( + pytest.approx(m4.get_decay_heat(units="W")) == 40175.15720273193 * 3 / 2 * 10 + ) # [W] # Test with volume specified as argument - assert pytest.approx(m4.get_decay_heat(units='W', volume=1.0)) == 40175.15720273193*3/2 + assert ( + pytest.approx(m4.get_decay_heat(units="W", volume=1.0)) + == 40175.15720273193 * 3 / 2 + ) def test_decay_photon_energy(): # Set chain file for testing - openmc.config['chain_file'] = Path(__file__).parents[1] / 'chain_simple.xml' + openmc.config["chain_file"] = Path(__file__).parents[1] / "chain_simple.xml" # Material representing single atom of I135 and Cs135 m = openmc.Material() - m.add_nuclide('I135', 1.0e-24) - m.add_nuclide('Cs135', 1.0e-24) + m.add_nuclide("I135", 1.0e-24) + m.add_nuclide("Cs135", 1.0e-24) m.volume = 1.0 # Get decay photon source and make sure it's the right type @@ -654,12 +704,12 @@ def test_decay_photon_energy(): # Make sure units/volume work as expected src_v2 = m.get_decay_photon_energy(volume=2.0) assert src.p * 2.0 == pytest.approx(src_v2.p) - src_per_cm3 = m.get_decay_photon_energy(units='Bq/cm3', volume=100.0) + src_per_cm3 = m.get_decay_photon_energy(units="Bq/cm3", volume=100.0) assert (src.p == src_per_cm3.p).all() # If we add Xe135 (which has a tabular distribution), the photon source # should be a mixture distribution - m.add_nuclide('Xe135', 1.0e-24) + m.add_nuclide("Xe135", 1.0e-24) src = m.get_decay_photon_energy() assert isinstance(src, openmc.stats.Mixture) @@ -668,19 +718,19 @@ def test_decay_photon_energy(): def intensity(src): return src.integral() if src is not None else 0.0 - assert src.integral() == pytest.approx(sum( - intensity(decay_photon_energy(nuc)) for nuc in m.get_nuclides() - ), rel=1e-3) + assert src.integral() == pytest.approx( + sum(intensity(decay_photon_energy(nuc)) for nuc in m.get_nuclides()), rel=1e-3 + ) # When the clipping threshold is zero, the intensities should match exactly src = m.get_decay_photon_energy(0.0) - assert src.integral() == pytest.approx(sum( - intensity(decay_photon_energy(nuc)) for nuc in m.get_nuclides() - )) + assert src.integral() == pytest.approx( + sum(intensity(decay_photon_energy(nuc)) for nuc in m.get_nuclides()) + ) # A material with no unstable nuclides should have no decay photon source stable = openmc.Material() - stable.add_nuclide('Gd156', 1.0) + stable.add_nuclide("Gd156", 1.0) stable.volume = 1.0 assert stable.get_decay_photon_energy() is None @@ -689,11 +739,11 @@ def test_avoid_subnormal(run_in_tmpdir): # Write a materials.xml with a material that has a nuclide density that is # represented as a subnormal floating point value mat = openmc.Material() - mat.add_nuclide('H1', 1.0) - mat.add_nuclide('H2', 1.0e-315) + mat.add_nuclide("H1", 1.0) + mat.add_nuclide("H2", 1.0e-315) mats = openmc.Materials([mat]) mats.export_to_xml() # When read back in, the density should be zero mats = openmc.Materials.from_xml() - assert mats[0].get_nuclide_atom_densities()['H2'] == 0.0 + assert mats[0].get_nuclide_atom_densities()["H2"] == 0.0 diff --git a/tests/unit_tests/test_mesh.py b/tests/unit_tests/test_mesh.py index ed08be816f5..4f0918a2e8f 100644 --- a/tests/unit_tests/test_mesh.py +++ b/tests/unit_tests/test_mesh.py @@ -5,7 +5,7 @@ import openmc -@pytest.mark.parametrize("val_left,val_right", [(0, 0), (-1., -1.), (2.0, 2)]) +@pytest.mark.parametrize("val_left,val_right", [(0, 0), (-1.0, -1.0), (2.0, 2)]) def test_raises_error_when_flat(val_left, val_right): """Checks that an error is raised when a mesh is flat""" mesh = openmc.RegularMesh() @@ -44,27 +44,25 @@ def test_regular_mesh_bounding_box(): mesh.upper_right = [2, 3, 5] bb = mesh.bounding_box assert isinstance(bb, openmc.BoundingBox) - np.testing.assert_array_equal(bb.lower_left, (-2, -3 ,-5)) + np.testing.assert_array_equal(bb.lower_left, (-2, -3, -5)) np.testing.assert_array_equal(bb.upper_right, (2, 3, 5)) def test_rectilinear_mesh_bounding_box(): mesh = openmc.RectilinearMesh() - mesh.x_grid = [0., 1., 5., 10.] - mesh.y_grid = [-10., -5., 0.] - mesh.z_grid = [-100., 0., 100.] + mesh.x_grid = [0.0, 1.0, 5.0, 10.0] + mesh.y_grid = [-10.0, -5.0, 0.0] + mesh.z_grid = [-100.0, 0.0, 100.0] bb = mesh.bounding_box assert isinstance(bb, openmc.BoundingBox) - np.testing.assert_array_equal(bb.lower_left, (0., -10. ,-100.)) - np.testing.assert_array_equal(bb.upper_right, (10., 0., 100.)) + np.testing.assert_array_equal(bb.lower_left, (0.0, -10.0, -100.0)) + np.testing.assert_array_equal(bb.upper_right, (10.0, 0.0, 100.0)) def test_cylindrical_mesh_bounding_box(): # test with mesh at origin (0, 0, 0) mesh = openmc.CylindricalMesh( - r_grid=[0.1, 0.2, 0.5, 1.], - z_grid=[0.1, 0.2, 0.4, 0.6, 1.], - origin=(0, 0, 0) + r_grid=[0.1, 0.2, 0.5, 1.0], z_grid=[0.1, 0.2, 0.4, 0.6, 1.0], origin=(0, 0, 0) ) np.testing.assert_array_equal(mesh.upper_right, (1, 1, 1)) np.testing.assert_array_equal(mesh.lower_left, (-1, -1, 0.1)) @@ -90,7 +88,7 @@ def test_cylindrical_mesh_bounding_box(): def test_spherical_mesh_bounding_box(): # test with mesh at origin (0, 0, 0) - mesh = openmc.SphericalMesh([0.1, 0.2, 0.5, 1.], origin=(0., 0., 0.)) + mesh = openmc.SphericalMesh([0.1, 0.2, 0.5, 1.0], origin=(0.0, 0.0, 0.0)) np.testing.assert_array_equal(mesh.upper_right, (1, 1, 1)) np.testing.assert_array_equal(mesh.lower_left, (-1, -1, -1)) bb = mesh.bounding_box @@ -114,23 +112,20 @@ def test_SphericalMesh_initiation(): assert (mesh.origin == np.array([0, 0, 0])).all() assert (mesh.r_grid == np.array([0, 10])).all() assert (mesh.theta_grid == np.array([0, pi])).all() - assert (mesh.phi_grid == np.array([0, 2*pi])).all() + assert (mesh.phi_grid == np.array([0, 2 * pi])).all() # test setting on creation mesh = openmc.SphericalMesh( - origin=(1, 2, 3), - r_grid=(0, 2), - theta_grid=(1, 3), - phi_grid=(2, 4) + origin=(1, 2, 3), r_grid=(0, 2), theta_grid=(1, 3), phi_grid=(2, 4) ) assert (mesh.origin == np.array([1, 2, 3])).all() - assert (mesh.r_grid == np.array([0., 2.])).all() + assert (mesh.r_grid == np.array([0.0, 2.0])).all() assert (mesh.theta_grid == np.array([1, 3])).all() assert (mesh.phi_grid == np.array([2, 4])).all() # test attribute changing mesh.r_grid = (0, 11) - assert (mesh.r_grid == np.array([0., 11.])).all() + assert (mesh.r_grid == np.array([0.0, 11.0])).all() # test invalid r_grid values with pytest.raises(ValueError): @@ -155,7 +150,7 @@ def test_SphericalMesh_initiation(): # waffles and pancakes are unfortunately not valid radii with pytest.raises(TypeError): - openmc.SphericalMesh(('šŸ§‡', 'šŸ„ž')) + openmc.SphericalMesh(("šŸ§‡", "šŸ„ž")) def test_CylindricalMesh_initiation(): @@ -163,30 +158,27 @@ def test_CylindricalMesh_initiation(): mesh = openmc.CylindricalMesh(r_grid=(0, 10), z_grid=(0, 10)) assert (mesh.origin == np.array([0, 0, 0])).all() assert (mesh.r_grid == np.array([0, 10])).all() - assert (mesh.phi_grid == np.array([0, 2*pi])).all() + assert (mesh.phi_grid == np.array([0, 2 * pi])).all() assert (mesh.z_grid == np.array([0, 10])).all() # test setting on creation mesh = openmc.CylindricalMesh( - origin=(1, 2, 3), - r_grid=(0, 2), - z_grid=(1, 3), - phi_grid=(2, 4) + origin=(1, 2, 3), r_grid=(0, 2), z_grid=(1, 3), phi_grid=(2, 4) ) assert (mesh.origin == np.array([1, 2, 3])).all() - assert (mesh.r_grid == np.array([0., 2.])).all() + assert (mesh.r_grid == np.array([0.0, 2.0])).all() assert (mesh.z_grid == np.array([1, 3])).all() assert (mesh.phi_grid == np.array([2, 4])).all() # test attribute changing - mesh.r_grid = (0., 10.) - assert (mesh.r_grid == np.array([0, 10.])).all() - mesh.z_grid = (0., 4.) - assert (mesh.z_grid == np.array([0, 4.])).all() + mesh.r_grid = (0.0, 10.0) + assert (mesh.r_grid == np.array([0, 10.0])).all() + mesh.z_grid = (0.0, 4.0) + assert (mesh.z_grid == np.array([0, 4.0])).all() # waffles and pancakes are unfortunately not valid radii with pytest.raises(TypeError): - openmc.SphericalMesh(('šŸ§‡', 'šŸ„ž')) + openmc.SphericalMesh(("šŸ§‡", "šŸ„ž")) def test_invalid_cylindrical_mesh_errors(): @@ -206,9 +198,7 @@ def test_invalid_cylindrical_mesh_errors(): with pytest.raises(ValueError): openmc.CylindricalMesh( - r_grid=[0, 1, 2], - phi_grid=[0, 2*pi + 0.1], - z_grid=[0, 10] + r_grid=[0, 1, 2], phi_grid=[0, 2 * pi + 0.1], z_grid=[0, 10] ) with pytest.raises(ValueError): @@ -228,17 +218,17 @@ def test_invalid_cylindrical_mesh_errors(): def test_centroids(): # regular mesh mesh = openmc.RegularMesh() - mesh.lower_left = (1., 2., 3.) - mesh.upper_right = (11., 12., 13.) + mesh.lower_left = (1.0, 2.0, 3.0) + mesh.upper_right = (11.0, 12.0, 13.0) mesh.dimension = (1, 1, 1) - np.testing.assert_array_almost_equal(mesh.centroids[0, 0, 0], [6., 7., 8.]) + np.testing.assert_array_almost_equal(mesh.centroids[0, 0, 0], [6.0, 7.0, 8.0]) # rectilinear mesh mesh = openmc.RectilinearMesh() - mesh.x_grid = [1., 11.] - mesh.y_grid = [2., 12.] - mesh.z_grid = [3., 13.] - np.testing.assert_array_almost_equal(mesh.centroids[0, 0, 0], [6., 7., 8.]) + mesh.x_grid = [1.0, 11.0] + mesh.y_grid = [2.0, 12.0] + mesh.z_grid = [3.0, 13.0] + np.testing.assert_array_almost_equal(mesh.centroids[0, 0, 0], [6.0, 7.0, 8.0]) # cylindrical mesh mesh = openmc.CylindricalMesh(r_grid=(0, 10), z_grid=(0, 10), phi_grid=(0, np.pi)) @@ -248,26 +238,32 @@ def test_centroids(): np.testing.assert_array_almost_equal(mesh.centroids[0, 0, 0], [5.0, 5.0, -5.0]) # spherical mesh, single element xyz-positive octant - mesh = openmc.SphericalMesh(r_grid=[0, 10], theta_grid=[0, 0.5*np.pi], phi_grid=[0, np.pi]) - x = 5.*np.cos(0.5*np.pi)*np.sin(0.25*np.pi) - y = 5.*np.sin(0.5*np.pi)*np.sin(0.25*np.pi) - z = 5.*np.sin(0.25*np.pi) + mesh = openmc.SphericalMesh( + r_grid=[0, 10], theta_grid=[0, 0.5 * np.pi], phi_grid=[0, np.pi] + ) + x = 5.0 * np.cos(0.5 * np.pi) * np.sin(0.25 * np.pi) + y = 5.0 * np.sin(0.5 * np.pi) * np.sin(0.25 * np.pi) + z = 5.0 * np.sin(0.25 * np.pi) np.testing.assert_array_almost_equal(mesh.centroids[0, 0, 0], [x, y, z]) mesh.origin = (-5.0, -5.0, 5.0) - np.testing.assert_array_almost_equal(mesh.centroids[0, 0, 0], [x-5.0, y-5.0, z+5.0]) + np.testing.assert_array_almost_equal( + mesh.centroids[0, 0, 0], [x - 5.0, y - 5.0, z + 5.0] + ) -@pytest.mark.parametrize('mesh_type', ('regular', 'rectilinear', 'cylindrical', 'spherical')) +@pytest.mark.parametrize( + "mesh_type", ("regular", "rectilinear", "cylindrical", "spherical") +) def test_mesh_vertices(mesh_type): ijk = (2, 3, 2) # create a new mesh object - if mesh_type == 'regular': + if mesh_type == "regular": mesh = openmc.RegularMesh() - ll = np.asarray([0.]*3) - width = np.asarray([0.5]*3) + ll = np.asarray([0.0] * 3) + width = np.asarray([0.5] * 3) mesh.lower_left = ll mesh.width = width mesh.dimension = (5, 7, 9) @@ -278,31 +274,33 @@ def test_mesh_vertices(mesh_type): np.testing.assert_equal(mesh.vertices[ijk], exp_i_j_k) # shift the mesh using the llc - shift = np.asarray((3.0, 6.0, 10.0)) + shift = np.asarray((3.0, 6.0, 10.0)) mesh.lower_left += shift - np.testing.assert_equal(mesh.vertices[ijk], exp_i_j_k+shift) - elif mesh_type == 'rectilinear': + np.testing.assert_equal(mesh.vertices[ijk], exp_i_j_k + shift) + elif mesh_type == "rectilinear": mesh = openmc.RectilinearMesh() w = np.asarray([0.5] * 3) - ll = np.asarray([0.]*3) + ll = np.asarray([0.0] * 3) dims = (5, 7, 9) - mesh.x_grid = np.linspace(ll[0], w[0]*dims[0], dims[0]) - mesh.y_grid = np.linspace(ll[1], w[1]*dims[1], dims[1]) - mesh.z_grid = np.linspace(ll[2], w[2]*dims[2], dims[2]) + mesh.x_grid = np.linspace(ll[0], w[0] * dims[0], dims[0]) + mesh.y_grid = np.linspace(ll[1], w[1] * dims[1], dims[1]) + mesh.z_grid = np.linspace(ll[2], w[2] * dims[2], dims[2]) exp_vert = np.asarray((mesh.x_grid[2], mesh.y_grid[3], mesh.z_grid[2])) np.testing.assert_equal(mesh.vertices[ijk], exp_vert) - elif mesh_type == 'cylindrical': + elif mesh_type == "cylindrical": r_grid = np.linspace(0, 5, 10) z_grid = np.linspace(-10, 10, 20) - phi_grid = np.linspace(0, 2*np.pi, 8) + phi_grid = np.linspace(0, 2 * np.pi, 8) mesh = openmc.CylindricalMesh(r_grid=r_grid, z_grid=z_grid, phi_grid=phi_grid) exp_vert = np.asarray((mesh.r_grid[2], mesh.phi_grid[3], mesh.z_grid[2])) np.testing.assert_equal(mesh.vertices_cylindrical[ijk], exp_vert) - elif mesh_type == 'spherical': + elif mesh_type == "spherical": r_grid = np.linspace(0, 13, 14) theta_grid = np.linspace(0, np.pi, 11) - phi_grid = np.linspace(0, 2*np.pi, 7) - mesh = openmc.SphericalMesh(r_grid=r_grid, theta_grid=theta_grid, phi_grid=phi_grid) + phi_grid = np.linspace(0, 2 * np.pi, 7) + mesh = openmc.SphericalMesh( + r_grid=r_grid, theta_grid=theta_grid, phi_grid=phi_grid + ) exp_vert = np.asarray((mesh.r_grid[2], mesh.theta_grid[3], mesh.phi_grid[2])) np.testing.assert_equal(mesh.vertices_spherical[ijk], exp_vert) @@ -336,9 +334,21 @@ def test_CylindricalMesh_get_indices_at_coords(): ) assert mesh.get_indices_at_coords([1, 1, 1]) == (0, 0, 1) # first angle quadrant assert mesh.get_indices_at_coords([2, 2, 6]) == (0, 0, 2) # first angle quadrant - assert mesh.get_indices_at_coords([-2, 0.1, -1]) == (0, 1, 0) # second angle quadrant - assert mesh.get_indices_at_coords([-2, -0.1, -1]) == (0, 2, 0) # third angle quadrant - assert mesh.get_indices_at_coords([2, -0.9, -1]) == (0, 3, 0) # forth angle quadrant + assert mesh.get_indices_at_coords([-2, 0.1, -1]) == ( + 0, + 1, + 0, + ) # second angle quadrant + assert mesh.get_indices_at_coords([-2, -0.1, -1]) == ( + 0, + 2, + 0, + ) # third angle quadrant + assert mesh.get_indices_at_coords([2, -0.9, -1]) == ( + 0, + 3, + 0, + ) # forth angle quadrant with pytest.raises(ValueError): assert mesh.get_indices_at_coords([2, -0.1, 1]) # outside of phi range @@ -350,22 +360,42 @@ def test_CylindricalMesh_get_indices_at_coords(): z_grid=(-5, 0, 5, 10), origin=(100, 200, 300), ) - assert mesh.get_indices_at_coords([101, 201, 301]) == (0, 0, 1) # first angle quadrant - assert mesh.get_indices_at_coords([102, 202, 306]) == (0, 0, 2) # first angle quadrant - assert mesh.get_indices_at_coords([98, 200.1, 299]) == (0, 1, 0) # second angle quadrant - assert mesh.get_indices_at_coords([98, 199.9, 299]) == (0, 2, 0) # third angle quadrant - assert mesh.get_indices_at_coords([102, 199.1, 299]) == (0, 3, 0) # forth angle quadrant + assert mesh.get_indices_at_coords([101, 201, 301]) == ( + 0, + 0, + 1, + ) # first angle quadrant + assert mesh.get_indices_at_coords([102, 202, 306]) == ( + 0, + 0, + 2, + ) # first angle quadrant + assert mesh.get_indices_at_coords([98, 200.1, 299]) == ( + 0, + 1, + 0, + ) # second angle quadrant + assert mesh.get_indices_at_coords([98, 199.9, 299]) == ( + 0, + 2, + 0, + ) # third angle quadrant + assert mesh.get_indices_at_coords([102, 199.1, 299]) == ( + 0, + 3, + 0, + ) # forth angle quadrant def test_umesh_roundtrip(run_in_tmpdir, request): - umesh = openmc.UnstructuredMesh(request.path.parent / 'test_mesh_tets.e', 'moab') + umesh = openmc.UnstructuredMesh(request.path.parent / "test_mesh_tets.e", "moab") umesh.output = True # create a tally using this mesh mf = openmc.MeshFilter(umesh) tally = openmc.Tally() tally.filters = [mf] - tally.scores = ['flux'] + tally.scores = ["flux"] tallies = openmc.Tallies([tally]) tallies.export_to_xml() @@ -381,16 +411,16 @@ def test_mesh_get_homogenized_materials(): """Test the get_homogenized_materials method""" # Simple model with 1 cm of Fe56 next to 1 cm of H1 fe = openmc.Material() - fe.add_nuclide('Fe56', 1.0) - fe.set_density('g/cm3', 5.0) + fe.add_nuclide("Fe56", 1.0) + fe.set_density("g/cm3", 5.0) h = openmc.Material() - h.add_nuclide('H1', 1.0) - h.set_density('g/cm3', 1.0) + h.add_nuclide("H1", 1.0) + h.set_density("g/cm3", 1.0) - x0 = openmc.XPlane(-1.0, boundary_type='vacuum') + x0 = openmc.XPlane(-1.0, boundary_type="vacuum") x1 = openmc.XPlane(0.0) x2 = openmc.XPlane(1.0) - x3 = openmc.XPlane(2.0, boundary_type='vacuum') + x3 = openmc.XPlane(2.0, boundary_type="vacuum") cell1 = openmc.Cell(fill=fe, region=+x0 & -x1) cell2 = openmc.Cell(fill=h, region=+x1 & -x2) cell_empty = openmc.Cell(region=+x2 & -x3) @@ -399,32 +429,33 @@ def test_mesh_get_homogenized_materials(): model.settings.batches = 10 mesh = openmc.RegularMesh() - mesh.lower_left = (-1., -1., -1.) - mesh.upper_right = (1., 1., 1.) + mesh.lower_left = (-1.0, -1.0, -1.0) + mesh.upper_right = (1.0, 1.0, 1.0) mesh.dimension = (3, 1, 1) m1, m2, m3 = mesh.get_homogenized_materials(model, n_samples=1_000_000) # Left mesh element should be only Fe56 - assert m1.get_mass_density('Fe56') == pytest.approx(5.0) + assert m1.get_mass_density("Fe56") == pytest.approx(5.0) # Middle mesh element should be 50% Fe56 and 50% H1 - assert m2.get_mass_density('Fe56') == pytest.approx(2.5, rel=1e-2) - assert m2.get_mass_density('H1') == pytest.approx(0.5, rel=1e-2) + assert m2.get_mass_density("Fe56") == pytest.approx(2.5, rel=1e-2) + assert m2.get_mass_density("H1") == pytest.approx(0.5, rel=1e-2) # Right mesh element should be only H1 - assert m3.get_mass_density('H1') == pytest.approx(1.0) + assert m3.get_mass_density("H1") == pytest.approx(1.0) mesh_void = openmc.RegularMesh() - mesh_void.lower_left = (0.5, 0.5, -1.) - mesh_void.upper_right = (1.5, 1.5, 1.) + mesh_void.lower_left = (0.5, 0.5, -1.0) + mesh_void.upper_right = (1.5, 1.5, 1.0) mesh_void.dimension = (1, 1, 1) - m4, = mesh_void.get_homogenized_materials(model, n_samples=1_000_000) + (m4,) = mesh_void.get_homogenized_materials(model, n_samples=1_000_000) # Mesh element that overlaps void should have half density - assert m4.get_mass_density('H1') == pytest.approx(0.5, rel=1e-2) + assert m4.get_mass_density("H1") == pytest.approx(0.5, rel=1e-2) # If not including void, density of homogenized material should be same as # original material - m5, = mesh_void.get_homogenized_materials( - model, n_samples=1000, include_void=False) - assert m5.get_mass_density('H1') == pytest.approx(1.0) + (m5,) = mesh_void.get_homogenized_materials( + model, n_samples=1000, include_void=False + ) + assert m5.get_mass_density("H1") == pytest.approx(1.0) diff --git a/tests/unit_tests/test_mesh_from_domain.py b/tests/unit_tests/test_mesh_from_domain.py index 0cbe413e86a..0c1bea944d6 100644 --- a/tests/unit_tests/test_mesh_from_domain.py +++ b/tests/unit_tests/test_mesh_from_domain.py @@ -28,10 +28,12 @@ def test_cylindrical_mesh_from_cell(): assert isinstance(mesh, openmc.CylindricalMesh) assert np.array_equal(mesh.dimension, (2, 4, 3)) - assert np.array_equal(mesh.r_grid, [0., 25., 50.]) - assert np.array_equal(mesh.phi_grid, [0., 0.5*np.pi, np.pi, 1.5*np.pi, 2.*np.pi]) - assert np.array_equal(mesh.z_grid, [0., 10., 20., 30.]) - assert np.array_equal(mesh.origin, [0., 0., 10.]) + assert np.array_equal(mesh.r_grid, [0.0, 25.0, 50.0]) + assert np.array_equal( + mesh.phi_grid, [0.0, 0.5 * np.pi, np.pi, 1.5 * np.pi, 2.0 * np.pi] + ) + assert np.array_equal(mesh.z_grid, [0.0, 10.0, 20.0, 30.0]) + assert np.array_equal(mesh.origin, [0.0, 0.0, 10.0]) # Cell is not centralized on Z or X axis cy_surface = openmc.ZCylinder(r=50, x0=100) @@ -40,8 +42,8 @@ def test_cylindrical_mesh_from_cell(): assert isinstance(mesh, openmc.CylindricalMesh) assert np.array_equal(mesh.dimension, (1, 1, 1)) - assert np.array_equal(mesh.r_grid, [0., 150.]) - assert np.array_equal(mesh.origin, [100., 0., 10.]) + assert np.array_equal(mesh.r_grid, [0.0, 150.0]) + assert np.array_equal(mesh.origin, [100.0, 0.0, 10.0]) # Cell is not centralized on Z, X or Y axis cy_surface = openmc.ZCylinder(r=50, x0=100, y0=170) @@ -49,8 +51,8 @@ def test_cylindrical_mesh_from_cell(): mesh = openmc.CylindricalMesh.from_domain(domain=cell, dimension=[1, 1, 1]) assert isinstance(mesh, openmc.CylindricalMesh) - assert np.array_equal(mesh.r_grid, [0., 220.]) - assert np.array_equal(mesh.origin, [100., 170., 10.]) + assert np.array_equal(mesh.r_grid, [0.0, 220.0]) + assert np.array_equal(mesh.origin, [100.0, 170.0, 10.0]) def test_reg_mesh_from_region(): @@ -74,17 +76,15 @@ def test_cylindrical_mesh_from_region(): z_surface_2 = openmc.ZPlane(z0=-30) cell = openmc.Cell(region=-cy_surface & -z_surface_1 & +z_surface_2) mesh = openmc.CylindricalMesh.from_domain( - domain=cell, - dimension=(6, 2, 3), - phi_grid_bounds=(0., np.pi) + domain=cell, dimension=(6, 2, 3), phi_grid_bounds=(0.0, np.pi) ) assert isinstance(mesh, openmc.CylindricalMesh) assert np.array_equal(mesh.dimension, (6, 2, 3)) - assert np.array_equal(mesh.r_grid, [0., 1., 2., 3., 4., 5., 6.]) - assert np.array_equal(mesh.phi_grid, [0., 0.5*np.pi, np.pi]) - assert np.array_equal(mesh.z_grid, [0.0, 20., 40., 60]) - assert np.array_equal(mesh.origin, (0.0, 0.0, -30.)) + assert np.array_equal(mesh.r_grid, [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0]) + assert np.array_equal(mesh.phi_grid, [0.0, 0.5 * np.pi, np.pi]) + assert np.array_equal(mesh.z_grid, [0.0, 20.0, 40.0, 60]) + assert np.array_equal(mesh.origin, (0.0, 0.0, -30.0)) def test_reg_mesh_from_universe(): diff --git a/tests/unit_tests/test_mesh_from_lattice.py b/tests/unit_tests/test_mesh_from_lattice.py index 0a01387cded..a8fa8b6406b 100644 --- a/tests/unit_tests/test_mesh_from_lattice.py +++ b/tests/unit_tests/test_mesh_from_lattice.py @@ -3,7 +3,7 @@ import pytest -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def pincell1(uo2, water): cyl = openmc.ZCylinder(r=0.35) fuel = openmc.Cell(fill=uo2, region=-cyl) @@ -15,7 +15,7 @@ def pincell1(uo2, water): return univ -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def pincell2(uo2, water): cyl = openmc.ZCylinder(r=0.4) fuel = openmc.Cell(fill=uo2, region=-cyl) @@ -27,15 +27,15 @@ def pincell2(uo2, water): return univ -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def zr(): zr = openmc.Material() - zr.add_element('Zr', 1.0) - zr.set_density('g/cm3', 1.0) + zr.add_element("Zr", 1.0) + zr.set_density("g/cm3", 1.0) return zr -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def rlat2(pincell1, pincell2, uo2, water, zr): """2D Rectangular lattice for testing.""" all_zr = openmc.Cell(fill=zr) @@ -43,26 +43,22 @@ def rlat2(pincell1, pincell2, uo2, water, zr): n = 3 u1, u2 = pincell1, pincell2 lattice = openmc.RectLattice() - lattice.lower_left = (-pitch*n/2, -pitch*n/2) + lattice.lower_left = (-pitch * n / 2, -pitch * n / 2) lattice.pitch = (pitch, pitch) lattice.outer = openmc.Universe(cells=[all_zr]) - lattice.universes = [ - [u1, u2, u1], - [u2, u1, u2], - [u2, u1, u1] - ] + lattice.universes = [[u1, u2, u1], [u2, u1, u2], [u2, u1, u1]] return lattice -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def rlat3(pincell1, pincell2, uo2, water, zr): """3D Rectangular lattice for testing.""" # Create another universe for top layer hydrogen = openmc.Material() - hydrogen.add_element('H', 1.0) - hydrogen.set_density('g/cm3', 0.09) + hydrogen.add_element("H", 1.0) + hydrogen.set_density("g/cm3", 0.09) h_cell = openmc.Cell(fill=hydrogen) u3 = openmc.Universe(cells=[h_cell]) @@ -71,16 +67,12 @@ def rlat3(pincell1, pincell2, uo2, water, zr): n = 3 u1, u2 = pincell1, pincell2 lattice = openmc.RectLattice() - lattice.lower_left = (-pitch*n/2, -pitch*n/2, -10.0) + lattice.lower_left = (-pitch * n / 2, -pitch * n / 2, -10.0) lattice.pitch = (pitch, pitch, 10.0) lattice.outer = openmc.Universe(cells=[all_zr]) lattice.universes = [ - [[u1, u2, u1], - [u2, u1, u2], - [u2, u1, u1]], - [[u3, u1, u2], - [u1, u3, u2], - [u2, u1, u1]] + [[u1, u2, u1], [u2, u1, u2], [u2, u1, u1]], + [[u3, u1, u2], [u1, u3, u2], [u2, u1, u1]], ] return lattice @@ -88,7 +80,7 @@ def rlat3(pincell1, pincell2, uo2, water, zr): def test_mesh2d(rlat2): shape = np.array(rlat2.shape) - width = shape*rlat2.pitch + width = shape * rlat2.pitch mesh1 = openmc.RegularMesh.from_rect_lattice(rlat2) assert np.array_equal(mesh1.dimension, (3, 3)) @@ -103,7 +95,7 @@ def test_mesh2d(rlat2): def test_mesh3d(rlat3): shape = np.array(rlat3.shape) - width = shape*rlat3.pitch + width = shape * rlat3.pitch mesh1 = openmc.RegularMesh.from_rect_lattice(rlat3) assert np.array_equal(mesh1.dimension, (3, 3, 2)) diff --git a/tests/unit_tests/test_model.py b/tests/unit_tests/test_model.py index 4b567c56d62..2947a806812 100644 --- a/tests/unit_tests/test_model.py +++ b/tests/unit_tests/test_model.py @@ -9,40 +9,38 @@ import openmc.lib -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def pin_model_attributes(): - uo2 = openmc.Material(material_id=1, name='UO2') - uo2.set_density('g/cm3', 10.29769) - uo2.add_element('U', 1., enrichment=2.4) - uo2.add_element('O', 2.) + uo2 = openmc.Material(material_id=1, name="UO2") + uo2.set_density("g/cm3", 10.29769) + uo2.add_element("U", 1.0, enrichment=2.4) + uo2.add_element("O", 2.0) uo2.depletable = True - zirc = openmc.Material(material_id=2, name='Zirc') - zirc.set_density('g/cm3', 6.55) - zirc.add_element('Zr', 1.) + zirc = openmc.Material(material_id=2, name="Zirc") + zirc.set_density("g/cm3", 6.55) + zirc.add_element("Zr", 1.0) zirc.depletable = False - borated_water = openmc.Material(material_id=3, name='Borated water') - borated_water.set_density('g/cm3', 0.740582) - borated_water.add_element('B', 4.0e-5) - borated_water.add_element('H', 5.0e-2) - borated_water.add_element('O', 2.4e-2) - borated_water.add_s_alpha_beta('c_H_in_H2O') + borated_water = openmc.Material(material_id=3, name="Borated water") + borated_water.set_density("g/cm3", 0.740582) + borated_water.add_element("B", 4.0e-5) + borated_water.add_element("H", 5.0e-2) + borated_water.add_element("O", 2.4e-2) + borated_water.add_s_alpha_beta("c_H_in_H2O") borated_water.depletable = False mats = openmc.Materials([uo2, zirc, borated_water]) pitch = 1.25984 - fuel_or = openmc.ZCylinder(r=0.39218, name='Fuel OR') - clad_or = openmc.ZCylinder(r=0.45720, name='Clad OR') - box = openmc.model.RectangularPrism(pitch, pitch, - boundary_type='reflective') + fuel_or = openmc.ZCylinder(r=0.39218, name="Fuel OR") + clad_or = openmc.ZCylinder(r=0.45720, name="Clad OR") + box = openmc.model.RectangularPrism(pitch, pitch, boundary_type="reflective") # Define cells - fuel_inf_cell = openmc.Cell(cell_id=1, name='inf fuel', fill=uo2) + fuel_inf_cell = openmc.Cell(cell_id=1, name="inf fuel", fill=uo2) fuel_inf_univ = openmc.Universe(universe_id=1, cells=[fuel_inf_cell]) - fuel = openmc.Cell(cell_id=2, name='fuel', - fill=fuel_inf_univ, region=-fuel_or) + fuel = openmc.Cell(cell_id=2, name="fuel", fill=fuel_inf_univ, region=-fuel_or) clad = openmc.Cell(cell_id=3, fill=zirc, region=+fuel_or & -clad_or) water = openmc.Cell(cell_id=4, fill=borated_water, region=+clad_or & -box) @@ -59,34 +57,35 @@ def pin_model_attributes(): bounds = [-0.62992, -0.62992, -1, 0.62992, 0.62992, 1] uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:]) settings.source = openmc.IndependentSource( - space=uniform_dist, constraints={'fissionable': True}) + space=uniform_dist, constraints={"fissionable": True} + ) entropy_mesh = openmc.RegularMesh() - entropy_mesh.lower_left = [-0.39218, -0.39218, -1.e50] - entropy_mesh.upper_right = [0.39218, 0.39218, 1.e50] + entropy_mesh.lower_left = [-0.39218, -0.39218, -1.0e50] + entropy_mesh.upper_right = [0.39218, 0.39218, 1.0e50] entropy_mesh.dimension = [10, 10, 1] settings.entropy_mesh = entropy_mesh tals = openmc.Tallies() - tal = openmc.Tally(tally_id=1, name='test') + tal = openmc.Tally(tally_id=1, name="test") tal.filters = [openmc.MaterialFilter(bins=[uo2])] - tal.scores = ['flux', 'fission'] + tal.scores = ["flux", "fission"] tals.append(tal) plot1 = openmc.Plot(plot_id=1) - plot1.origin = (0., 0., 0.) + plot1.origin = (0.0, 0.0, 0.0) plot1.width = (pitch, pitch) plot1.pixels = (300, 300) - plot1.color_by = 'material' - plot1.filename = 'test' + plot1.color_by = "material" + plot1.filename = "test" plot2 = openmc.Plot(plot_id=2) - plot2.origin = (0., 0., 0.) + plot2.origin = (0.0, 0.0, 0.0) plot2.width = (pitch, pitch) plot2.pixels = (300, 300) - plot2.color_by = 'cell' + plot2.color_by = "cell" plots = openmc.Plots((plot1, plot2)) - chain = './test_chain.xml' + chain = "./test_chain.xml" chain_file_xml = """ @@ -103,14 +102,13 @@ def pin_model_attributes(): """ - operator_kwargs = {'chain_file': chain} + operator_kwargs = {"chain_file": chain} return (mats, geom, settings, tals, plots, operator_kwargs, chain_file_xml) def test_init(run_in_tmpdir, pin_model_attributes, mpi_intracomm): - mats, geom, settings, tals, plots, _, _ = \ - pin_model_attributes + mats, geom, settings, tals, plots, _, _ = pin_model_attributes openmc.reset_auto_ids() # Check blank initialization of a model @@ -118,8 +116,9 @@ def test_init(run_in_tmpdir, pin_model_attributes, mpi_intracomm): assert test_model.geometry.root_universe is None assert len(test_model.materials) == 0 ref_settings = openmc.Settings() - assert sorted(test_model.settings.__dict__.keys()) == \ - sorted(ref_settings.__dict__.keys()) + assert sorted(test_model.settings.__dict__.keys()) == sorted( + ref_settings.__dict__.keys() + ) for ref_k, ref_v in ref_settings.__dict__.items(): assert test_model.settings.__dict__[ref_k] == ref_v assert len(test_model.tallies) == 0 @@ -141,18 +140,24 @@ def test_init(run_in_tmpdir, pin_model_attributes, mpi_intracomm): assert test_model.plots is plots assert test_model._materials_by_id == {1: mats[0], 2: mats[1], 3: mats[2]} assert test_model._materials_by_name == { - 'UO2': {mats[0]}, 'Zirc': {mats[1]}, 'Borated water': {mats[2]}} + "UO2": {mats[0]}, + "Zirc": {mats[1]}, + "Borated water": {mats[2]}, + } # The last cell is the one that contains the infinite fuel - assert test_model._cells_by_id == \ - {2: geom.root_universe.cells[2], 3: geom.root_universe.cells[3], - 4: geom.root_universe.cells[4], - 1: geom.root_universe.cells[2].fill.cells[1]} + assert test_model._cells_by_id == { + 2: geom.root_universe.cells[2], + 3: geom.root_universe.cells[3], + 4: geom.root_universe.cells[4], + 1: geom.root_universe.cells[2].fill.cells[1], + } # No cell name for 2 and 3, so we expect a blank name to be assigned to # cell 3 due to overwriting assert test_model._cells_by_name == { - 'fuel': {geom.root_universe.cells[2]}, - '': {geom.root_universe.cells[3], geom.root_universe.cells[4]}, - 'inf fuel': {geom.root_universe.cells[2].fill.cells[1]}} + "fuel": {geom.root_universe.cells[2]}, + "": {geom.root_universe.cells[3], geom.root_universe.cells[4]}, + "inf fuel": {geom.root_universe.cells[2].fill.cells[1]}, + } assert test_model.is_initialized is False # Finally test the parameter type checking by passing bad types and @@ -179,42 +184,51 @@ def test_from_xml(run_in_tmpdir, pin_model_attributes): # This from_xml method cannot load chain and fission_q test_model = openmc.Model.from_xml() - assert test_model.geometry.root_universe.cells.keys() == \ - geom.root_universe.cells.keys() - assert [c.fill.name for c in - test_model.geometry.root_universe.cells.values()] == \ - [c.fill.name for c in geom.root_universe.cells.values()] - assert [mat.name for mat in test_model.materials] == \ - [mat.name for mat in mats] + assert ( + test_model.geometry.root_universe.cells.keys() + == geom.root_universe.cells.keys() + ) + assert [c.fill.name for c in test_model.geometry.root_universe.cells.values()] == [ + c.fill.name for c in geom.root_universe.cells.values() + ] + assert [mat.name for mat in test_model.materials] == [mat.name for mat in mats] # We will assume the attributes of settings that are custom objects are # OK if the others are so we dotn need to implement explicit comparisons - no_test = ['_source', '_entropy_mesh'] - assert sorted(k for k in test_model.settings.__dict__.keys() - if k not in no_test) == \ - sorted(k for k in settings.__dict__.keys() if k not in no_test) + no_test = ["_source", "_entropy_mesh"] + assert sorted( + k for k in test_model.settings.__dict__.keys() if k not in no_test + ) == sorted(k for k in settings.__dict__.keys() if k not in no_test) keys = sorted(k for k in settings.__dict__.keys() if k not in no_test) for ref_k in keys: assert test_model.settings.__dict__[ref_k] == settings.__dict__[ref_k] assert len(test_model.tallies) == 1 assert len(test_model.plots) == 2 - assert test_model._materials_by_id == \ - {1: test_model.materials[0], 2: test_model.materials[1], - 3: test_model.materials[2]} + assert test_model._materials_by_id == { + 1: test_model.materials[0], + 2: test_model.materials[1], + 3: test_model.materials[2], + } assert test_model._materials_by_name == { - 'UO2': {test_model.materials[0]}, 'Zirc': {test_model.materials[1]}, - 'Borated water': {test_model.materials[2]}} + "UO2": {test_model.materials[0]}, + "Zirc": {test_model.materials[1]}, + "Borated water": {test_model.materials[2]}, + } assert test_model._cells_by_id == { 2: test_model.geometry.root_universe.cells[2], 3: test_model.geometry.root_universe.cells[3], 4: test_model.geometry.root_universe.cells[4], - 1: test_model.geometry.root_universe.cells[2].fill.cells[1]} + 1: test_model.geometry.root_universe.cells[2].fill.cells[1], + } # No cell name for 2 and 3, so we expect a blank name to be assigned to # cell 3 due to overwriting assert test_model._cells_by_name == { - 'fuel': {test_model.geometry.root_universe.cells[2]}, - '': {test_model.geometry.root_universe.cells[3], - test_model.geometry.root_universe.cells[4]}, - 'inf fuel': {test_model.geometry.root_universe.cells[2].fill.cells[1]}} + "fuel": {test_model.geometry.root_universe.cells[2]}, + "": { + test_model.geometry.root_universe.cells[3], + test_model.geometry.root_universe.cells[4], + }, + "inf fuel": {test_model.geometry.root_universe.cells[2].fill.cells[1]}, + } assert test_model.is_initialized is False @@ -228,9 +242,9 @@ def test_init_finalize_lib(run_in_tmpdir, pin_model_attributes, mpi_intracomm): assert openmc.lib.is_initialized is True assert test_model.is_initialized is True # Now make sure it actually is initialized by making a call to the lib - c_mat = openmc.lib.find_material((0.6, 0., 0.)) + c_mat = openmc.lib.find_material((0.6, 0.0, 0.0)) # This should be Borated water - assert c_mat.name == 'Borated water' + assert c_mat.name == "Borated water" assert c_mat.id == 3 # Ok, now lets test that we can clear the data and check that it is cleared @@ -244,7 +258,7 @@ def test_init_finalize_lib(run_in_tmpdir, pin_model_attributes, mpi_intracomm): def test_import_properties(run_in_tmpdir, mpi_intracomm): - """Test importing properties on the Model class """ + """Test importing properties on the Model class""" # Create PWR pin cell model and write XML files openmc.reset_auto_ids() @@ -254,7 +268,7 @@ def test_import_properties(run_in_tmpdir, mpi_intracomm): # Change fuel temperature and density and export properties cell = openmc.lib.cells[1] cell.set_temperature(600.0) - cell.fill.set_density(5.0, 'g/cm3') + cell.fill.set_density(5.0, "g/cm3") openmc.lib.export_properties(output=False) # Import properties to existing model @@ -266,8 +280,8 @@ def test_import_properties(run_in_tmpdir, mpi_intracomm): assert cell.temperature == [600.0] assert cell.fill.get_mass_density() == pytest.approx(5.0) # Now C - assert openmc.lib.cells[1].get_temperature() == 600. - assert openmc.lib.materials[1].get_density('g/cm3') == pytest.approx(5.0) + assert openmc.lib.cells[1].get_temperature() == 600.0 + assert openmc.lib.materials[1].get_density("g/cm3") == pytest.approx(5.0) # Clear the C API openmc.lib.finalize() @@ -277,9 +291,9 @@ def test_import_properties(run_in_tmpdir, mpi_intracomm): # Load model with properties and confirm temperature/density changed model_with_properties = openmc.Model.from_xml( - 'with_properties/geometry.xml', - 'with_properties/materials.xml', - 'with_properties/settings.xml' + "with_properties/geometry.xml", + "with_properties/materials.xml", + "with_properties/settings.xml", ) cell = model_with_properties.geometry.get_all_cells()[1] assert cell.temperature == [600.0] @@ -295,15 +309,15 @@ def test_run(run_in_tmpdir, pin_model_attributes, mpi_intracomm): sp_path = test_model.run(output=False) with openmc.StatePoint(sp_path) as sp: cli_keff = sp.keff - cli_flux = sp.get_tally(id=1).get_values(scores=['flux'])[0, 0, 0] - cli_fiss = sp.get_tally(id=1).get_values(scores=['fission'])[0, 0, 0] + cli_flux = sp.get_tally(id=1).get_values(scores=["flux"])[0, 0, 0] + cli_fiss = sp.get_tally(id=1).get_values(scores=["fission"])[0, 0, 0] test_model.init_lib(output=False, intracomm=mpi_intracomm) sp_path = test_model.run(output=False) with openmc.StatePoint(sp_path) as sp: lib_keff = sp.keff - lib_flux = sp.get_tally(id=1).get_values(scores=['flux'])[0, 0, 0] - lib_fiss = sp.get_tally(id=1).get_values(scores=['fission'])[0, 0, 0] + lib_flux = sp.get_tally(id=1).get_values(scores=["flux"])[0, 0, 0] + lib_fiss = sp.get_tally(id=1).get_values(scores=["fission"])[0, 0, 0] # and lets compare results assert lib_keff.n == pytest.approx(cli_keff.n, abs=1e-13) @@ -317,7 +331,7 @@ def test_run(run_in_tmpdir, pin_model_attributes, mpi_intracomm): with pytest.raises(ValueError): test_model.run(geometry_debug=True) with pytest.raises(ValueError): - test_model.run(restart_file='1.h5') + test_model.run(restart_file="1.h5") with pytest.raises(ValueError): test_model.run(tracks=True) @@ -338,7 +352,7 @@ def test_plots(run_in_tmpdir, pin_model_attributes, mpi_intracomm): test_model.plot_geometry(output=False) # Now look for the files - for fname in ('test.png', 'plot_2.png'): + for fname in ("test.png", "plot_2.png"): test_file = Path(fname) assert test_file.exists() test_file.unlink() @@ -372,75 +386,81 @@ def test_py_lib_attributes(run_in_tmpdir, pin_model_attributes, mpi_intracomm): test_model.rotate_cells([7200], (0, 0, 90)) with pytest.raises(openmc.exceptions.InvalidIDError): # Make sure it tells us we had a bad id - test_model.rotate_cells(['bad_name'], (0, 0, 90)) + test_model.rotate_cells(["bad_name"], (0, 0, 90)) # Now a good one - assert np.all(openmc.lib.cells[2].rotation == (0., 0., 0.)) + assert np.all(openmc.lib.cells[2].rotation == (0.0, 0.0, 0.0)) test_model.rotate_cells([2], (0, 0, 90)) - assert np.all(openmc.lib.cells[2].rotation == (0., 0., 90.)) + assert np.all(openmc.lib.cells[2].rotation == (0.0, 0.0, 90.0)) # And same thing by name - test_model.rotate_cells(['fuel'], (0, 0, 180)) + test_model.rotate_cells(["fuel"], (0, 0, 180)) # Now translate_cells. We dont need to re-check the TypeErrors/bad ids, # because the other functions use the same hidden method as rotate_cells - assert np.all(openmc.lib.cells[2].translation == (0., 0., 0.)) + assert np.all(openmc.lib.cells[2].translation == (0.0, 0.0, 0.0)) test_model.translate_cells([2], (0, 0, 10)) - assert np.all(openmc.lib.cells[2].translation == (0., 0., 10.)) + assert np.all(openmc.lib.cells[2].translation == (0.0, 0.0, 10.0)) # Now lets do the density updates. # Check initial conditions - assert openmc.lib.materials[1].get_density('atom/b-cm') == \ - pytest.approx(0.06891296988603757, abs=1e-13) + assert openmc.lib.materials[1].get_density("atom/b-cm") == pytest.approx( + 0.06891296988603757, abs=1e-13 + ) mat_a_dens = np.sum( - list(test_model.materials[0].get_nuclide_atom_densities().values())) + list(test_model.materials[0].get_nuclide_atom_densities().values()) + ) assert mat_a_dens == pytest.approx(0.06891296988603757, abs=1e-8) # Change the density - test_model.update_densities(['UO2'], 2.) - assert openmc.lib.materials[1].get_density('atom/b-cm') == \ - pytest.approx(2., abs=1e-13) + test_model.update_densities(["UO2"], 2.0) + assert openmc.lib.materials[1].get_density("atom/b-cm") == pytest.approx( + 2.0, abs=1e-13 + ) mat_a_dens = np.sum( - list(test_model.materials[0].get_nuclide_atom_densities().values())) - assert mat_a_dens == pytest.approx(2., abs=1e-8) + list(test_model.materials[0].get_nuclide_atom_densities().values()) + ) + assert mat_a_dens == pytest.approx(2.0, abs=1e-8) # Now lets do the cell temperature updates. # Check initial conditions - assert test_model._cells_by_id == \ - {2: geom.root_universe.cells[2], 3: geom.root_universe.cells[3], - 4: geom.root_universe.cells[4], - 1: geom.root_universe.cells[2].fill.cells[1]} - assert openmc.lib.cells[3].get_temperature() == \ - pytest.approx(293.6, abs=1e-13) + assert test_model._cells_by_id == { + 2: geom.root_universe.cells[2], + 3: geom.root_universe.cells[3], + 4: geom.root_universe.cells[4], + 1: geom.root_universe.cells[2].fill.cells[1], + } + assert openmc.lib.cells[3].get_temperature() == pytest.approx(293.6, abs=1e-13) assert test_model.geometry.root_universe.cells[3].temperature is None # Change the temperature - test_model.update_cell_temperatures([3], 600.) - assert openmc.lib.cells[3].get_temperature() == \ - pytest.approx(600., abs=1e-13) - assert test_model.geometry.root_universe.cells[3].temperature == \ - pytest.approx(600., abs=1e-13) + test_model.update_cell_temperatures([3], 600.0) + assert openmc.lib.cells[3].get_temperature() == pytest.approx(600.0, abs=1e-13) + assert test_model.geometry.root_universe.cells[3].temperature == pytest.approx( + 600.0, abs=1e-13 + ) # And finally material volume - assert openmc.lib.materials[1].volume == \ - pytest.approx(0.4831931368640985, abs=1e-13) + assert openmc.lib.materials[1].volume == pytest.approx( + 0.4831931368640985, abs=1e-13 + ) # The temperature on the material will be None because its just the default - assert test_model.materials[0].volume == \ - pytest.approx(0.4831931368640985, abs=1e-13) + assert test_model.materials[0].volume == pytest.approx( + 0.4831931368640985, abs=1e-13 + ) # Change the temperature - test_model.update_material_volumes(['UO2'], 2.) - assert openmc.lib.materials[1].volume == pytest.approx(2., abs=1e-13) - assert test_model.materials[0].volume == pytest.approx(2., abs=1e-13) + test_model.update_material_volumes(["UO2"], 2.0) + assert openmc.lib.materials[1].volume == pytest.approx(2.0, abs=1e-13) + assert test_model.materials[0].volume == pytest.approx(2.0, abs=1e-13) test_model.finalize_lib() def test_deplete(run_in_tmpdir, pin_model_attributes, mpi_intracomm): - mats, geom, settings, tals, plots, op_kwargs, chain_file_xml = \ - pin_model_attributes - with open('test_chain.xml', 'w') as f: + mats, geom, settings, tals, plots, op_kwargs, chain_file_xml = pin_model_attributes + with open("test_chain.xml", "w") as f: f.write(chain_file_xml) test_model = openmc.Model(geom, mats, settings, tals, plots) initial_mat = mats[0].clone() - initial_u = initial_mat.get_nuclide_atom_densities()['U235'] + initial_u = initial_mat.get_nuclide_atom_densities()["U235"] # Note that the chain file includes only U-235 fission to a stable Xe136 w/ # a yield of 100%. Thus all the U235 we lose becomes Xe136 @@ -448,21 +468,25 @@ def test_deplete(run_in_tmpdir, pin_model_attributes, mpi_intracomm): # In this test we first run without pre-initializing the shared library # data and then compare. Then we repeat with the C API already initialized # and make sure we get the same answer - test_model.deplete([1e6], 'predictor', final_step=False, - operator_kwargs=op_kwargs, - power=1., output=False) + test_model.deplete( + [1e6], + "predictor", + final_step=False, + operator_kwargs=op_kwargs, + power=1.0, + output=False, + ) # Get the new Xe136 and U235 atom densities - after_xe = mats[0].get_nuclide_atom_densities()['Xe136'] - after_u = mats[0].get_nuclide_atom_densities()['U235'] + after_xe = mats[0].get_nuclide_atom_densities()["Xe136"] + after_u = mats[0].get_nuclide_atom_densities()["U235"] assert after_xe + after_u == pytest.approx(initial_u, abs=1e-15) assert test_model.is_initialized is False # check the tally output def check_tally_output(): - with openmc.StatePoint('openmc_simulation_n0.h5') as sp: - flux = sp.get_tally(id=1).get_values(scores=['flux'])[0, 0, 0] - fission = sp.get_tally(id=1).get_values( - scores=['fission'])[0, 0, 0] + with openmc.StatePoint("openmc_simulation_n0.h5") as sp: + flux = sp.get_tally(id=1).get_values(scores=["flux"])[0, 0, 0] + fission = sp.get_tally(id=1).get_values(scores=["fission"])[0, 0, 0] # we're mainly just checking that the result was produced, # so a rough numerical comparison doesn't hurt to have. @@ -474,20 +498,25 @@ def check_tally_output(): # Reset the initial material densities mats[0].nuclides.clear() densities = initial_mat.get_nuclide_atom_densities() - tot_density = 0. + tot_density = 0.0 for nuc, density in densities.items(): mats[0].add_nuclide(nuc, density) tot_density += density - mats[0].set_density('atom/b-cm', tot_density) + mats[0].set_density("atom/b-cm", tot_density) # Now we can re-run with the pre-initialized API test_model.init_lib(output=False, intracomm=mpi_intracomm) - test_model.deplete([1e6], 'predictor', final_step=False, - operator_kwargs=op_kwargs, - power=1., output=False) + test_model.deplete( + [1e6], + "predictor", + final_step=False, + operator_kwargs=op_kwargs, + power=1.0, + output=False, + ) # Get the new Xe136 and U235 atom densities - after_lib_xe = mats[0].get_nuclide_atom_densities()['Xe136'] - after_lib_u = mats[0].get_nuclide_atom_densities()['U235'] + after_lib_xe = mats[0].get_nuclide_atom_densities()["Xe136"] + after_lib_u = mats[0].get_nuclide_atom_densities()["U235"] assert after_lib_xe + after_lib_u == pytest.approx(initial_u, abs=1e-15) assert test_model.is_initialized is True @@ -511,13 +540,18 @@ def test_calc_volumes(run_in_tmpdir, pin_model_attributes, mpi_intracomm): # Add a cell and mat volume calc material_vol_calc = openmc.VolumeCalculation( - [mats[2]], samples=1000, lower_left=(-.63, -.63, -100.), - upper_right=(.63, .63, 100.)) + [mats[2]], + samples=1000, + lower_left=(-0.63, -0.63, -100.0), + upper_right=(0.63, 0.63, 100.0), + ) cell_vol_calc = openmc.VolumeCalculation( - [geom.root_universe.cells[3]], samples=1000, - lower_left=(-.63, -.63, -100.), upper_right=(.63, .63, 100.)) - test_model.settings.volume_calculations = \ - [material_vol_calc, cell_vol_calc] + [geom.root_universe.cells[3]], + samples=1000, + lower_left=(-0.63, -0.63, -100.0), + upper_right=(0.63, 0.63, 100.0), + ) + test_model.settings.volume_calculations = [material_vol_calc, cell_vol_calc] # Now lets compute the volumes and check to see if it was applied # First lets do without using the C-API @@ -528,21 +562,21 @@ def test_calc_volumes(run_in_tmpdir, pin_model_attributes, mpi_intracomm): # Now let's test that we have volumes assigned; we arent checking the # value, just that the value was changed - assert mats[2].volume > 0. - assert geom.root_universe.cells[3].volume > 0. + assert mats[2].volume > 0.0 + assert geom.root_universe.cells[3].volume > 0.0 # Now reset the values mats[2].volume = None geom.root_universe.cells[3].volume = None # And do again with an initialized library - for file in ['volume_1.h5', 'volume_2.h5']: + for file in ["volume_1.h5", "volume_2.h5"]: file = Path(file) file.unlink() test_model.init_lib(output=False, intracomm=mpi_intracomm) test_model.calculate_volumes(output=False, apply_volumes=True) - assert mats[2].volume > 0. - assert geom.root_universe.cells[3].volume > 0. + assert mats[2].volume > 0.0 + assert geom.root_universe.cells[3].volume > 0.0 assert openmc.lib.materials[3].volume == mats[2].volume test_model.finalize_lib() @@ -554,9 +588,9 @@ def test_model_xml(run_in_tmpdir): pwr_model = openmc.examples.pwr_core() # export to separate XMLs manually - pwr_model.settings.export_to_xml('settings_ref.xml') - pwr_model.materials.export_to_xml('materials_ref.xml') - pwr_model.geometry.export_to_xml('geometry_ref.xml') + pwr_model.settings.export_to_xml("settings_ref.xml") + pwr_model.materials.export_to_xml("materials_ref.xml") + pwr_model.geometry.export_to_xml("geometry_ref.xml") # now write and read a model.xml file pwr_model.export_to_model_xml() @@ -571,26 +605,26 @@ def test_single_xml_exec(run_in_tmpdir): pincell_model = openmc.examples.pwr_pin_cell() - pincell_model.export_to_model_xml('pwr_pincell.xml') + pincell_model.export_to_model_xml("pwr_pincell.xml") - openmc.run(path_input='pwr_pincell.xml') + openmc.run(path_input="pwr_pincell.xml") - with pytest.raises(RuntimeError, match='ex-em-ell.xml'): - openmc.run(path_input='ex-em-ell.xml') + with pytest.raises(RuntimeError, match="ex-em-ell.xml"): + openmc.run(path_input="ex-em-ell.xml") # test that a file in a different directory can be used - os.mkdir('inputs') - pincell_model.export_to_model_xml('./inputs/pincell.xml') - openmc.run(path_input='./inputs/pincell.xml') + os.mkdir("inputs") + pincell_model.export_to_model_xml("./inputs/pincell.xml") + openmc.run(path_input="./inputs/pincell.xml") - with pytest.raises(RuntimeError, match='input_dir'): - openmc.run(path_input='input_dir/pincell.xml') + with pytest.raises(RuntimeError, match="input_dir"): + openmc.run(path_input="input_dir/pincell.xml") # Make sure path can be specified with run - pincell_model.run(path='my_model.xml') + pincell_model.run(path="my_model.xml") - os.mkdir('subdir') - pincell_model.run(path='subdir') + os.mkdir("subdir") + pincell_model.run(path="subdir") def test_model_plot(): diff --git a/tests/unit_tests/test_model_triso.py b/tests/unit_tests/test_model_triso.py index c1f8c039e57..b8fb2b2ecab 100644 --- a/tests/unit_tests/test_model_triso.py +++ b/tests/unit_tests/test_model_triso.py @@ -13,26 +13,26 @@ _RADIUS = 0.1 _PACKING_FRACTION = 0.35 _PARAMS = [ - {'shape': 'rectangular_prism', 'volume': 1**3}, - {'shape': 'x_cylinder', 'volume': 1*pi*1**2}, - {'shape': 'y_cylinder', 'volume': 1*pi*1**2}, - {'shape': 'z_cylinder', 'volume': 1*pi*1**2}, - {'shape': 'sphere', 'volume': 4/3*pi*1**3}, - {'shape': 'spherical_shell', 'volume': 4/3*pi*(1**3 - 0.5**3)} + {"shape": "rectangular_prism", "volume": 1**3}, + {"shape": "x_cylinder", "volume": 1 * pi * 1**2}, + {"shape": "y_cylinder", "volume": 1 * pi * 1**2}, + {"shape": "z_cylinder", "volume": 1 * pi * 1**2}, + {"shape": "sphere", "volume": 4 / 3 * pi * 1**3}, + {"shape": "spherical_shell", "volume": 4 / 3 * pi * (1**3 - 0.5**3)}, ] -@pytest.fixture(scope='module', params=_PARAMS) +@pytest.fixture(scope="module", params=_PARAMS) def container(request): return request.param -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def centers(request, container): - return request.getfixturevalue('centers_' + container['shape']) + return request.getfixturevalue("centers_" + container["shape"]) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def centers_rectangular_prism(): min_x = openmc.XPlane(0) max_x = openmc.XPlane(1) @@ -41,58 +41,64 @@ def centers_rectangular_prism(): min_z = openmc.ZPlane(0) max_z = openmc.ZPlane(1) region = +min_x & -max_x & +min_y & -max_y & +min_z & -max_z - return openmc.model.pack_spheres(radius=_RADIUS, region=region, - pf=_PACKING_FRACTION, initial_pf=0.2) + return openmc.model.pack_spheres( + radius=_RADIUS, region=region, pf=_PACKING_FRACTION, initial_pf=0.2 + ) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def centers_x_cylinder(): cylinder = openmc.XCylinder(r=1, y0=1, z0=2) min_x = openmc.XPlane(0) max_x = openmc.XPlane(1) region = +min_x & -max_x & -cylinder - return openmc.model.pack_spheres(radius=_RADIUS, region=region, - pf=_PACKING_FRACTION, initial_pf=0.2) + return openmc.model.pack_spheres( + radius=_RADIUS, region=region, pf=_PACKING_FRACTION, initial_pf=0.2 + ) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def centers_y_cylinder(): cylinder = openmc.YCylinder(r=1, x0=1, z0=2) min_y = openmc.YPlane(0) max_y = openmc.YPlane(1) region = +min_y & -max_y & -cylinder - return openmc.model.pack_spheres(radius=_RADIUS, region=region, - pf=_PACKING_FRACTION, initial_pf=0.2) + return openmc.model.pack_spheres( + radius=_RADIUS, region=region, pf=_PACKING_FRACTION, initial_pf=0.2 + ) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def centers_z_cylinder(): cylinder = openmc.ZCylinder(r=1, x0=1, y0=2) min_z = openmc.ZPlane(0) max_z = openmc.ZPlane(1) region = +min_z & -max_z & -cylinder - return openmc.model.pack_spheres(radius=_RADIUS, region=region, - pf=_PACKING_FRACTION, initial_pf=0.2) + return openmc.model.pack_spheres( + radius=_RADIUS, region=region, pf=_PACKING_FRACTION, initial_pf=0.2 + ) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def centers_sphere(): sphere = openmc.Sphere(r=1, x0=1, y0=2, z0=3) region = -sphere - return openmc.model.pack_spheres(radius=_RADIUS, region=region, - pf=_PACKING_FRACTION, initial_pf=0.2) + return openmc.model.pack_spheres( + radius=_RADIUS, region=region, pf=_PACKING_FRACTION, initial_pf=0.2 + ) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def centers_spherical_shell(): sphere = openmc.Sphere(r=1, x0=1, y0=2, z0=3) inner_sphere = openmc.Sphere(r=0.5, x0=1, y0=2, z0=3) region = -sphere & +inner_sphere - return openmc.model.pack_spheres(radius=_RADIUS, region=region, - pf=_PACKING_FRACTION, initial_pf=0.2) + return openmc.model.pack_spheres( + radius=_RADIUS, region=region, pf=_PACKING_FRACTION, initial_pf=0.2 + ) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def triso_universe(): sphere = openmc.Sphere(r=_RADIUS) cell = openmc.Cell(region=-sphere) @@ -110,7 +116,7 @@ def test_overlap(centers): # Get the smallest distance between any two spheres d_min = min(d[:, 1]) - assert d_min > 2*_RADIUS or d_min == pytest.approx(2*_RADIUS) + assert d_min > 2 * _RADIUS or d_min == pytest.approx(2 * _RADIUS) def test_contained_rectangular_prism(centers_rectangular_prism): @@ -123,10 +129,10 @@ def test_contained_rectangular_prism(centers_rectangular_prism): def test_contained_x_cylinder(centers_x_cylinder): """Make sure all spheres are entirely contained within the domain.""" - d = np.linalg.norm(centers_x_cylinder[:,[1,2]] - [1, 2], axis=1) + d = np.linalg.norm(centers_x_cylinder[:, [1, 2]] - [1, 2], axis=1) r_max = max(d) + _RADIUS - x_max = max(centers_x_cylinder[:,0]) + _RADIUS - x_min = min(centers_x_cylinder[:,0]) - _RADIUS + x_max = max(centers_x_cylinder[:, 0]) + _RADIUS + x_min = min(centers_x_cylinder[:, 0]) - _RADIUS assert r_max < 1 or r_max == pytest.approx(1) assert x_max < 1 or x_max == pytest.approx(1) assert x_min > 0 or x_min == pytest.approx(0) @@ -134,10 +140,10 @@ def test_contained_x_cylinder(centers_x_cylinder): def test_contained_y_cylinder(centers_y_cylinder): """Make sure all spheres are entirely contained within the domain.""" - d = np.linalg.norm(centers_y_cylinder[:,[0,2]] - [1, 2], axis=1) + d = np.linalg.norm(centers_y_cylinder[:, [0, 2]] - [1, 2], axis=1) r_max = max(d) + _RADIUS - y_max = max(centers_y_cylinder[:,1]) + _RADIUS - y_min = min(centers_y_cylinder[:,1]) - _RADIUS + y_max = max(centers_y_cylinder[:, 1]) + _RADIUS + y_min = min(centers_y_cylinder[:, 1]) - _RADIUS assert r_max < 1 or r_max == pytest.approx(1) assert y_max < 1 or y_max == pytest.approx(1) assert y_min > 0 or y_min == pytest.approx(0) @@ -145,10 +151,10 @@ def test_contained_y_cylinder(centers_y_cylinder): def test_contained_z_cylinder(centers_z_cylinder): """Make sure all spheres are entirely contained within the domain.""" - d = np.linalg.norm(centers_z_cylinder[:,[0,1]] - [1, 2], axis=1) + d = np.linalg.norm(centers_z_cylinder[:, [0, 1]] - [1, 2], axis=1) r_max = max(d) + _RADIUS - z_max = max(centers_z_cylinder[:,2]) + _RADIUS - z_min = min(centers_z_cylinder[:,2]) - _RADIUS + z_max = max(centers_z_cylinder[:, 2]) + _RADIUS + z_min = min(centers_z_cylinder[:, 2]) - _RADIUS assert r_max < 1 or r_max == pytest.approx(1) assert z_max < 1 or z_max == pytest.approx(1) assert z_min > 0 or z_min == pytest.approx(0) @@ -172,7 +178,7 @@ def test_contained_spherical_shell(centers_spherical_shell): def test_packing_fraction(container, centers): """Check that the actual PF is close to the requested PF.""" - pf = len(centers) * 4/3 * pi *_RADIUS**3 / container['volume'] + pf = len(centers) * 4 / 3 * pi * _RADIUS**3 / container["volume"] assert pf == pytest.approx(_PACKING_FRACTION, rel=1e-2) @@ -185,13 +191,15 @@ def test_num_spheres(): def test_triso_lattice(triso_universe, centers_rectangular_prism): - trisos = [openmc.model.TRISO(_RADIUS, triso_universe, c) - for c in centers_rectangular_prism] + trisos = [ + openmc.model.TRISO(_RADIUS, triso_universe, c) + for c in centers_rectangular_prism + ] lower_left = np.array((0, 0, 0)) upper_right = np.array((1, 1, 1)) shape = (3, 3, 3) - pitch = (upper_right - lower_left)/shape + pitch = (upper_right - lower_left) / shape background = openmc.Material() lattice = openmc.model.create_triso_lattice( @@ -210,9 +218,7 @@ def test_container_input(triso_universe): def test_packing_fraction_input(): # Provide neither packing fraction nor number of spheres with pytest.raises(ValueError): - centers = openmc.model.pack_spheres( - radius=_RADIUS, region=-openmc.Sphere(r=1) - ) + centers = openmc.model.pack_spheres(radius=_RADIUS, region=-openmc.Sphere(r=1)) # Specify a packing fraction that is too high for CRP with pytest.raises(ValueError): diff --git a/tests/unit_tests/test_no_visible_boundary.py b/tests/unit_tests/test_no_visible_boundary.py index 7c53e4e3fa3..0c8e69bb3d5 100644 --- a/tests/unit_tests/test_no_visible_boundary.py +++ b/tests/unit_tests/test_no_visible_boundary.py @@ -3,27 +3,26 @@ def test_no_visible_boundary(run_in_tmpdir): copper = openmc.Material() - copper.add_nuclide('Cu63', 1.0) - copper.set_density('g/cm3', 0.3) + copper.add_nuclide("Cu63", 1.0) + copper.set_density("g/cm3", 0.3) air = openmc.Material() - air.add_nuclide('N14', 1.0) - air.set_density('g/cm3', 0.0012) + air.add_nuclide("N14", 1.0) + air.set_density("g/cm3", 0.0012) # Create a simple model of a neutron source directly impinging on a thin # disc of copper. Neutrons leaving the back of the disc see no surfaces in # front of them. - disc = openmc.model.RightCircularCylinder((0., 0., 1.), 0.1, 1.2) - box = openmc.model.RectangularPrism(width=10, height=10, boundary_type='vacuum') + disc = openmc.model.RightCircularCylinder((0.0, 0.0, 1.0), 0.1, 1.2) + box = openmc.model.RectangularPrism(width=10, height=10, boundary_type="vacuum") c1 = openmc.Cell(fill=copper, region=-disc) c2 = openmc.Cell(fill=air, region=+disc & -box) model = openmc.Model() model.geometry = openmc.Geometry([c1, c2]) - model.settings.run_mode = 'fixed source' + model.settings.run_mode = "fixed source" model.settings.particles = 1000 model.settings.batches = 5 model.settings.source = openmc.IndependentSource( - space=openmc.stats.Point(), - angle=openmc.stats.Monodirectional((0., 0., 1.)) + space=openmc.stats.Point(), angle=openmc.stats.Monodirectional((0.0, 0.0, 1.0)) ) # Run model to ensure it doesn't segfault diff --git a/tests/unit_tests/test_pin.py b/tests/unit_tests/test_pin.py index 5496a3b73a6..4c32363d84c 100644 --- a/tests/unit_tests/test_pin.py +++ b/tests/unit_tests/test_pin.py @@ -44,7 +44,7 @@ def test_failure(pin_mats, good_radii): # Incorrect lengths with pytest.raises(ValueError, match="length"): - pin(good_surfaces[:len(pin_mats) - 2], pin_mats) + pin(good_surfaces[: len(pin_mats) - 2], pin_mats) # Non-positive radii rad = [openmc.ZCylinder(r=-0.1)] + good_surfaces[1:] @@ -69,15 +69,19 @@ def test_failure(pin_mats, good_radii): def test_pins_of_universes(pin_mats, good_radii): """Build a pin with a Universe in one ring""" u1 = openmc.Universe(cells=[openmc.Cell(fill=pin_mats[1])]) - new_items = pin_mats[:1] + (u1, ) + pin_mats[2:] + new_items = pin_mats[:1] + (u1,) + pin_mats[2:] new_pin = pin( - [openmc.ZCylinder(r=r) for r in good_radii], new_items, - subdivisions={0: 2}, divide_vols=True) + [openmc.ZCylinder(r=r) for r in good_radii], + new_items, + subdivisions={0: 2}, + divide_vols=True, + ) assert len(new_pin.cells) == len(pin_mats) + 1 @pytest.mark.parametrize( - "surf_type", [openmc.ZCylinder, openmc.XCylinder, openmc.YCylinder]) + "surf_type", [openmc.ZCylinder, openmc.XCylinder, openmc.YCylinder] +) def test_subdivide(pin_mats, good_radii, surf_type): """Test the subdivision with various orientations""" surfs = [surf_type(r=r) for r in good_radii] @@ -112,6 +116,8 @@ def test_subdivide(pin_mats, good_radii, surf_type): # check volumes of new rings radii = get_pin_radii(new_pin) - sqrs = np.square(radii[:N + 1]) - assert np.all(sqrs[1:] - sqrs[:-1] == pytest.approx( - (good_radii[1] ** 2 - good_radii[0] ** 2) / N)) + sqrs = np.square(radii[: N + 1]) + assert np.all( + sqrs[1:] - sqrs[:-1] + == pytest.approx((good_radii[1] ** 2 - good_radii[0] ** 2) / N) + ) diff --git a/tests/unit_tests/test_plots.py b/tests/unit_tests/test_plots.py index a1e15016a2e..4fb454fe608 100644 --- a/tests/unit_tests/test_plots.py +++ b/tests/unit_tests/test_plots.py @@ -5,63 +5,58 @@ import pytest -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def myplot(): - plot = openmc.Plot(name='myplot') - plot.width = (100., 100.) - plot.origin = (2., 3., -10.) + plot = openmc.Plot(name="myplot") + plot.width = (100.0, 100.0) + plot.origin = (2.0, 3.0, -10.0) plot.pixels = (500, 500) - plot.filename = './not-a-dir/myplot' - plot.type = 'slice' - plot.basis = 'yz' - plot.background = 'black' + plot.filename = "./not-a-dir/myplot" + plot.type = "slice" + plot.basis = "yz" + plot.background = "black" plot.background = (0, 0, 0) - plot.color_by = 'material' + plot.color_by = "material" m1, m2 = openmc.Material(), openmc.Material() plot.colors = {m1: (0, 255, 0), m2: (0, 0, 255)} - plot.colors = {m1: 'green', m2: 'blue'} + plot.colors = {m1: "green", m2: "blue"} plot.mask_components = [openmc.Material()] - plot.mask_background = 'white' + plot.mask_background = "white" plot.mask_background = (255, 255, 255) plot.overlap_color = (255, 211, 0) - plot.overlap_color = 'yellow' + plot.overlap_color = "yellow" plot.show_overlaps = True plot.level = 1 - plot.meshlines = { - 'type': 'tally', - 'id': 1, - 'linewidth': 2, - 'color': (40, 30, 20) - } + plot.meshlines = {"type": "tally", "id": 1, "linewidth": 2, "color": (40, 30, 20)} return plot -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def myprojectionplot(): - plot = openmc.ProjectionPlot(name='myprojectionplot') + plot = openmc.ProjectionPlot(name="myprojectionplot") plot.look_at = (0.0, 0.0, 0.0) plot.camera_position = (4.0, 3.0, 0.0) plot.pixels = (500, 500) - plot.filename = 'myprojectionplot' + plot.filename = "myprojectionplot" plot.background = (0, 0, 0) - plot.background = 'black' + plot.background = "black" - plot.color_by = 'material' + plot.color_by = "material" m1, m2 = openmc.Material(), openmc.Material() plot.colors = {m1: (0, 255, 0), m2: (0, 0, 255)} - plot.colors = {m1: 'green', m2: 'blue'} + plot.colors = {m1: "green", m2: "blue"} plot.xs = {m1: 1.0, m2: 0.01} plot.mask_components = [openmc.Material()] plot.mask_background = (255, 255, 255) - plot.mask_background = 'white' + plot.mask_background = "white" plot.overlap_color = (255, 211, 0) - plot.overlap_color = 'yellow' + plot.overlap_color = "yellow" plot.wireframe_thickness = 2 @@ -71,41 +66,41 @@ def myprojectionplot(): def test_voxel_plot(run_in_tmpdir): # attempt to preload VTK and skip this test if unavailable - vtk = pytest.importorskip('vtk') - surf1 = openmc.Sphere(r=500, boundary_type='vacuum') + vtk = pytest.importorskip("vtk") + surf1 = openmc.Sphere(r=500, boundary_type="vacuum") cell1 = openmc.Cell(region=-surf1) geometry = openmc.Geometry([cell1]) geometry.export_to_xml() materials = openmc.Materials() materials.export_to_xml() vox_plot = openmc.Plot() - vox_plot.type = 'voxel' + vox_plot.type = "voxel" vox_plot.id = 12 - vox_plot.width = (1500., 1500., 1500.) + vox_plot.width = (1500.0, 1500.0, 1500.0) vox_plot.pixels = (200, 200, 200) - vox_plot.color_by = 'cell' - vox_plot.to_vtk('test_voxel_plot.vti') + vox_plot.color_by = "cell" + vox_plot.to_vtk("test_voxel_plot.vti") - assert Path('plot_12.h5').is_file() - assert Path('test_voxel_plot.vti').is_file() + assert Path("plot_12.h5").is_file() + assert Path("test_voxel_plot.vti").is_file() - vox_plot.filename = 'h5_voxel_plot' - vox_plot.to_vtk('another_test_voxel_plot.vti') + vox_plot.filename = "h5_voxel_plot" + vox_plot.to_vtk("another_test_voxel_plot.vti") - assert Path('h5_voxel_plot.h5').is_file() - assert Path('another_test_voxel_plot.vti').is_file() + assert Path("h5_voxel_plot.h5").is_file() + assert Path("another_test_voxel_plot.vti").is_file() slice_plot = openmc.Plot() with pytest.raises(ValueError): - slice_plot.to_vtk('shimmy.vti') + slice_plot.to_vtk("shimmy.vti") def test_attributes(myplot): - assert myplot.name == 'myplot' + assert myplot.name == "myplot" def test_attributes_proj(myprojectionplot): - assert myprojectionplot.name == 'myprojectionplot' + assert myprojectionplot.name == "myprojectionplot" def test_repr(myplot): @@ -119,74 +114,86 @@ def test_repr_proj(myprojectionplot): def test_from_geometry(): - width = 25. - s = openmc.Sphere(r=width/2, boundary_type='vacuum') + width = 25.0 + s = openmc.Sphere(r=width / 2, boundary_type="vacuum") c = openmc.Cell(region=-s) univ = openmc.Universe(cells=[c]) geom = openmc.Geometry(univ) - for basis in ('xy', 'yz', 'xz'): + for basis in ("xy", "yz", "xz"): plot = openmc.Plot.from_geometry(geom, basis) - assert plot.origin == pytest.approx((0., 0., 0.)) + assert plot.origin == pytest.approx((0.0, 0.0, 0.0)) assert plot.width == pytest.approx((width, width)) assert plot.basis == basis def test_highlight_domains(): plot = openmc.Plot() - plot.color_by = 'material' + plot.color_by = "material" plots = openmc.Plots([plot]) model = openmc.examples.pwr_pin_cell() - mats = {m for m in model.materials if 'UO2' in m.name} + mats = {m for m in model.materials if "UO2" in m.name} plots.highlight_domains(model.geometry, mats) def test_xml_element(myplot): elem = myplot.to_xml_element() - assert 'id' in elem.attrib - assert 'color_by' in elem.attrib - assert 'type' in elem.attrib - assert elem.find('origin') is not None - assert elem.find('width') is not None - assert elem.find('pixels') is not None - assert elem.find('background').text == '0 0 0' + assert "id" in elem.attrib + assert "color_by" in elem.attrib + assert "type" in elem.attrib + assert elem.find("origin") is not None + assert elem.find("width") is not None + assert elem.find("pixels") is not None + assert elem.find("background").text == "0 0 0" newplot = openmc.Plot.from_xml_element(elem) - attributes = ('id', 'color_by', 'filename', 'type', 'basis', 'level', - 'meshlines', 'show_overlaps', 'origin', 'width', 'pixels', - 'background', 'mask_background') + attributes = ( + "id", + "color_by", + "filename", + "type", + "basis", + "level", + "meshlines", + "show_overlaps", + "origin", + "width", + "pixels", + "background", + "mask_background", + ) for attr in attributes: assert getattr(newplot, attr) == getattr(myplot, attr), attr def test_to_xml_element_proj(myprojectionplot): elem = myprojectionplot.to_xml_element() - assert 'id' in elem.attrib - assert 'color_by' in elem.attrib - assert 'type' in elem.attrib - assert elem.find('camera_position') is not None - assert elem.find('wireframe_thickness') is not None - assert elem.find('look_at') is not None - assert elem.find('pixels') is not None - assert elem.find('background').text == '0 0 0' + assert "id" in elem.attrib + assert "color_by" in elem.attrib + assert "type" in elem.attrib + assert elem.find("camera_position") is not None + assert elem.find("wireframe_thickness") is not None + assert elem.find("look_at") is not None + assert elem.find("pixels") is not None + assert elem.find("background").text == "0 0 0" def test_plots(run_in_tmpdir): - p1 = openmc.Plot(name='plot1') - p1.origin = (5., 5., 5.) + p1 = openmc.Plot(name="plot1") + p1.origin = (5.0, 5.0, 5.0) p1.colors = {10: (255, 100, 0)} p1.mask_components = [2, 4, 6] - p2 = openmc.Plot(name='plot2') - p2.origin = (-3., -3., -3.) + p2 = openmc.Plot(name="plot2") + p2.origin = (-3.0, -3.0, -3.0) plots = openmc.Plots([p1, p2]) assert len(plots) == 2 - p3 = openmc.ProjectionPlot(name='plot3') + p3 = openmc.ProjectionPlot(name="plot3") plots = openmc.Plots([p1, p2, p3]) assert len(plots) == 3 - p4 = openmc.Plot(name='plot4') + p4 = openmc.Plot(name="plot4") plots.append(p4) assert len(plots) == 4 @@ -203,13 +210,13 @@ def test_plots(run_in_tmpdir): def test_voxel_plot_roundtrip(): # Define a voxel plot and create XML element - plot = openmc.Plot(name='my voxel plot') - plot.type = 'voxel' - plot.filename = 'voxel1' + plot = openmc.Plot(name="my voxel plot") + plot.type = "voxel" + plot.filename = "voxel1" plot.pixels = (50, 50, 50) - plot.origin = (0., 0., 0.) - plot.width = (75., 75., 75.) - plot.color_by = 'material' + plot.origin = (0.0, 0.0, 0.0) + plot.width = (75.0, 75.0, 75.0) + plot.color_by = "material" elem = plot.to_xml_element() # Read back from XML and make sure it hasn't changed @@ -228,19 +235,19 @@ def test_plot_directory(run_in_tmpdir): # create a standard plot, expected to work plot = openmc.Plot() - plot.filename = 'plot_1' - plot.type = 'slice' + plot.filename = "plot_1" + plot.type = "slice" plot.pixels = (10, 10) - plot.color_by = 'material' - plot.width = (100., 100.) + plot.color_by = "material" + plot.width = (100.0, 100.0) pwr_pin.plots = [plot] pwr_pin.plot_geometry() # use current directory, also expected to work - plot.filename = './plot_1' + plot.filename = "./plot_1" pwr_pin.plot_geometry() # use a non-existent directory, should raise an error - plot.filename = './not-a-dir/plot_1' - with pytest.raises(RuntimeError, match='does not exist'): + plot.filename = "./not-a-dir/plot_1" + with pytest.raises(RuntimeError, match="does not exist"): pwr_pin.plot_geometry() diff --git a/tests/unit_tests/test_plotter.py b/tests/unit_tests/test_plotter.py index 0220cec3e71..faf467eecc0 100644 --- a/tests/unit_tests/test_plotter.py +++ b/tests/unit_tests/test_plotter.py @@ -3,7 +3,7 @@ import pytest -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def test_mat(): mat_1 = openmc.Material() mat_1.add_element("H", 4.0, "ao") @@ -35,9 +35,7 @@ def test_calculate_cexs_elem_mat_sab(test_mat): @pytest.mark.parametrize("this", ["Li", "Li6"]) def test_calculate_cexs_with_nuclide_and_element(this): # single type (reaction) - energy_grid, data = openmc.plotter.calculate_cexs( - this=this, types=[205] - ) + energy_grid, data = openmc.plotter.calculate_cexs(this=this, types=[205]) assert isinstance(energy_grid, np.ndarray) assert isinstance(data, np.ndarray) @@ -46,9 +44,7 @@ def test_calculate_cexs_with_nuclide_and_element(this): assert len(data[0]) == len(energy_grid) # two types (reactions) - energy_grid, data = openmc.plotter.calculate_cexs( - this=this, types=[2, "elastic"] - ) + energy_grid, data = openmc.plotter.calculate_cexs(this=this, types=[2, "elastic"]) assert isinstance(energy_grid, np.ndarray) assert isinstance(data, np.ndarray) @@ -61,9 +57,7 @@ def test_calculate_cexs_with_nuclide_and_element(this): def test_calculate_cexs_with_materials(test_mat): - energy_grid, data = openmc.plotter.calculate_cexs( - this=test_mat, types=[205] - ) + energy_grid, data = openmc.plotter.calculate_cexs(this=test_mat, types=[205]) assert isinstance(energy_grid, np.ndarray) assert isinstance(data, np.ndarray) @@ -75,48 +69,55 @@ def test_calculate_cexs_with_materials(test_mat): @pytest.mark.parametrize("this", ["Be", "Be9"]) def test_plot_xs(this): from matplotlib.figure import Figure - assert isinstance(openmc.plot_xs({this: ['total', 'elastic', 16, '(n,2n)']}), Figure) + + assert isinstance( + openmc.plot_xs({this: ["total", "elastic", 16, "(n,2n)"]}), Figure + ) def test_plot_xs_mat(test_mat): from matplotlib.figure import Figure - assert isinstance(openmc.plot_xs({test_mat: ['total']}), Figure) + + assert isinstance(openmc.plot_xs({test_mat: ["total"]}), Figure) @pytest.mark.parametrize("units", ["eV", "keV", "MeV"]) def test_plot_xs_energy_axis(units): - plot = openmc.plot_xs({'Be9': ['(n,2n)']}, energy_axis_units=units) + plot = openmc.plot_xs({"Be9": ["(n,2n)"]}, energy_axis_units=units) axis_text = plot.get_axes()[0].get_xaxis().get_label().get_text() - assert axis_text == f'Energy [{units}]' + assert axis_text == f"Energy [{units}]" def test_plot_axes_labels(): # just nuclides axis_label = openmc.plotter._get_yaxis_label( reactions={ - 'Li6': [205], - 'Li7': [205], - }, divisor_types=False + "Li6": [205], + "Li7": [205], + }, + divisor_types=False, ) - assert axis_label == 'Microscopic Cross Section [b]' + assert axis_label == "Microscopic Cross Section [b]" # just elements axis_label = openmc.plotter._get_yaxis_label( reactions={ - 'Li': [205], - 'Be': [16], - }, divisor_types=False + "Li": [205], + "Be": [16], + }, + divisor_types=False, ) - assert axis_label == 'Microscopic Cross Section [b]' + assert axis_label == "Microscopic Cross Section [b]" # mixed nuclide and element axis_label = openmc.plotter._get_yaxis_label( reactions={ - 'Li': [205], - 'Li7': [205], - }, divisor_types=False + "Li": [205], + "Li7": [205], + }, + divisor_types=False, ) - assert axis_label == 'Microscopic Cross Section [b]' + assert axis_label == "Microscopic Cross Section [b]" axis_label = openmc.plotter._get_yaxis_label( reactions={ @@ -135,49 +136,45 @@ def test_plot_axes_labels(): # just materials mat1 = openmc.Material() - mat1.add_nuclide('Fe56', 1) - mat1.set_density('g/cm3', 1) + mat1.add_nuclide("Fe56", 1) + mat1.set_density("g/cm3", 1) mat2 = openmc.Material() - mat2.add_element('Fe', 1) - mat2.add_nuclide('Fe55', 1) - mat2.set_density('g/cm3', 1) + mat2.add_element("Fe", 1) + mat2.add_nuclide("Fe55", 1) + mat2.set_density("g/cm3", 1) axis_label = openmc.plotter._get_yaxis_label( reactions={ mat1: [205], mat2: [16], - }, divisor_types=False + }, + divisor_types=False, ) - assert axis_label == 'Macroscopic Cross Section [1/cm]' + assert axis_label == "Macroscopic Cross Section [1/cm]" # mixed materials and nuclides with pytest.raises(TypeError): openmc.plotter._get_yaxis_label( - reactions={'Li6': [205], mat2: [16]}, - divisor_types=False + reactions={"Li6": [205], mat2: [16]}, divisor_types=False ) # mixed materials and elements with pytest.raises(TypeError): openmc.plotter._get_yaxis_label( - reactions={'Li': [205], mat2: [16]}, - divisor_types=False + reactions={"Li": [205], mat2: [16]}, divisor_types=False ) def test_get_title(): - title = openmc.plotter._get_title(reactions={'Li': [205]}) - assert title == 'Cross Section Plot For Li' - title = openmc.plotter._get_title(reactions={'Li6': [205]}) - assert title == 'Cross Section Plot For Li6' - title = openmc.plotter._get_title(reactions={ - 'Li6': [205], - 'Li7': [205] - }) - assert title == 'Cross Section Plot' + title = openmc.plotter._get_title(reactions={"Li": [205]}) + assert title == "Cross Section Plot For Li" + title = openmc.plotter._get_title(reactions={"Li6": [205]}) + assert title == "Cross Section Plot For Li6" + title = openmc.plotter._get_title(reactions={"Li6": [205], "Li7": [205]}) + assert title == "Cross Section Plot" mat1 = openmc.Material() - mat1.add_nuclide('Fe56', 1) - mat1.set_density('g/cm3', 1) - mat1.name = 'my_mat' + mat1.add_nuclide("Fe56", 1) + mat1.set_density("g/cm3", 1) + mat1.name = "my_mat" title = openmc.plotter._get_title(reactions={mat1: [205]}) - assert title == 'Cross Section Plot For my_mat' + assert title == "Cross Section Plot For my_mat" diff --git a/tests/unit_tests/test_polynomials.py b/tests/unit_tests/test_polynomials.py index 03f1fd5a467..f1a8be42e7c 100644 --- a/tests/unit_tests/test_polynomials.py +++ b/tests/unit_tests/test_polynomials.py @@ -13,14 +13,21 @@ def test_zernike_radial(): zn_rad = openmc.ZernikeRadial(coeff, 0.392) assert zn_rad.order == 10 assert zn_rad.radius == 0.392 - norm_vec = (2 * np.arange(6) + 1) / (np.pi * 0.392 ** 2) + norm_vec = (2 * np.arange(6) + 1) / (np.pi * 0.392**2) norm_coeff = norm_vec * coeff rho = 0.5 # Reference solution from running the Fortran implementation - raw_zn = np.array([ - 1.00000000e+00, -5.00000000e-01, -1.25000000e-01, - 4.37500000e-01, -2.89062500e-01, -8.98437500e-02]) + raw_zn = np.array( + [ + 1.00000000e00, + -5.00000000e-01, + -1.25000000e-01, + 4.37500000e-01, + -2.89062500e-01, + -8.98437500e-02, + ] + ) ref_vals = np.sum(norm_coeff * raw_zn) @@ -30,12 +37,26 @@ def test_zernike_radial(): rho = [0.2, 0.5] # Reference solution from running the Fortran implementation - raw_zn1 = np.array([ - 1.00000000e+00, -9.20000000e-01, 7.69600000e-01, - -5.66720000e-01, 3.35219200e-01, -1.01747000e-01]) - raw_zn2 = np.array([ - 1.00000000e+00, -5.00000000e-01, -1.25000000e-01, - 4.37500000e-01, -2.89062500e-01, -8.98437500e-02]) + raw_zn1 = np.array( + [ + 1.00000000e00, + -9.20000000e-01, + 7.69600000e-01, + -5.66720000e-01, + 3.35219200e-01, + -1.01747000e-01, + ] + ) + raw_zn2 = np.array( + [ + 1.00000000e00, + -5.00000000e-01, + -1.25000000e-01, + 4.37500000e-01, + -2.89062500e-01, + -8.98437500e-02, + ] + ) ref_vals = [np.sum(norm_coeff * raw_zn1), np.sum(norm_coeff * raw_zn2)] @@ -46,27 +67,45 @@ def test_zernike_radial(): def test_zernike(): import openmc.lib as lib - + coeff = np.asarray([1.1e-1, -3.2e2, 5.3, 7.4, -9.5, 0.005]) zn_azimuthal = openmc.Zernike(coeff) assert zn_azimuthal.order == 2 assert zn_azimuthal.radius == 1 - coeff = np.asarray([1.5, -3.6, 9.7e-1, -6.8e-1, 0.11, 0.33e2, 0.002, 13.75, - 3.1, -7.3, 7.8e-1, -1.1e-1, 2.56, 5.25e3, 0.123]) + coeff = np.asarray( + [ + 1.5, + -3.6, + 9.7e-1, + -6.8e-1, + 0.11, + 0.33e2, + 0.002, + 13.75, + 3.1, + -7.3, + 7.8e-1, + -1.1e-1, + 2.56, + 5.25e3, + 0.123, + ] + ) zn_azimuthal = openmc.Zernike(coeff, 0.392) assert zn_azimuthal.order == 4 assert zn_azimuthal.radius == 0.392 - norm_vec = np.array([1, 4, 4, 6, 3, 6, 8, 8, 8, 8, - 10, 10, 5, 10, 10]) / (np.pi * 0.392 ** 2) - norm_coeff = norm_vec * coeff - + norm_vec = np.array([1, 4, 4, 6, 3, 6, 8, 8, 8, 8, 10, 10, 5, 10, 10]) / ( + np.pi * 0.392**2 + ) + norm_coeff = norm_vec * coeff + rho = 0.5 - - theta = np.radians(45) + + theta = np.radians(45) # Reference solution from running the C API for calc_zn raw_zn = lib.calc_zn(zn_azimuthal.order, rho, theta) - + ref_vals = np.sum(norm_coeff * raw_zn) test_vals = zn_azimuthal(rho * zn_azimuthal.radius, theta) @@ -74,52 +113,54 @@ def test_zernike(): assert ref_vals == test_vals rho = [0.2, 0.5] - - theta = np.radians(30) - #Reference solution from running the C API for calc_zn + + theta = np.radians(30) + # Reference solution from running the C API for calc_zn raw_zn1 = lib.calc_zn(zn_azimuthal.order, rho[0], theta) - + raw_zn2 = lib.calc_zn(zn_azimuthal.order, rho[1], theta) - + ref_vals = [np.sum(norm_coeff * raw_zn1), np.sum(norm_coeff * raw_zn2)] test_vals = zn_azimuthal([i * zn_azimuthal.radius for i in rho], theta) - - assert np.allclose(ref_vals, test_vals) - + + assert np.allclose(ref_vals, test_vals) + rho = 0.2 - - theta = np.radians([30, 60]) - #Reference solution from running the C API for calc_zn + + theta = np.radians([30, 60]) + # Reference solution from running the C API for calc_zn raw_zn1 = lib.calc_zn(zn_azimuthal.order, rho, theta[0]) - + raw_zn2 = lib.calc_zn(zn_azimuthal.order, rho, theta[1]) ref_vals = [np.sum(norm_coeff * raw_zn1), np.sum(norm_coeff * raw_zn2)] test_vals = zn_azimuthal(rho * zn_azimuthal.radius, [j for j in theta]) - - assert np.allclose(ref_vals, test_vals) - + + assert np.allclose(ref_vals, test_vals) + rho = [0.2, 0.5] - - theta = np.radians([30, 60]) - #Reference solution from running the C API for calc_zn + + theta = np.radians([30, 60]) + # Reference solution from running the C API for calc_zn raw_zn1 = lib.calc_zn(zn_azimuthal.order, rho[0], theta[0]) - + raw_zn2 = lib.calc_zn(zn_azimuthal.order, rho[1], theta[0]) - + raw_zn3 = lib.calc_zn(zn_azimuthal.order, rho[0], theta[1]) - + raw_zn4 = lib.calc_zn(zn_azimuthal.order, rho[1], theta[1]) - ref_vals = [np.sum(norm_coeff * raw_zn1), np.sum(norm_coeff * raw_zn2), - np.sum(norm_coeff * raw_zn3), np.sum(norm_coeff * raw_zn4)] + ref_vals = [ + np.sum(norm_coeff * raw_zn1), + np.sum(norm_coeff * raw_zn2), + np.sum(norm_coeff * raw_zn3), + np.sum(norm_coeff * raw_zn4), + ] test_vals = zn_azimuthal([i * zn_azimuthal.radius for i in rho], [j for j in theta]) - - test_vals = np.ravel(test_vals) - - assert np.allclose(ref_vals, test_vals) - + test_vals = np.ravel(test_vals) + + assert np.allclose(ref_vals, test_vals) diff --git a/tests/unit_tests/test_region.py b/tests/unit_tests/test_region.py index cbcd1983125..01e25a8f4fc 100644 --- a/tests/unit_tests/test_region.py +++ b/tests/unit_tests/test_region.py @@ -25,14 +25,14 @@ def test_union(reset): assert (0, 0, 0) not in region # string representation - assert str(region) == '(1 | -2)' + assert str(region) == "(1 | -2)" # Combining region with intersection s3 = openmc.YPlane(surface_id=3) reg2 = region & +s3 assert (6, 1, 0) in reg2 assert (6, -1, 0) not in reg2 - assert str(reg2) == '((1 | -2) 3)' + assert str(reg2) == "((1 | -2) 3)" # translate method regt = region.translate((2.0, 0.0, 0.0)) @@ -41,7 +41,7 @@ def test_union(reset): assert (8, 0, 0) in regt # rotate method - regr = region.rotate((0., 90., 0.)) + regr = region.rotate((0.0, 90.0, 0.0)) assert (-4, 0, 0) not in regr assert (0, 0, 6) in regr assert (0, 0, -6) in regr @@ -65,14 +65,14 @@ def test_intersection(reset): assert (0, 0, 0) in region # string representation - assert str(region) == '(-1 2)' + assert str(region) == "(-1 2)" # Combining region with union s3 = openmc.YPlane(surface_id=3) reg2 = region | +s3 assert (-6, 2, 0) in reg2 assert (-6, -2, 0) not in reg2 - assert str(reg2) == '((-1 2) | 3)' + assert str(reg2) == "((-1 2) | 3)" # translate method regt = region.translate((2.0, 0.0, 0.0)) @@ -81,7 +81,7 @@ def test_intersection(reset): assert (8, 0, 0) not in regt # rotate method - regr = region.rotate((0., 90., 0.)) + regr = region.rotate((0.0, 90.0, 0.0)) assert (-4, 0, 0) in regr assert (0, 0, 6) not in regr assert (0, 0, -6) not in regr @@ -89,9 +89,9 @@ def test_intersection(reset): def test_complement(reset): - zcyl = openmc.ZCylinder(r=1., surface_id=1) - z0 = openmc.ZPlane(-5., surface_id=2) - z1 = openmc.ZPlane(5., surface_id=3) + zcyl = openmc.ZCylinder(r=1.0, surface_id=1) + z0 = openmc.ZPlane(-5.0, surface_id=2) + z1 = openmc.ZPlane(5.0, surface_id=3) outside = +zcyl | -z0 | +z1 inside = ~outside outside_equiv = ~(-zcyl & +z0 & -z1) @@ -100,13 +100,13 @@ def test_complement(reset): # Check bounding box for region in (inside, inside_equiv): ll, ur = region.bounding_box - assert ll == pytest.approx((-1., -1., -5.)) - assert ur == pytest.approx((1., 1., 5.)) + assert ll == pytest.approx((-1.0, -1.0, -5.0)) + assert ur == pytest.approx((1.0, 1.0, 5.0)) assert_unbounded(outside) assert_unbounded(outside_equiv) # string represention - assert str(inside) == '(-1 2 -3)' + assert str(inside) == "(-1 2 -3)" # evaluate method assert (0, 0, 0) in inside @@ -117,16 +117,16 @@ def test_complement(reset): # translate method inside_t = inside.translate((1.0, 1.0, 1.0)) ll, ur = inside_t.bounding_box - assert ll == pytest.approx((0., 0., -4.)) - assert ur == pytest.approx((2., 2., 6.)) + assert ll == pytest.approx((0.0, 0.0, -4.0)) + assert ur == pytest.approx((2.0, 2.0, 6.0)) # rotate method - inside_r = inside.rotate((90., 0., 0.)) + inside_r = inside.rotate((90.0, 0.0, 0.0)) ll, ur = inside_r.bounding_box - assert (.5, 2, 0) in inside_r + assert (0.5, 2, 0) in inside_r assert (0, 0, 6) not in inside_r - assert ll == pytest.approx((-1., -5., -1.)) - assert ur == pytest.approx((1., 5., 1.)) + assert ll == pytest.approx((-1.0, -5.0, -1.0)) + assert ur == pytest.approx((1.0, 5.0, 1.0)) def test_get_surfaces(): @@ -173,43 +173,44 @@ def test_extend_clone(): def test_from_expression(reset): # Create surface dictionary s1 = openmc.ZCylinder(surface_id=1) - s2 = openmc.ZPlane(-10., surface_id=2) - s3 = openmc.ZPlane(10., surface_id=3) + s2 = openmc.ZPlane(-10.0, surface_id=2) + s3 = openmc.ZPlane(10.0, surface_id=3) surfs = {1: s1, 2: s2, 3: s3} - r = openmc.Region.from_expression('-1 2 -3', surfs) + r = openmc.Region.from_expression("-1 2 -3", surfs) assert isinstance(r, openmc.Intersection) assert r[:] == [-s1, +s2, -s3] - r = openmc.Region.from_expression('+1 | -2 | +3', surfs) + r = openmc.Region.from_expression("+1 | -2 | +3", surfs) assert isinstance(r, openmc.Union) assert r[:] == [+s1, -s2, +s3] - r = openmc.Region.from_expression('~(-1)', surfs) + r = openmc.Region.from_expression("~(-1)", surfs) assert r == +s1 # Since & has higher precendence than |, the resulting region should be an # instance of Union - r = openmc.Region.from_expression('1 -2 | 3', surfs) + r = openmc.Region.from_expression("1 -2 | 3", surfs) assert isinstance(r, openmc.Union) assert isinstance(r[0], openmc.Intersection) assert r[0][:] == [+s1, -s2] # ...but not if we use parentheses - r = openmc.Region.from_expression('1 (-2 | 3)', surfs) + r = openmc.Region.from_expression("1 (-2 | 3)", surfs) assert isinstance(r, openmc.Intersection) assert isinstance(r[1], openmc.Union) assert r[1][:] == [-s2, +s3] # Make sure ")(" is handled correctly - r = openmc.Region.from_expression('(-1|2)(2|-3)', surfs) - assert str(r) == '((-1 | 2) (2 | -3))' + r = openmc.Region.from_expression("(-1|2)(2|-3)", surfs) + assert str(r) == "((-1 | 2) (2 | -3))" # Opening parenthesis immediately after halfspace - r = openmc.Region.from_expression('1(2|-3)', surfs) - assert str(r) == '(1 (2 | -3))' - r = openmc.Region.from_expression('-1|(1 2(-3))', surfs) - assert str(r) == '(-1 | (1 2 -3))' + r = openmc.Region.from_expression("1(2|-3)", surfs) + assert str(r) == "(1 (2 | -3))" + r = openmc.Region.from_expression("-1|(1 2(-3))", surfs) + assert str(r) == "(-1 | (1 2 -3))" + def test_translate_inplace(): sph = openmc.Sphere() @@ -230,15 +231,15 @@ def test_invalid_operands(): z = 3 # Intersection with invalid operand - with pytest.raises(ValueError, match='must be of type Region'): + with pytest.raises(ValueError, match="must be of type Region"): -s & +z # Union with invalid operand - with pytest.raises(ValueError, match='must be of type Region'): + with pytest.raises(ValueError, match="must be of type Region"): -s | +z # Complement with invalid operand - with pytest.raises(ValueError, match='must be of type Region'): + with pytest.raises(ValueError, match="must be of type Region"): openmc.Complement(z) diff --git a/tests/unit_tests/test_settings.py b/tests/unit_tests/test_settings.py index 02a47625162..e71e76d2377 100644 --- a/tests/unit_tests/test_settings.py +++ b/tests/unit_tests/test_settings.py @@ -3,67 +3,90 @@ def test_export_to_xml(run_in_tmpdir): - s = openmc.Settings(run_mode='fixed source', batches=1000, seed=17) + s = openmc.Settings(run_mode="fixed source", batches=1000, seed=17) s.generations_per_batch = 10 s.inactive = 100 s.particles = 1000000 s.max_lost_particles = 5 s.rel_max_lost_particles = 1e-4 - s.keff_trigger = {'type': 'std_dev', 'threshold': 0.001} - s.energy_mode = 'continuous-energy' + s.keff_trigger = {"type": "std_dev", "threshold": 0.001} + s.energy_mode = "continuous-energy" s.max_order = 5 s.max_tracks = 1234 s.source = openmc.IndependentSource(space=openmc.stats.Point()) - s.output = {'summary': True, 'tallies': False, 'path': 'here'} + s.output = {"summary": True, "tallies": False, "path": "here"} s.verbosity = 7 - s.sourcepoint = {'batches': [50, 150, 500, 1000], 'separate': True, - 'write': True, 'overwrite': True, 'mcpl': 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.sourcepoint = { + "batches": [50, 150, 500, 1000], + "separate": True, + "write": True, + "overwrite": True, + "mcpl": 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.confidence_intervals = True s.ptables = True s.plot_seed = 100 s.survival_biasing = True - s.cutoff = {'weight': 0.25, 'weight_avg': 0.5, 'energy_neutron': 1.0e-5, - 'energy_photon': 1000.0, 'energy_electron': 1.0e-5, - 'energy_positron': 1.0e-5, 'time_neutron': 1.0e-5, - 'time_photon': 1.0e-5, 'time_electron': 1.0e-5, - 'time_positron': 1.0e-5} + s.cutoff = { + "weight": 0.25, + "weight_avg": 0.5, + "energy_neutron": 1.0e-5, + "energy_photon": 1000.0, + "energy_electron": 1.0e-5, + "energy_positron": 1.0e-5, + "time_neutron": 1.0e-5, + "time_photon": 1.0e-5, + "time_electron": 1.0e-5, + "time_positron": 1.0e-5, + } mesh = openmc.RegularMesh() - mesh.lower_left = (-10., -10., -10.) - mesh.upper_right = (10., 10., 10.) + mesh.lower_left = (-10.0, -10.0, -10.0) + mesh.upper_right = (10.0, 10.0, 10.0) mesh.dimension = (5, 5, 5) s.entropy_mesh = mesh s.trigger_active = True s.trigger_max_batches = 10000 s.trigger_batch_interval = 50 s.no_reduce = False - s.tabular_legendre = {'enable': True, 'num_points': 50} - s.temperature = {'default': 293.6, 'method': 'interpolation', - 'multipole': True, 'range': (200., 1000.)} + s.tabular_legendre = {"enable": True, "num_points": 50} + s.temperature = { + "default": 293.6, + "method": "interpolation", + "multipole": True, + "range": (200.0, 1000.0), + } s.trace = (10, 1, 20) s.track = [(1, 1, 1), (2, 1, 1)] s.ufs_mesh = mesh - s.resonance_scattering = {'enable': True, 'method': 'rvs', - 'energy_min': 1.0, 'energy_max': 1000.0, - 'nuclides': ['U235', 'U238', 'Pu239']} + s.resonance_scattering = { + "enable": True, + "method": "rvs", + "energy_min": 1.0, + "energy_max": 1000.0, + "nuclides": ["U235", "U238", "Pu239"], + } s.volume_calculations = openmc.VolumeCalculation( - domains=[openmc.Cell()], samples=1000, lower_left=(-10., -10., -10.), - upper_right = (10., 10., 10.)) + domains=[openmc.Cell()], + samples=1000, + lower_left=(-10.0, -10.0, -10.0), + upper_right=(10.0, 10.0, 10.0), + ) s.create_fission_neutrons = True s.create_delayed_neutrons = False s.log_grid_bins = 2000 s.photon_transport = False - s.electron_treatment = 'led' + s.electron_treatment = "led" s.write_initial_source = True - s.weight_window_checkpoints = {'surface': True, 'collision': False} + s.weight_window_checkpoints = {"surface": True, "collision": False} s.random_ray = { - 'distance_inactive': 10.0, - 'distance_active': 100.0, - 'ray_source': openmc.IndependentSource( - space=openmc.stats.Box((-1., -1., -1.), (1., 1., 1.)) - ) + "distance_inactive": 10.0, + "distance_active": 100.0, + "ray_source": openmc.IndependentSource( + space=openmc.stats.Box((-1.0, -1.0, -1.0), (1.0, 1.0, 1.0)) + ), } s.max_particle_events = 100 @@ -73,72 +96,92 @@ def test_export_to_xml(run_in_tmpdir): # Generate settings from XML s = openmc.Settings.from_xml() - assert s.run_mode == 'fixed source' + assert s.run_mode == "fixed source" assert s.batches == 1000 assert s.generations_per_batch == 10 assert s.inactive == 100 assert s.particles == 1000000 assert s.max_lost_particles == 5 assert s.rel_max_lost_particles == 1e-4 - assert s.keff_trigger == {'type': 'std_dev', 'threshold': 0.001} - assert s.energy_mode == 'continuous-energy' + assert s.keff_trigger == {"type": "std_dev", "threshold": 0.001} + assert s.energy_mode == "continuous-energy" assert s.max_order == 5 assert s.max_tracks == 1234 assert isinstance(s.source[0], openmc.IndependentSource) assert isinstance(s.source[0].space, openmc.stats.Point) - assert s.output == {'summary': True, 'tallies': False, 'path': 'here'} + assert s.output == {"summary": True, "tallies": False, "path": "here"} assert s.verbosity == 7 - assert s.sourcepoint == {'batches': [50, 150, 500, 1000], 'separate': True, - 'write': True, 'overwrite': True, 'mcpl': True} - assert s.statepoint == {'batches': [50, 150, 500, 1000]} - assert s.surf_source_read['path'].name == 'surface_source_1.h5' - assert s.surf_source_write == {'surface_ids': [2], 'max_particles': 200} + assert s.sourcepoint == { + "batches": [50, 150, 500, 1000], + "separate": True, + "write": True, + "overwrite": True, + "mcpl": True, + } + assert s.statepoint == {"batches": [50, 150, 500, 1000]} + assert s.surf_source_read["path"].name == "surface_source_1.h5" + assert s.surf_source_write == {"surface_ids": [2], "max_particles": 200} assert s.confidence_intervals assert s.ptables assert s.plot_seed == 100 assert s.seed == 17 assert s.survival_biasing - assert s.cutoff == {'weight': 0.25, 'weight_avg': 0.5, - 'energy_neutron': 1.0e-5, 'energy_photon': 1000.0, - 'energy_electron': 1.0e-5, 'energy_positron': 1.0e-5, - 'time_neutron': 1.0e-5, 'time_photon': 1.0e-5, - 'time_electron': 1.0e-5, 'time_positron': 1.0e-5} + assert s.cutoff == { + "weight": 0.25, + "weight_avg": 0.5, + "energy_neutron": 1.0e-5, + "energy_photon": 1000.0, + "energy_electron": 1.0e-5, + "energy_positron": 1.0e-5, + "time_neutron": 1.0e-5, + "time_photon": 1.0e-5, + "time_electron": 1.0e-5, + "time_positron": 1.0e-5, + } assert isinstance(s.entropy_mesh, openmc.RegularMesh) - assert s.entropy_mesh.lower_left == [-10., -10., -10.] - assert s.entropy_mesh.upper_right == [10., 10., 10.] + assert s.entropy_mesh.lower_left == [-10.0, -10.0, -10.0] + assert s.entropy_mesh.upper_right == [10.0, 10.0, 10.0] assert s.entropy_mesh.dimension == (5, 5, 5) assert s.trigger_active assert s.trigger_max_batches == 10000 assert s.trigger_batch_interval == 50 assert not s.no_reduce - assert s.tabular_legendre == {'enable': True, 'num_points': 50} - assert s.temperature == {'default': 293.6, 'method': 'interpolation', - 'multipole': True, 'range': [200., 1000.]} + assert s.tabular_legendre == {"enable": True, "num_points": 50} + assert s.temperature == { + "default": 293.6, + "method": "interpolation", + "multipole": True, + "range": [200.0, 1000.0], + } assert s.trace == [10, 1, 20] assert s.track == [(1, 1, 1), (2, 1, 1)] assert isinstance(s.ufs_mesh, openmc.RegularMesh) - assert s.ufs_mesh.lower_left == [-10., -10., -10.] - assert s.ufs_mesh.upper_right == [10., 10., 10.] + assert s.ufs_mesh.lower_left == [-10.0, -10.0, -10.0] + assert s.ufs_mesh.upper_right == [10.0, 10.0, 10.0] assert s.ufs_mesh.dimension == (5, 5, 5) - assert s.resonance_scattering == {'enable': True, 'method': 'rvs', - 'energy_min': 1.0, 'energy_max': 1000.0, - 'nuclides': ['U235', 'U238', 'Pu239']} + assert s.resonance_scattering == { + "enable": True, + "method": "rvs", + "energy_min": 1.0, + "energy_max": 1000.0, + "nuclides": ["U235", "U238", "Pu239"], + } assert s.create_fission_neutrons assert not s.create_delayed_neutrons assert s.log_grid_bins == 2000 assert not s.photon_transport - assert s.electron_treatment == 'led' + assert s.electron_treatment == "led" assert s.write_initial_source == True assert len(s.volume_calculations) == 1 vol = s.volume_calculations[0] - assert vol.domain_type == 'cell' + assert vol.domain_type == "cell" assert len(vol.ids) == 1 assert vol.samples == 1000 - assert vol.lower_left == (-10., -10., -10.) - assert vol.upper_right == (10., 10., 10.) - assert s.weight_window_checkpoints == {'surface': True, 'collision': False} + assert vol.lower_left == (-10.0, -10.0, -10.0) + assert vol.upper_right == (10.0, 10.0, 10.0) + assert s.weight_window_checkpoints == {"surface": True, "collision": False} assert s.max_particle_events == 100 - assert s.random_ray['distance_inactive'] == 10.0 - assert s.random_ray['distance_active'] == 100.0 - assert s.random_ray['ray_source'].space.lower_left == [-1., -1., -1.] - assert s.random_ray['ray_source'].space.upper_right == [1., 1., 1.] + assert s.random_ray["distance_inactive"] == 10.0 + assert s.random_ray["distance_active"] == 100.0 + assert s.random_ray["ray_source"].space.lower_left == [-1.0, -1.0, -1.0] + assert s.random_ray["ray_source"].space.upper_right == [1.0, 1.0, 1.0] diff --git a/tests/unit_tests/test_source.py b/tests/unit_tests/test_source.py index c88fbcbe62d..ccdd426ce68 100644 --- a/tests/unit_tests/test_source.py +++ b/tests/unit_tests/test_source.py @@ -22,10 +22,10 @@ def test_source(): assert src.energy == energy elem = src.to_xml_element() - assert 'strength' in elem.attrib - assert elem.find('space') is not None - assert elem.find('angle') is not None - assert elem.find('energy') is not None + assert "strength" in elem.attrib + assert elem.find("space") is not None + assert elem.find("angle") is not None + assert elem.find("energy") is not None src = openmc.IndependentSource.from_xml_element(elem) assert isinstance(src.angle, openmc.stats.Isotropic) @@ -38,18 +38,17 @@ def test_source(): def test_spherical_uniform(): r_outer = 2.0 r_inner = 1.0 - thetas = (0.0, pi/2) + thetas = (0.0, pi / 2) phis = (0.0, pi) origin = (0.0, 1.0, 2.0) - sph_indep_function = openmc.stats.spherical_uniform(r_outer, - r_inner, - thetas, - phis, - origin) + sph_indep_function = openmc.stats.spherical_uniform( + r_outer, r_inner, thetas, phis, origin + ) assert isinstance(sph_indep_function, openmc.stats.SphericalIndependent) + def test_point_cloud(): positions = [(1, 0, 2), (0, 1, 0), (0, 0, 3), (4, 9, 2)] strengths = [1, 2, 3, 4] @@ -70,26 +69,26 @@ def test_point_cloud(): def test_point_cloud_invalid(): - with pytest.raises(ValueError, match='2D'): + with pytest.raises(ValueError, match="2D"): openmc.stats.PointCloud([1, 0, 2, 0, 1, 0]) - with pytest.raises(ValueError, match='3 values'): + with pytest.raises(ValueError, match="3 values"): openmc.stats.PointCloud([(1, 0, 2, 3), (4, 5, 2, 3)]) - with pytest.raises(ValueError, match='1D'): + with pytest.raises(ValueError, match="1D"): openmc.stats.PointCloud([(1, 0, 2), (4, 5, 2)], [(1, 2), (3, 4)]) - with pytest.raises(ValueError, match='same length'): + with pytest.raises(ValueError, match="same length"): openmc.stats.PointCloud([(1, 0, 2), (4, 5, 2)], [1, 2, 4]) def test_point_cloud_strengths(run_in_tmpdir, sphere_box_model): - positions = [(1., 0., 2.), (0., 1., 0.), (0., 0., 3.), (-1., -1., 2.)] + positions = [(1.0, 0.0, 2.0), (0.0, 1.0, 0.0), (0.0, 0.0, 3.0), (-1.0, -1.0, 2.0)] strengths = [1, 2, 3, 4] space = openmc.stats.PointCloud(positions, strengths) model = sphere_box_model[0] - model.settings.run_mode = 'fixed source' + model.settings.run_mode = "fixed source" model.settings.source = openmc.IndependentSource(space=space) try: @@ -102,41 +101,42 @@ def test_point_cloud_strengths(run_in_tmpdir, sphere_box_model): count = Counter(s.r for s in sites) for i, (strength, position) in enumerate(zip(strengths, positions)): sampled_strength = count[position] / n_samples - expected_strength = pytest.approx(strength/sum(strengths), abs=0.02) - assert sampled_strength == expected_strength, f'Strength incorrect for {positions[i]}' + expected_strength = pytest.approx(strength / sum(strengths), abs=0.02) + assert ( + sampled_strength == expected_strength + ), f"Strength incorrect for {positions[i]}" def test_source_file(): - filename = 'source.h5' + filename = "source.h5" src = openmc.FileSource(path=filename) assert src.path.name == filename elem = src.to_xml_element() - assert 'strength' in elem.attrib - assert 'file' in elem.attrib + assert "strength" in elem.attrib + assert "file" in elem.attrib def test_source_dlopen(): - library = 'libsource.so' + library = "libsource.so" src = openmc.CompiledSource(library) assert src.library.name == library elem = src.to_xml_element() - assert 'library' in elem.attrib + assert "library" in elem.attrib def test_source_xml_roundtrip(): # Create a source and write to an XML element - space = openmc.stats.Box([-5., -5., -5.], [5., 5., 5.]) + space = openmc.stats.Box([-5.0, -5.0, -5.0], [5.0, 5.0, 5.0]) energy = openmc.stats.Discrete([1.0e6, 2.0e6, 5.0e6], [0.3, 0.5, 0.2]) angle = openmc.stats.PolarAzimuthal( - mu=openmc.stats.Uniform(0., 1.), - phi=openmc.stats.Uniform(0., 2*pi), - reference_uvw=(0., 1., 0.) + mu=openmc.stats.Uniform(0.0, 1.0), + phi=openmc.stats.Uniform(0.0, 2 * pi), + reference_uvw=(0.0, 1.0, 0.0), ) src = openmc.IndependentSource( - space=space, angle=angle, energy=energy, - particle='photon', strength=100.0 + space=space, angle=angle, energy=energy, particle="photon", strength=100.0 ) elem = src.to_xml_element() @@ -162,11 +162,11 @@ def test_source_xml_roundtrip(): def sphere_box_model(): # Model with two spheres inside a box mat = openmc.Material() - mat.add_nuclide('H1', 1.0) + mat.add_nuclide("H1", 1.0) sph1 = openmc.Sphere(x0=3, r=1.0) sph2 = openmc.Sphere(x0=-3, r=1.0) cube = openmc.model.RectangularParallelepiped( - -5., 5., -5., 5., -5., 5., boundary_type='reflective' + -5.0, 5.0, -5.0, 5.0, -5.0, 5.0, boundary_type="reflective" ) cell1 = openmc.Cell(fill=mat, region=-sph1) cell2 = openmc.Cell(fill=mat, region=-sph2) @@ -176,7 +176,7 @@ def sphere_box_model(): model.geometry = openmc.Geometry([cell1, cell2, cell3]) model.settings.particles = 100 model.settings.batches = 10 - model.settings.run_mode = 'fixed source' + model.settings.run_mode = "fixed source" return model, cell1, cell2, cell3 @@ -185,9 +185,9 @@ def test_constraints_independent(sphere_box_model, run_in_tmpdir): model, cell1, cell2, cell3 = sphere_box_model # Set up a box source with rejection on the spherical cell - space = openmc.stats.Box((-4., -1., -1.), (4., 1., 1.)) + space = openmc.stats.Box((-4.0, -1.0, -1.0), (4.0, 1.0, 1.0)) model.settings.source = openmc.IndependentSource( - space=space, constraints={'domains': [cell1, cell2]} + space=space, constraints={"domains": [cell1, cell2]} ) # Load up model via openmc.lib and sample source @@ -215,7 +215,7 @@ def test_constraints_mesh(sphere_box_model, run_in_tmpdir): left_source = openmc.IndependentSource() right_source = openmc.IndependentSource() model.settings.source = openmc.MeshSource( - mesh, [left_source, right_source], constraints={'domains': [cell1, cell2]} + mesh, [left_source, right_source], constraints={"domains": [cell1, cell2]} ) # Load up model via openmc.lib and sample source @@ -236,18 +236,18 @@ def test_constraints_file(sphere_box_model, run_in_tmpdir): # Create source file with randomly sampled source sites rng = np.random.default_rng() - energy = rng.uniform(0., 1e6, 10_000) - time = rng.uniform(0., 1., 10_000) + energy = rng.uniform(0.0, 1e6, 10_000) + time = rng.uniform(0.0, 1.0, 10_000) particles = [openmc.SourceParticle(E=e, time=t) for e, t in zip(energy, time)] - openmc.write_source_file(particles, 'uniform_source.h5') + openmc.write_source_file(particles, "uniform_source.h5") # Use source file model.settings.source = openmc.FileSource( - 'uniform_source.h5', + "uniform_source.h5", constraints={ - 'time_bounds': [0.25, 0.75], - 'energy_bounds': [500.e3, 1.0e6], - } + "time_bounds": [0.25, 0.75], + "energy_bounds": [500.0e3, 1.0e6], + }, ) # Load up model via openmc.lib and sample source @@ -258,45 +258,44 @@ def test_constraints_file(sphere_box_model, run_in_tmpdir): # Make sure that all sampled sources are within energy/time bounds for p in particles: assert 0.25 <= p.time <= 0.75 - assert 500.e3 <= p.E <= 1.0e6 + assert 500.0e3 <= p.E <= 1.0e6 openmc.lib.finalize() -@pytest.mark.skipif(config['mpi'], reason='Not compatible with MPI') +@pytest.mark.skipif(config["mpi"], reason="Not compatible with MPI") def test_rejection_limit(sphere_box_model, run_in_tmpdir): model, cell1 = sphere_box_model[:2] # Define a point source that will get rejected 100% of the time model.settings.source = openmc.IndependentSource( - space=openmc.stats.Point((-3., 0., 0.)), - constraints={'domains': [cell1]} + space=openmc.stats.Point((-3.0, 0.0, 0.0)), constraints={"domains": [cell1]} ) # Confirm that OpenMC doesn't run in an infinite loop. Note that this may # work when running with MPI since it won't necessarily capture the error # message correctly with pytest.raises(RuntimeError, match="rejected"): - model.run(openmc_exec=config['exe']) + model.run(openmc_exec=config["exe"]) def test_exceptions(): - with pytest.raises(AttributeError, match=r'Please use the FileSource class'): + with pytest.raises(AttributeError, match=r"Please use the FileSource class"): s = openmc.IndependentSource() - s.file = 'my_file' + s.file = "my_file" - with pytest.raises(AttributeError, match=r'Please use the CompiledSource class'): + with pytest.raises(AttributeError, match=r"Please use the CompiledSource class"): s = openmc.IndependentSource() - s.library = 'my_library' + s.library = "my_library" - with pytest.raises(AttributeError, match=r'Please use the CompiledSource class'): + with pytest.raises(AttributeError, match=r"Please use the CompiledSource class"): s = openmc.IndependentSource() - s.parameters = 'my_params' + s.parameters = "my_params" - with pytest.warns(FutureWarning, match=r'in favor of \'IndependentSource\''): + with pytest.warns(FutureWarning, match=r"in favor of \'IndependentSource\'"): s = openmc.Source() - with pytest.raises(AttributeError, match=r'has no attribute \'frisbee\''): + with pytest.raises(AttributeError, match=r"has no attribute \'frisbee\'"): s = openmc.IndependentSource() s.frisbee diff --git a/tests/unit_tests/test_source_file.py b/tests/unit_tests/test_source_file.py index 41906c80f83..f81b113e844 100644 --- a/tests/unit_tests/test_source_file.py +++ b/tests/unit_tests/test_source_file.py @@ -9,43 +9,45 @@ def test_source_file(run_in_tmpdir): # write_source_file shouldn't accept non-SourceParticle items with pytest.raises(TypeError): - openmc.write_source_file([1, 2, 3], 'test_source.h5') + openmc.write_source_file([1, 2, 3], "test_source.h5") # Create source particles source = [] n = 1000 for i in range(n): - source.append(openmc.SourceParticle( - r=(random(), i, 0), - u=(0., 0., 1.), - E=float(n - i), - )) + source.append( + openmc.SourceParticle( + r=(random(), i, 0), + u=(0.0, 0.0, 1.0), + E=float(n - i), + ) + ) # Create source file - openmc.write_source_file(source, 'test_source.h5') + openmc.write_source_file(source, "test_source.h5") # Get array of source particles from file - with h5py.File('test_source.h5', 'r') as fh: - filetype = fh.attrs['filetype'] - arr = fh['source_bank'][...] + with h5py.File("test_source.h5", "r") as fh: + filetype = fh.attrs["filetype"] + arr = fh["source_bank"][...] # Ensure data is consistent - assert filetype == b'source' - r = arr['r'] - assert np.all((r['x'] > 0.0) & (r['x'] < 1.0)) - assert np.all(r['y'] == np.arange(1000)) - assert np.all(r['z'] == 0.0) - u = arr['u'] - assert np.all(u['x'] == 0.0) - assert np.all(u['y'] == 0.0) - assert np.all(u['z'] == 1.0) - assert np.all(arr['E'] == n - np.arange(n)) - assert np.all(arr['wgt'] == 1.0) - assert np.all(arr['delayed_group'] == 0) - assert np.all(arr['particle'] == 0) + assert filetype == b"source" + r = arr["r"] + assert np.all((r["x"] > 0.0) & (r["x"] < 1.0)) + assert np.all(r["y"] == np.arange(1000)) + assert np.all(r["z"] == 0.0) + u = arr["u"] + assert np.all(u["x"] == 0.0) + assert np.all(u["y"] == 0.0) + assert np.all(u["z"] == 1.0) + assert np.all(arr["E"] == n - np.arange(n)) + assert np.all(arr["wgt"] == 1.0) + assert np.all(arr["delayed_group"] == 0) + assert np.all(arr["particle"] == 0) # Ensure sites read in are consistent - sites = openmc.ParticleList.from_hdf5('test_source.h5') + sites = openmc.ParticleList.from_hdf5("test_source.h5") xs = np.array([site.r[0] for site in sites]) ys = np.array([site.r[1] for site in sites]) @@ -74,7 +76,7 @@ def test_source_file(run_in_tmpdir): # Ensure site slice read in and exported are consistent sites_slice = sites[:10] sites_slice.export_to_hdf5("test_source_slice.h5") - sites_slice = openmc.ParticleList.from_hdf5('test_source_slice.h5') + sites_slice = openmc.ParticleList.from_hdf5("test_source_slice.h5") assert isinstance(sites_slice, openmc.ParticleList) assert len(sites_slice) == 10 @@ -85,7 +87,7 @@ def test_source_file(run_in_tmpdir): df = sites.to_dataframe() sites_filtered = sites[df[df.E <= 10.0].index.tolist()] sites_filtered.export_to_hdf5("test_source_filtered.h5") - sites_filtered = openmc.read_source_file('test_source_filtered.h5') + sites_filtered = openmc.read_source_file("test_source_filtered.h5") assert isinstance(sites_filtered, openmc.ParticleList) assert len(sites_filtered) == 10 @@ -95,53 +97,55 @@ def test_source_file(run_in_tmpdir): def test_wrong_source_attributes(run_in_tmpdir): # Create a source file with animal attributes - source_dtype = np.dtype([ - ('platypus', '= 0.95) - assert((diff < 6*std_dev).sum() / diff.size >= 0.997) + assert (diff < 2 * std_dev).sum() / diff.size >= 0.95 + assert (diff < 6 * std_dev).sum() / diff.size >= 0.997 def test_strengths_size_failure(request, model): # setup mesh source ### mesh_filename = Path(request.fspath).parent / "test_mesh_tets.e" - uscd_mesh = openmc.UnstructuredMesh(mesh_filename, 'libmesh') + uscd_mesh = openmc.UnstructuredMesh(mesh_filename, "libmesh") # intentionally incorrectly sized to trigger an error n_cells = len(model.geometry.get_all_cells()) - strengths = np.random.rand(n_cells*TETS_PER_VOXEL) + strengths = np.random.rand(n_cells * TETS_PER_VOXEL) # create the spatial distribution based on the mesh space = openmc.stats.MeshSpatial(uscd_mesh, strengths) - energy = openmc.stats.Discrete(x=[15.e+06], p=[1.0]) + energy = openmc.stats.Discrete(x=[15.0e06], p=[1.0]) source = openmc.IndependentSource(space=space, energy=energy) model.settings.source = source # skip the test if unstructured mesh is not available if not openmc.lib._libmesh_enabled(): if openmc.lib._dagmc_enabled(): - source.space.mesh.library = 'moab' + source.space.mesh.library = "moab" else: pytest.skip("Unstructured mesh support unavailable.") @@ -171,7 +174,7 @@ def test_strengths_size_failure(request, model): mesh_filename = Path(request.fspath).parent / source.space.mesh.filename - with pytest.raises(RuntimeError, match=r'strengths array'), cdtemp([mesh_filename]): + with pytest.raises(RuntimeError, match=r"strengths array"), cdtemp([mesh_filename]): model.export_to_xml() openmc.run() @@ -180,16 +183,16 @@ def test_roundtrip(run_in_tmpdir, model, request): if not openmc.lib._libmesh_enabled() and not openmc.lib._dagmc_enabled(): pytest.skip("Unstructured mesh is not enabled in this build.") - mesh_filename = Path(request.fspath).parent / 'test_mesh_tets.e' - ucd_mesh = openmc.UnstructuredMesh(mesh_filename, library='libmesh') + mesh_filename = Path(request.fspath).parent / "test_mesh_tets.e" + ucd_mesh = openmc.UnstructuredMesh(mesh_filename, library="libmesh") if not openmc.lib._libmesh_enabled(): - ucd_mesh.library = 'moab' + ucd_mesh.library = "moab" n_cells = len(model.geometry.get_all_cells()) space_out = openmc.MeshSpatial(ucd_mesh) - space_out.strengths = np.random.rand(n_cells*TETS_PER_VOXEL) + space_out.strengths = np.random.rand(n_cells * TETS_PER_VOXEL) model.settings.source = openmc.IndependentSource(space=space_out) # write out the model @@ -215,17 +218,17 @@ def void_model(): """ model = openmc.Model() - box = openmc.model.RectangularParallelepiped(*[-10, 10]*3, boundary_type='vacuum') + box = openmc.model.RectangularParallelepiped(*[-10, 10] * 3, boundary_type="vacuum") model.geometry = openmc.Geometry([openmc.Cell(region=-box)]) model.settings.particles = 100 model.settings.batches = 10 - model.settings.run_mode = 'fixed source' + model.settings.run_mode = "fixed source" return model -@pytest.mark.parametrize('mesh_type', ('rectangular', 'cylindrical')) +@pytest.mark.parametrize("mesh_type", ("rectangular", "cylindrical")) def test_mesh_source_independent(run_in_tmpdir, void_model, mesh_type): """ A void model containing a single box @@ -233,12 +236,12 @@ def test_mesh_source_independent(run_in_tmpdir, void_model, mesh_type): model = void_model # define a 2 x 2 x 2 mesh - if mesh_type == 'rectangular': + if mesh_type == "rectangular": mesh = openmc.RegularMesh.from_domain(model.geometry, (2, 2, 2)) - elif mesh_type == 'cylindrical': + elif mesh_type == "cylindrical": mesh = openmc.CylindricalMesh.from_domain(model.geometry, (1, 4, 2)) - energy = openmc.stats.Discrete([1.e6], [1.0]) + energy = openmc.stats.Discrete([1.0e6], [1.0]) # create sources with only one non-zero strength for the source in the mesh # voxel occupying the lowest octant. Direct source particles straight out of @@ -251,7 +254,7 @@ def test_mesh_source_independent(run_in_tmpdir, void_model, mesh_type): x, y, z = np.swapaxes(mesh.centroids, -1, 0) for i, j, k in mesh.indices: # mesh.indices is currently one-indexed, adjust for Python arrays - ijk = (i-1, j-1, k-1) + ijk = (i - 1, j - 1, k - 1) # get the centroid of the ijk mesh element and use it to set the # direction of the source directly out of the problem @@ -259,7 +262,9 @@ def test_mesh_source_independent(run_in_tmpdir, void_model, mesh_type): vec = np.sign(centroid, dtype=float) vec /= np.linalg.norm(vec) angle = openmc.stats.Monodirectional(vec) - sources[ijk] = openmc.IndependentSource(energy=energy, angle=angle, strength=0.0) + sources[ijk] = openmc.IndependentSource( + energy=energy, angle=angle, strength=0.0 + ) # create and apply the mesh source mesh_source = openmc.MeshSource(mesh, sources) @@ -269,7 +274,7 @@ def test_mesh_source_independent(run_in_tmpdir, void_model, mesh_type): mesh_filter = openmc.MeshFilter(mesh) tally = openmc.Tally() tally.filters = [mesh_filter] - tally.scores = ['flux'] + tally.scores = ["flux"] model.tallies = openmc.Tallies([tally]) @@ -277,7 +282,7 @@ def test_mesh_source_independent(run_in_tmpdir, void_model, mesh_type): # traveling out of the mesh (and geometry) w/o crossing any other # mesh elements for flat_index, (i, j, k) in enumerate(mesh.indices): - ijk = (i-1, j-1, k-1) + ijk = (i - 1, j - 1, k - 1) # zero-out all source strengths and set the strength # on the element of interest mesh_source.strength = 0.0 @@ -295,7 +300,9 @@ def test_mesh_source_independent(run_in_tmpdir, void_model, mesh_type): assert mean[ijk] != 0 # all other values should be zero mean[ijk] = 0 - assert np.all(mean == 0), f'Failed on index {ijk} with centroid {mesh.centroids[ijk]}' + assert np.all( + mean == 0 + ), f"Failed on index {ijk} with centroid {mesh.centroids[ijk]}" # test roundtrip xml_model = openmc.Model.from_model_xml() @@ -316,14 +323,15 @@ def test_mesh_source_independent(run_in_tmpdir, void_model, mesh_type): assert mesh_source.strength == 1.0 -@pytest.mark.parametrize("library", ('moab', 'libmesh')) +@pytest.mark.parametrize("library", ("moab", "libmesh")) def test_umesh_source_independent(run_in_tmpdir, request, void_model, library): import openmc.lib + # skip the test if the library is not enabled - if library == 'moab' and not openmc.lib._dagmc_enabled(): + if library == "moab" and not openmc.lib._dagmc_enabled(): pytest.skip("DAGMC (and MOAB) mesh not enabled in this build.") - if library == 'libmesh' and not openmc.lib._libmesh_enabled(): + if library == "libmesh" and not openmc.lib._libmesh_enabled(): pytest.skip("LibMesh is not enabled in this build.") model = void_model @@ -332,17 +340,17 @@ def test_umesh_source_independent(run_in_tmpdir, request, void_model, library): uscd_mesh = openmc.UnstructuredMesh(mesh_filename, library) ind_source = openmc.IndependentSource() n_elements = 12_000 - model.settings.source = openmc.MeshSource(uscd_mesh, n_elements*[ind_source]) + model.settings.source = openmc.MeshSource(uscd_mesh, n_elements * [ind_source]) model.export_to_model_xml() try: openmc.lib.init() openmc.lib.simulation_init() sites = openmc.lib.sample_external_source(10) - openmc.lib.statepoint_write('statepoint.h5') + openmc.lib.statepoint_write("statepoint.h5") finally: openmc.lib.finalize() - with openmc.StatePoint('statepoint.h5') as sp: + with openmc.StatePoint("statepoint.h5") as sp: uscd_mesh = sp.meshes[uscd_mesh.id] # ensure at least that all sites are inside the mesh @@ -354,21 +362,22 @@ def test_umesh_source_independent(run_in_tmpdir, request, void_model, library): def test_mesh_source_file(run_in_tmpdir): # Creating a source file with a single particle source_particle = openmc.SourceParticle(time=10.0) - openmc.write_source_file([source_particle], 'source.h5') - file_source = openmc.FileSource('source.h5') + openmc.write_source_file([source_particle], "source.h5") + file_source = openmc.FileSource("source.h5") model = openmc.Model() rect_prism = openmc.model.RectangularParallelepiped( - -5.0, 5.0, -5.0, 5.0, -5.0, 5.0, boundary_type='vacuum') + -5.0, 5.0, -5.0, 5.0, -5.0, 5.0, boundary_type="vacuum" + ) mat = openmc.Material() - mat.add_nuclide('H1', 1.0) + mat.add_nuclide("H1", 1.0) model.geometry = openmc.Geometry([openmc.Cell(fill=mat, region=-rect_prism)]) model.settings.particles = 1000 model.settings.batches = 10 - model.settings.run_mode = 'fixed source' + model.settings.run_mode = "fixed source" mesh = openmc.RegularMesh() mesh.lower_left = (-1, -2, -3) diff --git a/tests/unit_tests/test_spherical_mesh.py b/tests/unit_tests/test_spherical_mesh.py index 0b579be35f7..3b0746d068a 100644 --- a/tests/unit_tests/test_spherical_mesh.py +++ b/tests/unit_tests/test_spherical_mesh.py @@ -7,17 +7,19 @@ geom_size = 5 + @pytest.fixture() def model(): openmc.reset_auto_ids() - water = openmc.Material(name='water') - water.add_element('H', 2.0) - water.add_element('O', 1.0) - water.set_density('g/cc', 1.0) + water = openmc.Material(name="water") + water.add_element("H", 2.0) + water.add_element("O", 1.0) + water.set_density("g/cc", 1.0) - rpp = openmc.model.RectangularParallelepiped(*([-geom_size, geom_size] * 3), - boundary_type='vacuum') + rpp = openmc.model.RectangularParallelepiped( + *([-geom_size, geom_size] * 3), boundary_type="vacuum" + ) cell = openmc.Cell(region=-rpp, fill=water) @@ -30,11 +32,11 @@ def model(): settings = openmc.Settings() settings.particles = 2000 settings.batches = 10 - settings.run_mode = 'fixed source' + settings.run_mode = "fixed source" # build mesh = openmc.SphericalMesh( - phi_grid=np.linspace(0, 2*np.pi, 13), + phi_grid=np.linspace(0, 2 * np.pi, 13), theta_grid=np.linspace(0, np.pi, 7), r_grid=np.linspace(0, geom_size, geom_size), ) @@ -49,9 +51,9 @@ def model(): return openmc.Model(geometry=geom, settings=settings, tallies=tallies) + def test_origin_read_write_to_xml(run_in_tmpdir, model): - """Tests that the origin attribute can be written and read back to XML - """ + """Tests that the origin attribute can be written and read back to XML""" mesh = model.tallies[0].filters[0].mesh mesh.origin = [0.1, 0.2, 0.3] model.tallies.export_to_xml() @@ -62,7 +64,8 @@ def test_origin_read_write_to_xml(run_in_tmpdir, model): new_mesh = new_tally.filters[0].mesh np.testing.assert_equal(new_mesh.origin, mesh.origin) -estimators = ('tracklength', 'collision') + +estimators = ("tracklength", "collision") # TODO: determine why this is needed for spherical mesh # but not cylindrical mesh offset = geom_size + 0.001 @@ -72,16 +75,17 @@ def test_origin_read_write_to_xml(run_in_tmpdir, model): test_cases = product(estimators, origins) + def label(p): if isinstance(p, tuple): - return f'origin:{p}' + return f"origin:{p}" if isinstance(p, str): - return f'estimator:{p}' + return f"estimator:{p}" + -@pytest.mark.parametrize('estimator,origin', test_cases, ids=label) +@pytest.mark.parametrize("estimator,origin", test_cases, ids=label) def test_offset_mesh(run_in_tmpdir, model, estimator, origin): - """Tests that the mesh has been moved based on tally results - """ + """Tests that the mesh has been moved based on tally results""" mesh = model.tallies[0].filters[0].mesh model.tallies[0].estimator = estimator # move the center of the spherical mesh @@ -98,7 +102,7 @@ def test_offset_mesh(run_in_tmpdir, model, estimator, origin): # check that the half of the mesh that is outside of the geometry # contains the zero values - mean = tally.get_reshaped_data('mean', expand_dims=True) + mean = tally.get_reshaped_data("mean", expand_dims=True) centroids = mesh.centroids for ijk in mesh.indices: i, j, k = np.array(ijk) - 1 @@ -107,9 +111,11 @@ def test_offset_mesh(run_in_tmpdir, model, estimator, origin): else: mean[i, j, k] != 0.0 + # Some void geometry tests to check our radial intersection methods on # spherical and cylindrical meshes + @pytest.fixture() def void_coincident_geom_model(): """A model with many geometric boundaries coincident with mesh boundaries @@ -122,7 +128,7 @@ def void_coincident_geom_model(): model.materials = openmc.Materials() radii = [0.1, 1, 5, 50, 100, 150, 250] spheres = [openmc.Sphere(r=ri) for ri in radii] - spheres[-1].boundary_type = 'vacuum' + spheres[-1].boundary_type = "vacuum" regions = openmc.model.subdivide(spheres)[:-1] cells = [openmc.Cell(region=r, fill=None) for r in regions] @@ -130,7 +136,7 @@ def void_coincident_geom_model(): model.geometry = geom - settings = openmc.Settings(run_mode='fixed source') + settings = openmc.Settings(run_mode="fixed source") settings.batches = 2 settings.particles = 5000 model.settings = settings @@ -139,7 +145,7 @@ def void_coincident_geom_model(): mesh_filter = openmc.MeshFilter(mesh) tally = openmc.Tally() - tally.scores = ['flux'] + tally.scores = ["flux"] tally.filters = [mesh_filter] model.tallies = openmc.Tallies([tally]) @@ -177,7 +183,7 @@ def test_void_geom_boundary_src(run_in_tmpdir, void_coincident_geom_model): # with directions pointing toward the origin n_sources = 20 phi_vals = np.linspace(0, np.pi, n_sources) - theta_vals = np.linspace(0, 2.0*np.pi, n_sources) + theta_vals = np.linspace(0, 2.0 * np.pi, n_sources) bbox = void_coincident_geom_model.geometry.bounding_box # can't source particles directly on the geometry boundary @@ -192,13 +198,15 @@ def test_void_geom_boundary_src(run_in_tmpdir, void_coincident_geom_model): src = openmc.IndependentSource() src.energy = energy - pnt = np.array([np.sin(phi)*np.cos(theta), np.sin(phi)*np.sin(theta), np.cos(phi)]) + pnt = np.array( + [np.sin(phi) * np.cos(theta), np.sin(phi) * np.sin(theta), np.cos(phi)] + ) u = -pnt - src.space = openmc.stats.Point(outer_r*pnt) + src.space = openmc.stats.Point(outer_r * pnt) src.angle = openmc.stats.Monodirectional(u) # set source strengths so that we can still expect # a tally value of 0.5 - src.strength = 0.5/n_sources + src.strength = 0.5 / n_sources sources.append(src) diff --git a/tests/unit_tests/test_stats.py b/tests/unit_tests/test_stats.py index 643e115564b..5ccbaba7d7c 100644 --- a/tests/unit_tests/test_stats.py +++ b/tests/unit_tests/test_stats.py @@ -13,14 +13,14 @@ def assert_sample_mean(samples, expected_mean): # Means should agree within 4 sigma 99.993% of the time. Note that this is # expected to fail about 1 out of 16,000 times - assert np.abs(expected_mean - samples.mean()) < 4*std_dev + assert np.abs(expected_mean - samples.mean()) < 4 * std_dev def test_discrete(): x = [0.0, 1.0, 10.0] p = [0.3, 0.2, 0.5] d = openmc.stats.Discrete(x, p) - elem = d.to_xml_element('distribution') + elem = d.to_xml_element("distribution") d = openmc.stats.Discrete.from_xml_element(elem) np.testing.assert_array_equal(d.x, x) @@ -71,7 +71,8 @@ def test_merge_discrete(): merged = openmc.stats.Discrete.merge([d1, d2], [0.6, 0.4]) assert merged.x == pytest.approx([0.0, 0.5, 1.0, 5.0, 10.0]) assert merged.p == pytest.approx( - [0.6*0.3, 0.4*0.4, 0.6*0.2 + 0.4*0.5, 0.4*0.1, 0.6*0.5]) + [0.6 * 0.3, 0.4 * 0.4, 0.6 * 0.2 + 0.4 * 0.5, 0.4 * 0.1, 0.6 * 0.5] + ) assert merged.integral() == pytest.approx(1.0) # Probabilities add up but are not normalized @@ -98,7 +99,7 @@ def test_clip_discrete(): assert d_same is d with pytest.raises(ValueError): - d.clip(-1.) + d.clip(-1.0) with pytest.raises(ValueError): d.clip(5) @@ -107,7 +108,7 @@ def test_clip_discrete(): def test_uniform(): a, b = 10.0, 20.0 d = openmc.stats.Uniform(a, b) - elem = d.to_xml_element('distribution') + elem = d.to_xml_element("distribution") d = openmc.stats.Uniform.from_xml_element(elem) assert d.a == a @@ -116,8 +117,8 @@ def test_uniform(): t = d.to_tabular() np.testing.assert_array_equal(t.x, [a, b]) - np.testing.assert_array_equal(t.p, [1/(b-a), 1/(b-a)]) - assert t.interpolation == 'histogram' + np.testing.assert_array_equal(t.p, [1 / (b - a), 1 / (b - a)]) + assert t.interpolation == "histogram" # Sample distribution and check that the mean of the samples is within 4 # std. dev. of the expected mean @@ -130,7 +131,7 @@ def test_uniform(): def test_powerlaw(): a, b, n = 10.0, 100.0, 2.0 d = openmc.stats.PowerLaw(a, b, n) - elem = d.to_xml_element('distribution') + elem = d.to_xml_element("distribution") d = openmc.stats.PowerLaw.from_xml_element(elem) assert d.a == a @@ -139,7 +140,11 @@ def test_powerlaw(): assert len(d) == 3 # Determine mean of distribution - exp_mean = (n+1)*(b**(n+2) - a**(n+2))/((n+2)*(b**(n+1) - a**(n+1))) + exp_mean = ( + (n + 1) + * (b ** (n + 2) - a ** (n + 2)) + / ((n + 2) * (b ** (n + 1) - a ** (n + 1))) + ) # sample power law distribution and check that the mean of the samples is # within 4 std. dev. of the expected mean @@ -151,13 +156,13 @@ def test_powerlaw(): def test_maxwell(): theta = 1.2895e6 d = openmc.stats.Maxwell(theta) - elem = d.to_xml_element('distribution') + elem = d.to_xml_element("distribution") d = openmc.stats.Maxwell.from_xml_element(elem) assert d.theta == theta assert len(d) == 1 - exp_mean = 3/2 * theta + exp_mean = 3 / 2 * theta # sample maxwell distribution and check that the mean of the samples is # within 4 std. dev. of the expected mean @@ -174,7 +179,7 @@ def test_maxwell(): def test_watt(): a, b = 0.965e6, 2.29e-6 d = openmc.stats.Watt(a, b) - elem = d.to_xml_element('distribution') + elem = d.to_xml_element("distribution") d = openmc.stats.Watt.from_xml_element(elem) assert d.a == a @@ -185,7 +190,7 @@ def test_watt(): # "Prompt-fission-neutron average energy for 238U(n, f ) from # threshold to 200 MeV" Ethvignot et. al. # https://doi.org/10.1016/j.physletb.2003.09.048 - exp_mean = 3/2 * a + a**2 * b / 4 + exp_mean = 3 / 2 * a + a**2 * b / 4 # sample Watt distribution and check that the mean of the samples is within # 4 std. dev. of the expected mean @@ -197,13 +202,13 @@ def test_watt(): def test_tabular(): x = np.array([0.0, 5.0, 7.0, 10.0]) p = np.array([10.0, 20.0, 5.0, 6.0]) - d = openmc.stats.Tabular(x, p, 'linear-linear') - elem = d.to_xml_element('distribution') + d = openmc.stats.Tabular(x, p, "linear-linear") + elem = d.to_xml_element("distribution") d = openmc.stats.Tabular.from_xml_element(elem) assert all(d.x == x) assert all(d.p == p) - assert d.interpolation == 'linear-linear' + assert d.interpolation == "linear-linear" assert len(d) == len(x) # test linear-linear sampling @@ -217,7 +222,7 @@ def test_tabular(): assert d.integral() == pytest.approx(1.0) # test histogram sampling - d = openmc.stats.Tabular(x, p, interpolation='histogram') + d = openmc.stats.Tabular(x, p, interpolation="histogram") samples = d.sample(n_samples) assert_sample_mean(samples, d.mean()) @@ -226,35 +231,35 @@ def test_tabular(): # ensure that passing a set of probabilities shorter than x works # for histogram interpolation - d = openmc.stats.Tabular(x, p[:-1], interpolation='histogram') + d = openmc.stats.Tabular(x, p[:-1], interpolation="histogram") d.cdf() d.mean() assert_sample_mean(d.sample(n_samples), d.mean()) # passing a shorter probability set should raise an error for linear-linear with pytest.raises(ValueError): - d = openmc.stats.Tabular(x, p[:-1], interpolation='linear-linear') + d = openmc.stats.Tabular(x, p[:-1], interpolation="linear-linear") d.cdf() # Use probabilities of correct length for linear-linear interpolation and # call the CDF method - d = openmc.stats.Tabular(x, p, interpolation='linear-linear') + d = openmc.stats.Tabular(x, p, interpolation="linear-linear") d.cdf() def test_legendre(): # Pu239 elastic scattering at 100 keV - coeffs = [1.000e+0, 1.536e-1, 1.772e-2, 5.945e-4, 3.497e-5, 1.881e-5] + coeffs = [1.000e0, 1.536e-1, 1.772e-2, 5.945e-4, 3.497e-5, 1.881e-5] d = openmc.stats.Legendre(coeffs) assert d.coefficients == pytest.approx(coeffs) assert len(d) == len(coeffs) # Integrating distribution should yield one - mu = np.linspace(-1., 1., 1000) + mu = np.linspace(-1.0, 1.0, 1000) assert trapezoid(d(mu), mu) == pytest.approx(1.0, rel=1e-4) with pytest.raises(NotImplementedError): - d.to_xml_element('distribution') + d.to_xml_element("distribution") def test_mixture(): @@ -269,9 +274,9 @@ def test_mixture(): # Sample and make sure sample mean is close to expected mean n_samples = 1_000_000 samples = mix.sample(n_samples) - assert_sample_mean(samples, (2.5 + 5.0)/2) + assert_sample_mean(samples, (2.5 + 5.0) / 2) - elem = mix.to_xml_element('distribution') + elem = mix.to_xml_element("distribution") d = openmc.stats.Mixture.from_xml_element(elem) np.testing.assert_allclose(d.probability, p) @@ -297,15 +302,15 @@ def test_mixture_clip(): assert mix_same is mix # Make sure clip removes low probability distributions - d_small = openmc.stats.Uniform(0., 1.) - d_large = openmc.stats.Uniform(2., 5.) + d_small = openmc.stats.Uniform(0.0, 1.0) + d_large = openmc.stats.Uniform(2.0, 5.0) mix = openmc.stats.Mixture([1e-10, 1.0], [d_small, d_large]) mix_clip = mix.clip(1e-3) assert mix_clip.distribution == [d_large] # Make sure warning is raised if tolerance is exceeded d1 = openmc.stats.Discrete([1.0, 1.001], [1.0, 0.7e-6]) - d2 = openmc.stats.Tabular([0.0, 1.0], [0.7e-6], interpolation='histogram') + d2 = openmc.stats.Tabular([0.0, 1.0], [0.7e-6], interpolation="histogram") mix = openmc.stats.Mixture([1.0, 1.0], [d1, d2]) with pytest.warns(UserWarning): mix_clip = mix.clip(1e-6) @@ -315,29 +320,29 @@ def test_polar_azimuthal(): # default polar-azimuthal should be uniform in mu and phi d = openmc.stats.PolarAzimuthal() assert isinstance(d.mu, openmc.stats.Uniform) - assert d.mu.a == -1. - assert d.mu.b == 1. + assert d.mu.a == -1.0 + assert d.mu.b == 1.0 assert isinstance(d.phi, openmc.stats.Uniform) - assert d.phi.a == 0. - assert d.phi.b == 2*pi + assert d.phi.a == 0.0 + assert d.phi.b == 2 * pi - mu = openmc.stats.Discrete(1., 1.) - phi = openmc.stats.Discrete(0., 1.) + mu = openmc.stats.Discrete(1.0, 1.0) + phi = openmc.stats.Discrete(0.0, 1.0) d = openmc.stats.PolarAzimuthal(mu, phi) assert d.mu == mu assert d.phi == phi elem = d.to_xml_element() - assert elem.tag == 'angle' - assert elem.attrib['type'] == 'mu-phi' - assert elem.find('mu') is not None - assert elem.find('phi') is not None + assert elem.tag == "angle" + assert elem.attrib["type"] == "mu-phi" + assert elem.find("mu") is not None + assert elem.find("phi") is not None d = openmc.stats.PolarAzimuthal.from_xml_element(elem) - assert d.mu.x == [1.] - assert d.mu.p == [1.] - assert d.phi.x == [0.] - assert d.phi.p == [1.] + assert d.mu.x == [1.0] + assert d.mu.p == [1.0] + assert d.phi.x == [0.0] + assert d.phi.p == [1.0] d = openmc.stats.UnitSphere.from_xml_element(elem) assert isinstance(d, openmc.stats.PolarAzimuthal) @@ -346,34 +351,34 @@ def test_polar_azimuthal(): def test_isotropic(): d = openmc.stats.Isotropic() elem = d.to_xml_element() - assert elem.tag == 'angle' - assert elem.attrib['type'] == 'isotropic' + assert elem.tag == "angle" + assert elem.attrib["type"] == "isotropic" d = openmc.stats.Isotropic.from_xml_element(elem) assert isinstance(d, openmc.stats.Isotropic) def test_monodirectional(): - d = openmc.stats.Monodirectional((1., 0., 0.)) + d = openmc.stats.Monodirectional((1.0, 0.0, 0.0)) elem = d.to_xml_element() - assert elem.tag == 'angle' - assert elem.attrib['type'] == 'monodirectional' + assert elem.tag == "angle" + assert elem.attrib["type"] == "monodirectional" d = openmc.stats.Monodirectional.from_xml_element(elem) - assert d.reference_uvw == pytest.approx((1., 0., 0.)) + assert d.reference_uvw == pytest.approx((1.0, 0.0, 0.0)) def test_cartesian(): - x = openmc.stats.Uniform(-10., 10.) - y = openmc.stats.Uniform(-10., 10.) - z = openmc.stats.Uniform(0., 20.) + x = openmc.stats.Uniform(-10.0, 10.0) + y = openmc.stats.Uniform(-10.0, 10.0) + z = openmc.stats.Uniform(0.0, 20.0) d = openmc.stats.CartesianIndependent(x, y, z) elem = d.to_xml_element() - assert elem.tag == 'space' - assert elem.attrib['type'] == 'cartesian' - assert elem.find('x') is not None - assert elem.find('y') is not None + assert elem.tag == "space" + assert elem.attrib["type"] == "cartesian" + assert elem.find("x") is not None + assert elem.find("y") is not None d = openmc.stats.CartesianIndependent.from_xml_element(elem) assert d.x == x @@ -385,14 +390,14 @@ def test_cartesian(): def test_box(): - lower_left = (-10., -10., -10.) - upper_right = (10., 10., 10.) + lower_left = (-10.0, -10.0, -10.0) + upper_right = (10.0, 10.0, 10.0) d = openmc.stats.Box(lower_left, upper_right) elem = d.to_xml_element() - assert elem.tag == 'space' - assert elem.attrib['type'] == 'box' - assert elem.find('parameters') is not None + assert elem.tag == "space" + assert elem.attrib["type"] == "box" + assert elem.find("parameters") is not None d = openmc.stats.Box.from_xml_element(elem) assert d.lower_left == pytest.approx(lower_left) @@ -400,13 +405,13 @@ def test_box(): def test_point(): - p = (-4., 2., 10.) + p = (-4.0, 2.0, 10.0) d = openmc.stats.Point(p) elem = d.to_xml_element() - assert elem.tag == 'space' - assert elem.attrib['type'] == 'point' - assert elem.find('parameters') is not None + assert elem.tag == "space" + assert elem.attrib["type"] == "point" + assert elem.find("parameters") is not None d = openmc.stats.Point.from_xml_element(elem) assert d.xyz == pytest.approx(p) @@ -415,10 +420,10 @@ def test_point(): def test_normal(): mean = 10.0 std_dev = 2.0 - d = openmc.stats.Normal(mean,std_dev) + d = openmc.stats.Normal(mean, std_dev) - elem = d.to_xml_element('distribution') - assert elem.attrib['type'] == 'normal' + elem = d.to_xml_element("distribution") + assert elem.attrib["type"] == "normal" d = openmc.stats.Normal.from_xml_element(elem) assert d.mean_value == pytest.approx(mean) @@ -434,12 +439,12 @@ def test_normal(): def test_muir(): mean = 10.0 mass = 5.0 - temp = 20000. + temp = 20000.0 d = openmc.stats.muir(mean, mass, temp) assert isinstance(d, openmc.stats.Normal) - elem = d.to_xml_element('energy') - assert elem.attrib['type'] == 'normal' + elem = d.to_xml_element("energy") + assert elem.attrib["type"] == "normal" d = openmc.stats.Univariate.from_xml_element(elem) assert isinstance(d, openmc.stats.Normal) @@ -465,7 +470,8 @@ def test_combine_distributions(): assert isinstance(merged, openmc.stats.Discrete) assert merged.x == pytest.approx([0.0, 0.5, 1.0, 5.0, 10.0]) assert merged.p == pytest.approx( - [0.6*0.3, 0.4*0.4, 0.6*0.2 + 0.4*0.5, 0.4*0.1, 0.6*0.5]) + [0.6 * 0.3, 0.4 * 0.4, 0.6 * 0.2 + 0.4 * 0.5, 0.4 * 0.1, 0.6 * 0.5] + ) # Probabilities add up but are not normalized d1 = openmc.stats.Discrete([3.0], [1.0]) @@ -483,8 +489,8 @@ def test_combine_distributions(): # Combine 1 discrete and 2 tabular -- the tabular distributions should # combine to produce a uniform distribution with mean 0.5. The combined # distribution should have a mean of 0.25. - t1 = openmc.stats.Tabular([0., 1.], [2.0, 0.0]) - t2 = openmc.stats.Tabular([0., 1.], [0.0, 2.0]) + t1 = openmc.stats.Tabular([0.0, 1.0], [2.0, 0.0]) + t2 = openmc.stats.Tabular([0.0, 1.0], [0.0, 2.0]) d1 = openmc.stats.Discrete([0.0], [1.0]) combined = openmc.stats.combine_distributions([t1, t2, d1], [0.25, 0.25, 0.5]) assert combined.integral() == pytest.approx(1.0) diff --git a/tests/unit_tests/test_surface.py b/tests/unit_tests/test_surface.py index 12cd8c9d20d..ddd3cdae97b 100644 --- a/tests/unit_tests/test_surface.py +++ b/tests/unit_tests/test_surface.py @@ -17,35 +17,35 @@ def assert_infinite_bb(s): def test_plane(): - s = openmc.Plane(a=1, b=2, c=-1, d=3, name='my plane') + s = openmc.Plane(a=1, b=2, c=-1, d=3, name="my plane") assert s.a == 1 assert s.b == 2 assert s.c == -1 assert s.d == 3 - assert s.boundary_type == 'transmission' - assert s.name == 'my plane' - assert s.type == 'plane' + assert s.boundary_type == "transmission" + assert s.name == "my plane" + assert s.type == "plane" # Generic planes don't have well-defined bounding boxes assert_infinite_bb(s) # evaluate method x, y, z = (4, 3, 6) - assert s.evaluate((x, y, z)) == pytest.approx(s.a*x + s.b*y + s.c*z - s.d) + assert s.evaluate((x, y, z)) == pytest.approx(s.a * x + s.b * y + s.c * z - s.d) # translate method st = s.translate((1.0, 0.0, 0.0)) assert (st.a, st.b, st.c, st.d) == (s.a, s.b, s.c, 4) # rotate method - yp = openmc.YPlane(abs(s.d)/math.sqrt(s.a**2 + s.b**2 + s.c**2)) + yp = openmc.YPlane(abs(s.d) / math.sqrt(s.a**2 + s.b**2 + s.c**2)) psi = math.degrees(math.atan2(1, 2)) phi = math.degrees(math.atan2(1, math.sqrt(5))) - sr = s.rotate((phi, 0., psi), order='zyx') + sr = s.rotate((phi, 0.0, psi), order="zyx") assert yp.normalize() == pytest.approx(sr.normalize()) # test rotation ordering phi = math.degrees(math.atan2(1, math.sqrt(2))) - sr = s.rotate((0., -45., phi), order='xyz') + sr = s.rotate((0.0, -45.0, phi), order="xyz") assert yp.normalize() == pytest.approx(sr.normalize()) # Make sure repr works @@ -67,16 +67,16 @@ def test_plane_from_points(): def test_xplane(): - s = openmc.XPlane(3., boundary_type='reflective') - assert s.x0 == 3. - assert s.boundary_type == 'reflective' + s = openmc.XPlane(3.0, boundary_type="reflective") + assert s.x0 == 3.0 + assert s.boundary_type == "reflective" # Check bounding box ll, ur = (+s).bounding_box - assert ll == pytest.approx((3., -np.inf, -np.inf)) + assert ll == pytest.approx((3.0, -np.inf, -np.inf)) assert np.all(np.isinf(ur)) ll, ur = (-s).bounding_box - assert ur == pytest.approx((3., np.inf, np.inf)) + assert ur == pytest.approx((3.0, np.inf, np.inf)) assert np.all(np.isinf(ll)) # __contains__ on associated half-spaces @@ -86,7 +86,7 @@ def test_xplane(): assert (-2, 1, 10) not in +s # evaluate method - assert s.evaluate((5., 0., 0.)) == pytest.approx(2.) + assert s.evaluate((5.0, 0.0, 0.0)) == pytest.approx(2.0) # translate method st = s.translate((1.0, 0.0, 0.0)) @@ -94,26 +94,26 @@ def test_xplane(): # rotate method # make sure rotating around x axis does nothing to coefficients - sr = s.rotate((37.4, 0., 0.)) + sr = s.rotate((37.4, 0.0, 0.0)) assert s._get_base_coeffs() == pytest.approx(sr._get_base_coeffs()) # rotating around z by 90 deg then x by -90 deg should give negative z-plane - sr = s.rotate((-90., 0., 90), order='zyx') - assert (0., 0., -1., 3.) == pytest.approx(sr._get_base_coeffs()) + sr = s.rotate((-90.0, 0.0, 90), order="zyx") + assert (0.0, 0.0, -1.0, 3.0) == pytest.approx(sr._get_base_coeffs()) # Make sure repr works repr(s) def test_yplane(): - s = openmc.YPlane(y0=3.) - assert s.y0 == 3. + s = openmc.YPlane(y0=3.0) + assert s.y0 == 3.0 # Check bounding box ll, ur = (+s).bounding_box - assert ll == pytest.approx((-np.inf, 3., -np.inf)) + assert ll == pytest.approx((-np.inf, 3.0, -np.inf)) assert np.all(np.isinf(ur)) - ll, ur = s.bounding_box('-') - assert ur == pytest.approx((np.inf, 3., np.inf)) + ll, ur = s.bounding_box("-") + assert ur == pytest.approx((np.inf, 3.0, np.inf)) assert np.all(np.isinf(ll)) # __contains__ on associated half-spaces @@ -123,7 +123,7 @@ def test_yplane(): assert (-2, 1, 10) not in +s # evaluate method - assert s.evaluate((0., 0., 0.)) == pytest.approx(-3.) + assert s.evaluate((0.0, 0.0, 0.0)) == pytest.approx(-3.0) # translate method st = s.translate((0.0, 1.0, 0.0)) @@ -131,26 +131,26 @@ def test_yplane(): # rotate method # make sure rotating around y axis does nothing to coefficients - sr = s.rotate((0., -12.4, 0.), order='yxz') + sr = s.rotate((0.0, -12.4, 0.0), order="yxz") assert s._get_base_coeffs() == pytest.approx(sr._get_base_coeffs()) # rotate around x by -90 deg and y by 90 deg should give negative x-plane - sr = s.rotate((-90, 90, 0.)) - assert (-1, 0., 0., 3.) == pytest.approx(sr._get_base_coeffs()) + sr = s.rotate((-90, 90, 0.0)) + assert (-1, 0.0, 0.0, 3.0) == pytest.approx(sr._get_base_coeffs()) # Make sure repr works repr(s) def test_zplane(): - s = openmc.ZPlane(z0=3.) - assert s.z0 == 3. + s = openmc.ZPlane(z0=3.0) + assert s.z0 == 3.0 # Check bounding box ll, ur = (+s).bounding_box - assert ll == pytest.approx((-np.inf, -np.inf, 3.)) + assert ll == pytest.approx((-np.inf, -np.inf, 3.0)) assert np.all(np.isinf(ur)) ll, ur = (-s).bounding_box - assert ur == pytest.approx((np.inf, np.inf, 3.)) + assert ur == pytest.approx((np.inf, np.inf, 3.0)) assert np.all(np.isinf(ll)) # __contains__ on associated half-spaces @@ -160,7 +160,7 @@ def test_zplane(): assert (-2, 1, -10) not in +s # evaluate method - assert s.evaluate((0., 0., 10.)) == pytest.approx(7.) + assert s.evaluate((0.0, 0.0, 10.0)) == pytest.approx(7.0) # translate method st = s.translate((0.0, 0.0, 1.0)) @@ -168,11 +168,11 @@ def test_zplane(): # rotate method # make sure rotating around z axis does nothing to coefficients - sr = s.rotate((0., 0., 123), order='zxy') + sr = s.rotate((0.0, 0.0, 123), order="zxy") assert s._get_base_coeffs() == pytest.approx(sr._get_base_coeffs()) # rotate around x by -90 deg and y by 90 deg should give negative x-plane - sr = s.rotate((-90, 0., 90.)) - assert (-1., 0., 0., 3.) == pytest.approx(sr._get_base_coeffs()) + sr = s.rotate((-90, 0.0, 90.0)) + assert (-1.0, 0.0, 0.0, 3.0) == pytest.approx(sr._get_base_coeffs()) # Make sure repr works repr(s) @@ -197,16 +197,16 @@ def test_cylinder(): # |(p - p1) ⨯ (p - p2)|^2 / |p2 - p1|^2 - r^2 p1 = s._origin p2 = p1 + s._axis - perp = np.array((1, -2, 1))*(1 / s._axis) + perp = np.array((1, -2, 1)) * (1 / s._axis) divisor = np.linalg.norm(p2 - p1) - pin = p1 + 5*s._axis # point inside cylinder - pout = np.array((4., 0., 2.5)) # point outside the cylinder - pon = p1 + s.r*perp / np.linalg.norm(perp) # point on cylinder + pin = p1 + 5 * s._axis # point inside cylinder + pout = np.array((4.0, 0.0, 2.5)) # point outside the cylinder + pon = p1 + s.r * perp / np.linalg.norm(perp) # point on cylinder for p, fn in zip((pin, pout, pon), (np.less, np.greater, np.isclose)): c1 = np.linalg.norm(np.cross(p - p1, p - p2)) / divisor - val = c1*c1 - s.r*s.r + val = c1 * c1 - s.r * s.r p_eval = s.evaluate(p) - assert fn(p_eval, 0.) + assert fn(p_eval, 0.0) assert p_eval == pytest.approx(val) # translate method @@ -244,11 +244,11 @@ def test_xcylinder(): assert np.all(np.isinf(ll)) assert np.all(np.isinf(ur)) ll, ur = (-s).bounding_box - assert ll == pytest.approx((-np.inf, y-r, z-r)) - assert ur == pytest.approx((np.inf, y+r, z+r)) + assert ll == pytest.approx((-np.inf, y - r, z - r)) + assert ur == pytest.approx((np.inf, y + r, z + r)) # evaluate method - assert s.evaluate((0, y, z)) == pytest.approx(-r**2) + assert s.evaluate((0, y, z)) == pytest.approx(-(r**2)) # translate method st = s.translate((1.0, 1.0, 1.0)) @@ -270,8 +270,8 @@ def test_xcylinder(): def test_periodic(): - x = openmc.XPlane(boundary_type='periodic') - y = openmc.YPlane(boundary_type='periodic') + x = openmc.XPlane(boundary_type="periodic") + y = openmc.YPlane(boundary_type="periodic") x.periodic_surface = y assert y.periodic_surface == x with pytest.raises(TypeError): @@ -290,11 +290,11 @@ def test_ycylinder(): assert np.all(np.isinf(ll)) assert np.all(np.isinf(ur)) ll, ur = (-s).bounding_box - assert ll == pytest.approx((x-r, -np.inf, z-r)) - assert ur == pytest.approx((x+r, np.inf, z+r)) + assert ll == pytest.approx((x - r, -np.inf, z - r)) + assert ur == pytest.approx((x + r, np.inf, z + r)) # evaluate method - assert s.evaluate((x, 0, z)) == pytest.approx(-r**2) + assert s.evaluate((x, 0, z)) == pytest.approx(-(r**2)) # translate method st = s.translate((1.0, 1.0, 1.0)) @@ -327,11 +327,11 @@ def test_zcylinder(): assert np.all(np.isinf(ll)) assert np.all(np.isinf(ur)) ll, ur = (-s).bounding_box - assert ll == pytest.approx((x-r, y-r, -np.inf)) - assert ur == pytest.approx((x+r, y+r, np.inf)) + assert ll == pytest.approx((x - r, y - r, -np.inf)) + assert ur == pytest.approx((x + r, y + r, np.inf)) # evaluate method - assert s.evaluate((x, y, 0)) == pytest.approx(-r**2) + assert s.evaluate((x, y, 0)) == pytest.approx(-(r**2)) # translate method st = s.translate((1.0, 1.0, 1.0)) @@ -365,11 +365,11 @@ def test_sphere(): assert np.all(np.isinf(ll)) assert np.all(np.isinf(ur)) ll, ur = (-s).bounding_box - assert ll == pytest.approx((x-r, y-r, z-r)) - assert ur == pytest.approx((x+r, y+r, z+r)) + assert ll == pytest.approx((x - r, y - r, z - r)) + assert ur == pytest.approx((x + r, y + r, z + r)) # evaluate method - assert s.evaluate((x, y, z)) == pytest.approx(-r**2) + assert s.evaluate((x, y, z)) == pytest.approx(-(r**2)) # translate method st = s.translate((1.0, 1.0, 1.0)) @@ -452,15 +452,15 @@ def test_cone(): # point inside p1 = s._origin d = s._axis - perp = np.array((1, -2, 1))*(1 / d) + perp = np.array((1, -2, 1)) * (1 / d) perp /= np.linalg.norm(perp) - pin = p1 + 5*d # point inside cone - pout = p1 + 3.2*perp # point outside cone - pon = p1 + 3.2*d + 3.2*math.sqrt(s.r2)*perp # point on cone + pin = p1 + 5 * d # point inside cone + pout = p1 + 3.2 * perp # point outside cone + pon = p1 + 3.2 * d + 3.2 * math.sqrt(s.r2) * perp # point on cone for p, fn in zip((pin, pout, pon), (np.less, np.greater, np.isclose)): - val = np.sum((p - p1)**2) / (1 + s.r2) - np.sum((d @ (p - p1))**2) + val = np.sum((p - p1) ** 2) / (1 + s.r2) - np.sum((d @ (p - p1)) ** 2) p_eval = s.evaluate(p) - assert fn(p_eval, 0.) + assert fn(p_eval, 0.0) assert p_eval == pytest.approx(val) # translate method @@ -507,28 +507,28 @@ def test_zcone(): def test_quadric(): # Make a sphere from a quadric r = 10.0 - coeffs = {'a': 1, 'b': 1, 'c': 1, 'k': -r**2} + coeffs = {"a": 1, "b": 1, "c": 1, "k": -(r**2)} s = openmc.Quadric(**coeffs) - assert s.a == coeffs['a'] - assert s.b == coeffs['b'] - assert s.c == coeffs['c'] - assert s.k == coeffs['k'] + assert s.a == coeffs["a"] + assert s.b == coeffs["b"] + assert s.c == coeffs["c"] + assert s.k == coeffs["k"] assert openmc.Sphere(r=10).is_equal(s) # All other coeffs should be zero - for coeff in ('d', 'e', 'f', 'g', 'h', 'j'): + for coeff in ("d", "e", "f", "g", "h", "j"): assert getattr(s, coeff) == 0.0 # Check bounding box assert_infinite_bb(s) # evaluate method - assert s.evaluate((0., 0., 0.)) == pytest.approx(coeffs['k']) - assert s.evaluate((1., 1., 1.)) == pytest.approx(3 + coeffs['k']) + assert s.evaluate((0.0, 0.0, 0.0)) == pytest.approx(coeffs["k"]) + assert s.evaluate((1.0, 1.0, 1.0)) == pytest.approx(3 + coeffs["k"]) # translate method st = s.translate((1.0, 1.0, 1.0)) - for coeff in 'abcdef': + for coeff in "abcdef": assert getattr(s, coeff) == getattr(st, coeff) assert (st.g, st.h, st.j) == (-2, -2, -2) assert st.k == s.k + 3 @@ -559,7 +559,7 @@ def test_cylinder_from_points(): # Points further along the line should be inside cylinder as well t = uniform(-100.0, 100.0) - p = p1 + t*(p2 - p1) + p = p1 + t * (p2 - p1) assert p in -s # Check that points outside cylinder are in positive half-space and @@ -567,13 +567,13 @@ def test_cylinder_from_points(): # that includes the cylinder's axis, finding the normal to the plane, # and using it to find a point slightly more/less than one radius away # from the axis. - plane = openmc.Plane.from_points(p1, p2, (0., 0., 0.)) + plane = openmc.Plane.from_points(p1, p2, (0.0, 0.0, 0.0)) n = np.array([plane.a, plane.b, plane.c]) n /= np.linalg.norm(n) - assert p1 + 1.1*r*n in +s - assert p2 + 1.1*r*n in +s - assert p1 + 0.9*r*n in -s - assert p2 + 0.9*r*n in -s + assert p1 + 1.1 * r * n in +s + assert p2 + 1.1 * r * n in +s + assert p1 + 0.9 * r * n in -s + assert p2 + 0.9 * r * n in -s def test_cylinder_from_points_axis(): @@ -581,30 +581,30 @@ def test_cylinder_from_points_axis(): # (x - 3)^2 + (y - 4)^2 = 2^2 # x^2 + y^2 - 6x - 8y + 21 = 0 - s = openmc.Cylinder.from_points((3., 4., 0.), (3., 4., 1.), 2.) + s = openmc.Cylinder.from_points((3.0, 4.0, 0.0), (3.0, 4.0, 1.0), 2.0) a, b, c, d, e, f, g, h, j, k = s._get_base_coeffs() - assert (a, b, c) == pytest.approx((1., 1., 0.)) - assert (d, e, f) == pytest.approx((0., 0., 0.)) - assert (g, h, j) == pytest.approx((-6., -8., 0.)) - assert k == pytest.approx(21.) + assert (a, b, c) == pytest.approx((1.0, 1.0, 0.0)) + assert (d, e, f) == pytest.approx((0.0, 0.0, 0.0)) + assert (g, h, j) == pytest.approx((-6.0, -8.0, 0.0)) + assert k == pytest.approx(21.0) # (y + 7)^2 + (z - 1)^2 = 3^2 # y^2 + z^2 + 14y - 2z + 41 = 0 - s = openmc.Cylinder.from_points((0., -7, 1.), (1., -7., 1.), 3.) + s = openmc.Cylinder.from_points((0.0, -7, 1.0), (1.0, -7.0, 1.0), 3.0) a, b, c, d, e, f, g, h, j, k = s._get_base_coeffs() - assert (a, b, c) == pytest.approx((0., 1., 1.)) - assert (d, e, f) == pytest.approx((0., 0., 0.)) - assert (g, h, j) == pytest.approx((0., 14., -2.)) - assert k == 41. + assert (a, b, c) == pytest.approx((0.0, 1.0, 1.0)) + assert (d, e, f) == pytest.approx((0.0, 0.0, 0.0)) + assert (g, h, j) == pytest.approx((0.0, 14.0, -2.0)) + assert k == 41.0 # (x - 2)^2 + (z - 5)^2 = 4^2 # x^2 + z^2 - 4x - 10z + 13 = 0 - s = openmc.Cylinder.from_points((2., 0., 5.), (2., 1., 5.), 4.) + s = openmc.Cylinder.from_points((2.0, 0.0, 5.0), (2.0, 1.0, 5.0), 4.0) a, b, c, d, e, f, g, h, j, k = s._get_base_coeffs() - assert (a, b, c) == pytest.approx((1., 0., 1.)) - assert (d, e, f) == pytest.approx((0., 0., 0.)) - assert (g, h, j) == pytest.approx((-4., 0., -10.)) - assert k == pytest.approx(13.) + assert (a, b, c) == pytest.approx((1.0, 0.0, 1.0)) + assert (d, e, f) == pytest.approx((0.0, 0.0, 0.0)) + assert (g, h, j) == pytest.approx((-4.0, 0.0, -10.0)) + assert k == pytest.approx(13.0) def torus_common(center, R, r1, r2, cls): @@ -631,14 +631,19 @@ def torus_common(center, R, r1, r2, cls): assert st.c == s.c # trivial rotations - for rotation in [(0., 0., 0.), (180., 0., 0.), (0., 180., 0.), (0., 0., 180.)]: + for rotation in [ + (0.0, 0.0, 0.0), + (180.0, 0.0, 0.0), + (0.0, 180.0, 0.0), + (0.0, 0.0, 180.0), + ]: sr = s.rotate(rotation) assert type(sr) == type(s) assert (sr.a, sr.b, sr.c) == (s.a, s.b, s.c) # can't do generic rotate at present with pytest.raises(NotImplementedError): - s.rotate((0., 45., 0.)) + s.rotate((0.0, 45.0, 0.0)) # Check bounding box ll, ur = (+s).bounding_box @@ -683,9 +688,9 @@ def test_xtorus(): assert s.evaluate((x + r1 + 0.01, y + R, z)) > 0.0 # rotation - sr = s.rotate((0., 0., 90.)) + sr = s.rotate((0.0, 0.0, 90.0)) assert isinstance(sr, openmc.YTorus) - sr = s.rotate((0., 90., 0.)) + sr = s.rotate((0.0, 90.0, 0.0)) assert isinstance(sr, openmc.ZTorus) @@ -716,9 +721,9 @@ def test_ytorus(): assert s.evaluate((x + R, y + r1 + 0.01, z)) > 0.0 # rotation - sr = s.rotate((90., 0., 0.)) + sr = s.rotate((90.0, 0.0, 0.0)) assert isinstance(sr, openmc.ZTorus) - sr = s.rotate((0., 0., 90.)) + sr = s.rotate((0.0, 0.0, 90.0)) assert isinstance(sr, openmc.XTorus) @@ -749,7 +754,7 @@ def test_ztorus(): assert s.evaluate((x, y + R, z + r1 + 0.01)) > 0.0 # rotation - sr = s.rotate((90., 0., 0.)) + sr = s.rotate((90.0, 0.0, 0.0)) assert isinstance(sr, openmc.YTorus) - sr = s.rotate((0., 90., 0.)) + sr = s.rotate((0.0, 90.0, 0.0)) assert isinstance(sr, openmc.XTorus) diff --git a/tests/unit_tests/test_surface_composite.py b/tests/unit_tests/test_surface_composite.py index da7ffbcc470..8fd5fdbcd18 100644 --- a/tests/unit_tests/test_surface_composite.py +++ b/tests/unit_tests/test_surface_composite.py @@ -6,14 +6,13 @@ def test_rectangular_parallelepiped(): - xmin = uniform(-5., 5.) - xmax = xmin + uniform(0., 5.) - ymin = uniform(-5., 5.) - ymax = ymin + uniform(0., 5.) - zmin = uniform(-5., 5.) - zmax = zmin + uniform(0., 5.) - s = openmc.model.RectangularParallelepiped( - xmin, xmax, ymin, ymax, zmin, zmax) + xmin = uniform(-5.0, 5.0) + xmax = xmin + uniform(0.0, 5.0) + ymin = uniform(-5.0, 5.0) + ymax = ymin + uniform(0.0, 5.0) + zmin = uniform(-5.0, 5.0) + zmax = zmin + uniform(0.0, 5.0) + s = openmc.model.RectangularParallelepiped(xmin, xmax, ymin, ymax, zmin, zmax) assert isinstance(s.xmin, openmc.XPlane) assert isinstance(s.xmax, openmc.XPlane) assert isinstance(s.ymin, openmc.YPlane) @@ -22,11 +21,11 @@ def test_rectangular_parallelepiped(): assert isinstance(s.zmax, openmc.ZPlane) # Make sure boundary condition propagates - s.boundary_type = 'reflective' - assert s.boundary_type == 'reflective' - for axis in 'xyz': - assert getattr(s, '{}min'.format(axis)).boundary_type == 'reflective' - assert getattr(s, '{}max'.format(axis)).boundary_type == 'reflective' + s.boundary_type = "reflective" + assert s.boundary_type == "reflective" + for axis in "xyz": + assert getattr(s, "{}min".format(axis)).boundary_type == "reflective" + assert getattr(s, "{}max".format(axis)).boundary_type == "reflective" # Check bounding box ll, ur = (+s).bounding_box @@ -37,11 +36,11 @@ def test_rectangular_parallelepiped(): assert ll == pytest.approx((xmin, ymin, zmin)) # __contains__ on associated half-spaces - assert (xmin - 0.1, 0., 0.) in +s - assert (xmin - 0.1, 0., 0.) not in -s + assert (xmin - 0.1, 0.0, 0.0) in +s + assert (xmin - 0.1, 0.0, 0.0) not in -s dx, dy, dz = xmax - xmin, ymax - ymin, zmax - zmin - assert (xmin + dx/2, ymin + dy/2, zmin + dz/2) in -s - assert (xmin + dx/2, ymin + dy/2, zmin + dz/2) not in +s + assert (xmin + dx / 2, ymin + dy / 2, zmin + dz / 2) in -s + assert (xmin + dx / 2, ymin + dy / 2, zmin + dz / 2) not in +s # translate method t = uniform(-5.0, 5.0) @@ -55,30 +54,36 @@ def test_rectangular_parallelepiped(): @pytest.mark.parametrize( - "axis, indices", [ + "axis, indices", + [ ("X", [0, 1, 2]), ("Y", [1, 2, 0]), ("Z", [2, 0, 1]), - ] + ], ) def test_right_circular_cylinder(axis, indices): x, y, z = 1.0, -2.5, 3.0 h, r = 5.0, 3.0 s = openmc.model.RightCircularCylinder((x, y, z), h, r, axis=axis.lower()) - s_r = openmc.model.RightCircularCylinder((x, y, z), h, r, axis=axis.lower(), - upper_fillet_radius=1.6, - lower_fillet_radius=1.6) + s_r = openmc.model.RightCircularCylinder( + (x, y, z), + h, + r, + axis=axis.lower(), + upper_fillet_radius=1.6, + lower_fillet_radius=1.6, + ) for s in (s, s_r): assert isinstance(s.cyl, getattr(openmc, axis + "Cylinder")) assert isinstance(s.top, getattr(openmc, axis + "Plane")) assert isinstance(s.bottom, getattr(openmc, axis + "Plane")) # Make sure boundary condition propagates - s.boundary_type = 'reflective' - assert s.boundary_type == 'reflective' - assert s.cyl.boundary_type == 'reflective' - assert s.bottom.boundary_type == 'reflective' - assert s.top.boundary_type == 'reflective' + s.boundary_type = "reflective" + assert s.boundary_type == "reflective" + assert s.cyl.boundary_type == "reflective" + assert s.bottom.boundary_type == "reflective" + assert s.top.boundary_type == "reflective" # Check bounding box ll, ur = (+s).bounding_box @@ -89,10 +94,10 @@ def test_right_circular_cylinder(axis, indices): assert ur == pytest.approx((x, y, z) + np.roll([h, r, r], indices[0])) # __contains__ on associated half-spaces - point_pos = (x, y, z) + np.roll([h/2, r+1, r+1], indices[0]) + point_pos = (x, y, z) + np.roll([h / 2, r + 1, r + 1], indices[0]) assert point_pos in +s assert point_pos not in -s - point_neg = (x, y, z) + np.roll([h/2, 0, 0], indices[0]) + point_neg = (x, y, z) + np.roll([h / 2, 0, 0], indices[0]) assert point_neg in -s assert point_neg not in +s @@ -108,29 +113,30 @@ def test_right_circular_cylinder(axis, indices): @pytest.mark.parametrize( - "axis, point_pos, point_neg, ll_true", [ - ("X", (8., 0., 0.), (12., 0., 0.), (10., -np.inf, -np.inf)), - ("Y", (10., -2., 0.), (10., 2., 0.), (-np.inf, 0., -np.inf)), - ("Z", (10., 0., -3.), (10., 0., 3.), (-np.inf, -np.inf, 0.)) - ] + "axis, point_pos, point_neg, ll_true", + [ + ("X", (8.0, 0.0, 0.0), (12.0, 0.0, 0.0), (10.0, -np.inf, -np.inf)), + ("Y", (10.0, -2.0, 0.0), (10.0, 2.0, 0.0), (-np.inf, 0.0, -np.inf)), + ("Z", (10.0, 0.0, -3.0), (10.0, 0.0, 3.0), (-np.inf, -np.inf, 0.0)), + ], ) def test_cone_one_sided(axis, point_pos, point_neg, ll_true): cone_oneside = getattr(openmc.model, axis + "ConeOneSided") cone_twoside = getattr(openmc, axis + "Cone") plane = getattr(openmc, axis + "Plane") - x, y, z = 10., 0., 0. - r2 = 4. + x, y, z = 10.0, 0.0, 0.0 + r2 = 4.0 s = cone_oneside(x, y, z, r2, True) assert isinstance(s.cone, cone_twoside) assert isinstance(s.plane, plane) assert s.up # Make sure boundary condition propagates - s.boundary_type = 'reflective' - assert s.boundary_type == 'reflective' - assert s.cone.boundary_type == 'reflective' - assert s.plane.boundary_type == 'transmission' + s.boundary_type = "reflective" + assert s.boundary_type == "reflective" + assert s.cone.boundary_type == "reflective" + assert s.plane.boundary_type == "transmission" # Check bounding box ll, ur = (+s).bounding_box @@ -158,36 +164,37 @@ def test_cone_one_sided(axis, point_pos, point_neg, ll_true): @pytest.mark.parametrize( - "axis, indices, center", [ - ("X", [2, 0, 1], (0., 0.)), - ("Y", [0, 2, 1], (0., 0.)), - ("Z", [0, 1, 2], (0., 0.)), - ("X", [2, 0, 1], (10., 5.)), - ("Y", [0, 2, 1], (10., 5.)), - ("Z", [0, 1, 2], (10., 5.)), - - ] + "axis, indices, center", + [ + ("X", [2, 0, 1], (0.0, 0.0)), + ("Y", [0, 2, 1], (0.0, 0.0)), + ("Z", [0, 1, 2], (0.0, 0.0)), + ("X", [2, 0, 1], (10.0, 5.0)), + ("Y", [0, 2, 1], (10.0, 5.0)), + ("Z", [0, 1, 2], (10.0, 5.0)), + ], ) def test_cylinder_sector(axis, indices, center): c1, c2 = center r1, r2 = 0.5, 1.5 d = (r2 - r1) / 2 - phi1 = -60. + phi1 = -60.0 phi2 = 60 - s = openmc.model.CylinderSector(r1, r2, phi1, phi2, center=center, - axis=axis.lower()) + s = openmc.model.CylinderSector( + r1, r2, phi1, phi2, center=center, axis=axis.lower() + ) assert isinstance(s.outer_cyl, getattr(openmc, axis + "Cylinder")) assert isinstance(s.inner_cyl, getattr(openmc, axis + "Cylinder")) assert isinstance(s.plane1, openmc.Plane) assert isinstance(s.plane2, openmc.Plane) # Make sure boundary condition propagates - s.boundary_type = 'reflective' - assert s.boundary_type == 'reflective' - assert s.outer_cyl.boundary_type == 'reflective' - assert s.inner_cyl.boundary_type == 'reflective' - assert s.plane1.boundary_type == 'reflective' - assert s.plane2.boundary_type == 'reflective' + s.boundary_type = "reflective" + assert s.boundary_type == "reflective" + assert s.outer_cyl.boundary_type == "reflective" + assert s.inner_cyl.boundary_type == "reflective" + assert s.plane1.boundary_type == "reflective" + assert s.plane2.boundary_type == "reflective" # Check bounding box ll, ur = (+s).bounding_box @@ -229,15 +236,12 @@ def test_cylinder_sector(axis, indices, center): def test_cylinder_sector_from_theta_alpha(): r1, r2 = 0.5, 1.5 d = (r2 - r1) / 2 - theta = 120. - alpha = -60. + theta = 120.0 + alpha = -60.0 theta1 = alpha theta2 = alpha + theta s = openmc.model.CylinderSector(r1, r2, theta1, theta2) - s_alt = openmc.model.CylinderSector.from_theta_alpha(r1, - r2, - theta, - alpha) + s_alt = openmc.model.CylinderSector.from_theta_alpha(r1, r2, theta, alpha) # Check that the angles are correct assert s.plane1.coefficients == s_alt.plane1.coefficients @@ -253,18 +257,19 @@ def test_cylinder_sector_from_theta_alpha(): @pytest.mark.parametrize( - "axis, plane_tb, plane_lr, axis_idx", [ + "axis, plane_tb, plane_lr, axis_idx", + [ ("x", "Z", "Y", 0), ("y", "Z", "X", 1), ("z", "Y", "X", 2), - ] + ], ) def test_isogonal_octagon(axis, plane_tb, plane_lr, axis_idx): - center = np.array([0., 0.]) + center = np.array([0.0, 0.0]) point_pos = np.array([0.8, 0.8]) point_neg = np.array([0.7, 0.7]) - r1 = 1. - r2 = 1. + r1 = 1.0 + r2 = 1.0 plane_top_bottom = getattr(openmc, plane_tb + "Plane") plane_left_right = getattr(openmc, plane_lr + "Plane") s = openmc.model.IsogonalOctagon(center, r1, r2, axis=axis) @@ -278,16 +283,16 @@ def test_isogonal_octagon(axis, plane_tb, plane_lr, axis_idx): assert isinstance(s.lower_left, openmc.Plane) # Make sure boundary condition propagates - s.boundary_type = 'reflective' - assert s.boundary_type == 'reflective' - assert s.top.boundary_type == 'reflective' - assert s.bottom.boundary_type == 'reflective' - assert s.right.boundary_type == 'reflective' - assert s.left.boundary_type == 'reflective' - assert s.upper_right.boundary_type == 'reflective' - assert s.lower_right.boundary_type == 'reflective' - assert s.lower_left.boundary_type == 'reflective' - assert s.upper_left.boundary_type == 'reflective' + s.boundary_type = "reflective" + assert s.boundary_type == "reflective" + assert s.top.boundary_type == "reflective" + assert s.bottom.boundary_type == "reflective" + assert s.right.boundary_type == "reflective" + assert s.left.boundary_type == "reflective" + assert s.upper_right.boundary_type == "reflective" + assert s.lower_right.boundary_type == "reflective" + assert s.lower_left.boundary_type == "reflective" + assert s.upper_left.boundary_type == "reflective" # Check bounding box center = np.insert(center, axis_idx, np.inf) @@ -319,9 +324,9 @@ def test_isogonal_octagon(axis, plane_tb, plane_lr, axis_idx): # Check invalid r1, r2 combinations with pytest.raises(ValueError): - openmc.model.IsogonalOctagon(center, r1=1.0, r2=10.) + openmc.model.IsogonalOctagon(center, r1=1.0, r2=10.0) with pytest.raises(ValueError): - openmc.model.IsogonalOctagon(center, r1=10., r2=1.) + openmc.model.IsogonalOctagon(center, r1=10.0, r2=1.0) # Make sure repr works repr(s) @@ -329,19 +334,23 @@ def test_isogonal_octagon(axis, plane_tb, plane_lr, axis_idx): def test_polygon(): # define a 5 pointed star centered on 1, 1 - star = np.array([[1. , 2. ], - [0.70610737, 1.4045085 ], - [0.04894348, 1.30901699], - [0.52447174, 0.8454915 ], - [0.41221475, 0.19098301], - [1. , 0.5 ], - [1.58778525, 0.19098301], - [1.47552826, 0.8454915 ], - [1.95105652, 1.30901699], - [1.29389263, 1.4045085 ], - [1. , 2. ]]) - points_in = [(1, 1, 0), (0, 1, 1), (1, 0, 1), (.707, .707, 1)] - for i, basis in enumerate(('xy', 'yz', 'xz', 'rz')): + star = np.array( + [ + [1.0, 2.0], + [0.70610737, 1.4045085], + [0.04894348, 1.30901699], + [0.52447174, 0.8454915], + [0.41221475, 0.19098301], + [1.0, 0.5], + [1.58778525, 0.19098301], + [1.47552826, 0.8454915], + [1.95105652, 1.30901699], + [1.29389263, 1.4045085], + [1.0, 2.0], + ] + ) + points_in = [(1, 1, 0), (0, 1, 1), (1, 0, 1), (0.707, 0.707, 1)] + for i, basis in enumerate(("xy", "yz", "xz", "rz")): star_poly = openmc.model.Polygon(star, basis=basis) assert points_in[i] in -star_poly assert any([points_in[i] in reg for reg in star_poly.regions]) @@ -357,75 +366,95 @@ def test_polygon(): # check invalid Polygon input points # duplicate points not just at start and end - rz_points = np.array([[6.88, 3.02], - [6.88, 2.72], - [6.88, 3.02], - [7.63, 0.0], - [5.75, 0.0], - [5.75, 1.22], - [7.63, 0.0], - [6.30, 1.22], - [6.30, 3.02], - [6.88, 3.02]]) + rz_points = np.array( + [ + [6.88, 3.02], + [6.88, 2.72], + [6.88, 3.02], + [7.63, 0.0], + [5.75, 0.0], + [5.75, 1.22], + [7.63, 0.0], + [6.30, 1.22], + [6.30, 3.02], + [6.88, 3.02], + ] + ) with pytest.raises(ValueError): openmc.model.Polygon(rz_points) # segment traces back on previous segment - rz_points = np.array([[6.88, 3.02], - [6.88, 2.72], - [6.88, 2.32], - [6.88, 2.52], - [7.63, 0.0], - [5.75, 0.0], - [6.75, 0.0], - [5.75, 1.22], - [6.30, 1.22], - [6.30, 3.02], - [6.88, 3.02]]) + rz_points = np.array( + [ + [6.88, 3.02], + [6.88, 2.72], + [6.88, 2.32], + [6.88, 2.52], + [7.63, 0.0], + [5.75, 0.0], + [6.75, 0.0], + [5.75, 1.22], + [6.30, 1.22], + [6.30, 3.02], + [6.88, 3.02], + ] + ) with pytest.raises(ValueError): openmc.model.Polygon(rz_points) # segments intersect (line-line) - rz_points = np.array([[6.88, 3.02], - [5.88, 2.32], - [7.63, 0.0], - [5.75, 0.0], - [5.75, 1.22], - [6.30, 1.22], - [6.30, 3.02], - [6.88, 3.02]]) + rz_points = np.array( + [ + [6.88, 3.02], + [5.88, 2.32], + [7.63, 0.0], + [5.75, 0.0], + [5.75, 1.22], + [6.30, 1.22], + [6.30, 3.02], + [6.88, 3.02], + ] + ) with pytest.raises(ValueError): openmc.model.Polygon(rz_points) # segments intersect (line-point) - rz_points = np.array([[6.88, 3.02], - [6.3, 2.32], - [7.63, 0.0], - [5.75, 0.0], - [5.75, 1.22], - [6.30, 1.22], - [6.30, 3.02], - [6.88, 3.02]]) + rz_points = np.array( + [ + [6.88, 3.02], + [6.3, 2.32], + [7.63, 0.0], + [5.75, 0.0], + [5.75, 1.22], + [6.30, 1.22], + [6.30, 3.02], + [6.88, 3.02], + ] + ) with pytest.raises(ValueError): openmc.model.Polygon(rz_points) # Test "M" shaped polygon - points = np.array([[8.5151581, -17.988337], - [10.381711000000001, -17.988337], - [12.744357, -24.288728000000003], - [15.119406000000001, -17.988337], - [16.985959, -17.988337], - [16.985959, -27.246687], - [15.764328, -27.246687], - [15.764328, -19.116951], - [13.376877, -25.466951], - [12.118039, -25.466951], - [9.7305877, -19.116951], - [9.7305877, -27.246687], - [8.5151581, -27.246687]]) + points = np.array( + [ + [8.5151581, -17.988337], + [10.381711000000001, -17.988337], + [12.744357, -24.288728000000003], + [15.119406000000001, -17.988337], + [16.985959, -17.988337], + [16.985959, -27.246687], + [15.764328, -27.246687], + [15.764328, -19.116951], + [13.376877, -25.466951], + [12.118039, -25.466951], + [9.7305877, -19.116951], + [9.7305877, -27.246687], + [8.5151581, -27.246687], + ] + ) # Test points inside and outside by using offset method - m_polygon = openmc.model.Polygon(points, basis='xz') + m_polygon = openmc.model.Polygon(points, basis="xz") inner_pts = m_polygon.offset(-0.1).points assert all([(pt[0], 0, pt[1]) in -m_polygon for pt in inner_pts]) outer_pts = m_polygon.offset(0.1).points @@ -438,32 +467,32 @@ def test_polygon(): @pytest.mark.parametrize("axis", ["x", "y", "z"]) def test_cruciform_prism(axis): - center = x0, y0 = (3., 4.) - distances = [2., 3., 5.] + center = x0, y0 = (3.0, 4.0) + distances = [2.0, 3.0, 5.0] s = openmc.model.CruciformPrism(distances, center, axis=axis) - if axis == 'x': + if axis == "x": i1, i2 = 1, 2 - elif axis == 'y': + elif axis == "y": i1, i2 = 0, 2 - elif axis == 'z': + elif axis == "z": i1, i2 = 0, 1 plane_cls = (openmc.XPlane, openmc.YPlane, openmc.ZPlane) # Check type of surfaces for i in range(3): - assert isinstance(getattr(s, f'hmin{i}'), plane_cls[i1]) - assert isinstance(getattr(s, f'hmax{i}'), plane_cls[i1]) - assert isinstance(getattr(s, f'vmin{i}'), plane_cls[i2]) - assert isinstance(getattr(s, f'vmax{i}'), plane_cls[i2]) + assert isinstance(getattr(s, f"hmin{i}"), plane_cls[i1]) + assert isinstance(getattr(s, f"hmax{i}"), plane_cls[i1]) + assert isinstance(getattr(s, f"vmin{i}"), plane_cls[i2]) + assert isinstance(getattr(s, f"vmax{i}"), plane_cls[i2]) # Make sure boundary condition propagates - s.boundary_type = 'reflective' + s.boundary_type = "reflective" for i in range(3): - assert getattr(s, f'hmin{i}').boundary_type == 'reflective' - assert getattr(s, f'hmax{i}').boundary_type == 'reflective' - assert getattr(s, f'vmin{i}').boundary_type == 'reflective' - assert getattr(s, f'vmax{i}').boundary_type == 'reflective' + assert getattr(s, f"hmin{i}").boundary_type == "reflective" + assert getattr(s, f"hmax{i}").boundary_type == "reflective" + assert getattr(s, f"vmin{i}").boundary_type == "reflective" + assert getattr(s, f"vmax{i}").boundary_type == "reflective" # Check bounding box ll, ur = (+s).bounding_box @@ -510,15 +539,15 @@ def test_box(): a3 = (0.0, 0.0, 5.0) s = openmc.model.OrthogonalBox(v, a1, a2, a3) for num in (1, 2, 3): - assert isinstance(getattr(s, f'ax{num}_min'), openmc.Plane) - assert isinstance(getattr(s, f'ax{num}_max'), openmc.Plane) + assert isinstance(getattr(s, f"ax{num}_min"), openmc.Plane) + assert isinstance(getattr(s, f"ax{num}_max"), openmc.Plane) # Make sure boundary condition propagates - s.boundary_type = 'reflective' - assert s.boundary_type == 'reflective' + s.boundary_type = "reflective" + assert s.boundary_type == "reflective" for num in (1, 2, 3): - assert getattr(s, f'ax{num}_min').boundary_type == 'reflective' - assert getattr(s, f'ax{num}_max').boundary_type == 'reflective' + assert getattr(s, f"ax{num}_min").boundary_type == "reflective" + assert getattr(s, f"ax{num}_max").boundary_type == "reflective" # Check bounding box ll, ur = (+s).bounding_box @@ -529,37 +558,37 @@ def test_box(): assert ur[2] == pytest.approx(2.5) # __contains__ on associated half-spaces - assert (0., 0., 0.) in -s - assert (-2., 0., 0.) not in -s - assert (0., 0.9, 0.) in -s - assert (0., 0., -3.) in +s - assert (0., 0., 3.) in +s + assert (0.0, 0.0, 0.0) in -s + assert (-2.0, 0.0, 0.0) not in -s + assert (0.0, 0.9, 0.0) in -s + assert (0.0, 0.0, -3.0) in +s + assert (0.0, 0.0, 3.0) in +s # translate method - s_t = s.translate((1., 1., 0.)) - assert (-0.01, 0., 0.) in +s_t - assert (0.01, 0., 0.) in -s_t + s_t = s.translate((1.0, 1.0, 0.0)) + assert (-0.01, 0.0, 0.0) in +s_t + assert (0.01, 0.0, 0.0) in -s_t # Make sure repr works repr(s) # Version with infinite 3rd dimension s = openmc.model.OrthogonalBox(v, a1, a2) - assert not hasattr(s, 'ax3_min') - assert not hasattr(s, 'ax3_max') + assert not hasattr(s, "ax3_min") + assert not hasattr(s, "ax3_max") ll, ur = (-s).bounding_box assert np.all(np.isinf(ll)) assert np.all(np.isinf(ur)) - assert (0., 0., 0.) in -s - assert (-2., 0., 0.) not in -s - assert (0., 0.9, 0.) in -s - assert (0., 0., -3.) not in +s - assert (0., 0., 3.) not in +s + assert (0.0, 0.0, 0.0) in -s + assert (-2.0, 0.0, 0.0) not in -s + assert (0.0, 0.9, 0.0) in -s + assert (0.0, 0.0, -3.0) not in +s + assert (0.0, 0.0, 3.0) not in +s def test_conical_frustum(): center_base = (0.0, 0.0, -3) - axis = (0., 0., 3.) + axis = (0.0, 0.0, 3.0) r1 = 2.0 r2 = 0.5 s = openmc.model.ConicalFrustum(center_base, axis, r1, r2) @@ -568,11 +597,11 @@ def test_conical_frustum(): assert isinstance(s.plane_top, openmc.Plane) # Make sure boundary condition propagates - s.boundary_type = 'reflective' - assert s.boundary_type == 'reflective' - assert s.cone.boundary_type == 'reflective' - assert s.plane_bottom.boundary_type == 'reflective' - assert s.plane_top.boundary_type == 'reflective' + s.boundary_type = "reflective" + assert s.boundary_type == "reflective" + assert s.cone.boundary_type == "reflective" + assert s.plane_bottom.boundary_type == "reflective" + assert s.plane_top.boundary_type == "reflective" # Check bounding box ll, ur = (+s).bounding_box @@ -583,22 +612,22 @@ def test_conical_frustum(): assert ur[2] == pytest.approx(0.0) # __contains__ on associated half-spaces - assert (0., 0., -1.) in -s - assert (0., 0., -4.) not in -s - assert (0., 0., 1.) not in -s - assert (1., 1., -2.99) in -s - assert (1., 1., -0.01) in +s + assert (0.0, 0.0, -1.0) in -s + assert (0.0, 0.0, -4.0) not in -s + assert (0.0, 0.0, 1.0) not in -s + assert (1.0, 1.0, -2.99) in -s + assert (1.0, 1.0, -0.01) in +s # translate method - s_t = s.translate((1., 1., 0.)) - assert (1., 1., -0.01) in -s_t + s_t = s.translate((1.0, 1.0, 0.0)) + assert (1.0, 1.0, -0.01) in -s_t # Make sure repr works repr(s) # Denegenerate case with r1 = r2 s = openmc.model.ConicalFrustum(center_base, axis, r1, r1) - assert (1., 1., -0.01) in -s + assert (1.0, 1.0, -0.01) in -s def test_vessel(): @@ -614,13 +643,13 @@ def test_vessel(): assert isinstance(s.top, openmc.Quadric) # Make sure boundary condition propagates (but not for planes) - s.boundary_type = 'reflective' - assert s.boundary_type == 'reflective' - assert s.cyl.boundary_type == 'reflective' - assert s.bottom.boundary_type == 'reflective' - assert s.top.boundary_type == 'reflective' - assert s.plane_bottom.boundary_type == 'transmission' - assert s.plane_top.boundary_type == 'transmission' + s.boundary_type = "reflective" + assert s.boundary_type == "reflective" + assert s.cyl.boundary_type == "reflective" + assert s.bottom.boundary_type == "reflective" + assert s.top.boundary_type == "reflective" + assert s.plane_bottom.boundary_type == "transmission" + assert s.plane_top.boundary_type == "transmission" # Check bounding box ll, ur = (+s).bounding_box @@ -631,20 +660,20 @@ def test_vessel(): assert np.all(np.isinf(ur)) # __contains__ on associated half-spaces - assert (3., 2., 0.) in -s - assert (3., 2., -5.0) in -s - assert (3., 2., 5.0) in -s - assert (3., 2., -5.9) in -s - assert (3., 2., 5.9) in -s - assert (3., 2., -6.1) not in -s - assert (3., 2., 6.1) not in -s - assert (4.5, 2., 0.) in +s - assert (3., 3.2, 0.) in +s - assert (3., 2., 7.) in +s + assert (3.0, 2.0, 0.0) in -s + assert (3.0, 2.0, -5.0) in -s + assert (3.0, 2.0, 5.0) in -s + assert (3.0, 2.0, -5.9) in -s + assert (3.0, 2.0, 5.9) in -s + assert (3.0, 2.0, -6.1) not in -s + assert (3.0, 2.0, 6.1) not in -s + assert (4.5, 2.0, 0.0) in +s + assert (3.0, 3.2, 0.0) in +s + assert (3.0, 2.0, 7.0) in +s # translate method - s_t = s.translate((0., 0., 1.)) - assert (3., 2., 6.1) in -s_t + s_t = s.translate((0.0, 0.0, 1.0)) + assert (3.0, 2.0, 6.1) in -s_t # Make sure repr works repr(s) diff --git a/tests/unit_tests/test_surface_source_write.py b/tests/unit_tests/test_surface_source_write.py index 6f18d32b718..3763d5acd35 100644 --- a/tests/unit_tests/test_surface_source_write.py +++ b/tests/unit_tests/test_surface_source_write.py @@ -54,7 +54,7 @@ def model(): # Material h1 = openmc.Material(name="H1") h1.add_nuclide("H1", 1.0) - h1.set_density('g/cm3', 1e-7) + h1.set_density("g/cm3", 1e-7) # Geometry radius = 1.0 @@ -82,12 +82,13 @@ def model(): (100, 1), ], ) -def test_number_surface_source_file_created(max_particles, max_source_files, - run_in_tmpdir, model): +def test_number_surface_source_file_created( + max_particles, max_source_files, run_in_tmpdir, model +): """Check the number of surface source files written.""" model.settings.surf_source_write = { "max_particles": max_particles, - "max_source_files": max_source_files + "max_source_files": max_source_files, } model.run() should_be_numbered = max_source_files > 1 @@ -97,6 +98,7 @@ def test_number_surface_source_file_created(max_particles, max_source_files, if not should_be_numbered: assert Path("surface_source.h5").exists() + ERROR_MSG_1 = ( "A maximum number of particles needs to be specified " "using the 'max_particles' parameter to store surface " @@ -223,7 +225,9 @@ def model_dagmc(request): # ============================================================================= # Geometry # ============================================================================= - dagmc_path = Path(request.fspath).parent / "../regression_tests/dagmc/legacy/dagmc.h5m" + dagmc_path = ( + Path(request.fspath).parent / "../regression_tests/dagmc/legacy/dagmc.h5m" + ) dagmc_univ = openmc.DAGMCUniverse(dagmc_path) model.geometry = openmc.Geometry(dagmc_univ) diff --git a/tests/unit_tests/test_tallies.py b/tests/unit_tests/test_tallies.py index 54444331257..8d43c9ee13f 100644 --- a/tests/unit_tests/test_tallies.py +++ b/tests/unit_tests/test_tallies.py @@ -6,20 +6,24 @@ def test_xml_roundtrip(run_in_tmpdir): # Create a tally with all possible gizmos mesh = openmc.RegularMesh() - mesh.lower_left = (-10., -10., -10.) - mesh.upper_right = (10., 10., 10.,) + mesh.lower_left = (-10.0, -10.0, -10.0) + mesh.upper_right = ( + 10.0, + 10.0, + 10.0, + ) mesh.dimension = (5, 5, 5) mesh_filter = openmc.MeshFilter(mesh) meshborn_filter = openmc.MeshBornFilter(mesh) tally = openmc.Tally() tally.filters = [mesh_filter, meshborn_filter] - tally.nuclides = ['U235', 'I135', 'Li6'] - tally.scores = ['total', 'fission', 'heating'] + tally.nuclides = ["U235", "I135", "Li6"] + tally.scores = ["total", "fission", "heating"] tally.derivative = openmc.TallyDerivative( - variable='nuclide_density', material=1, nuclide='Li6' + variable="nuclide_density", material=1, nuclide="Li6" ) - tally.triggers = [openmc.Trigger('rel_err', 0.025)] - tally.triggers[0].scores = ['total', 'fission'] + tally.triggers = [openmc.Trigger("rel_err", 0.025)] + tally.triggers[0].scores = ["total", "fission"] tallies = openmc.Tallies([tally]) # Roundtrip through XML and make sure we get what we started with @@ -41,4 +45,4 @@ def test_xml_roundtrip(run_in_tmpdir): assert len(new_tally.triggers) == 1 assert new_tally.triggers[0].trigger_type == tally.triggers[0].trigger_type assert new_tally.triggers[0].threshold == tally.triggers[0].threshold - assert new_tally.triggers[0].scores == tally.triggers[0].scores \ No newline at end of file + assert new_tally.triggers[0].scores == tally.triggers[0].scores diff --git a/tests/unit_tests/test_tally_multiply_density.py b/tests/unit_tests/test_tally_multiply_density.py index 552d76bd52f..53abafe076f 100644 --- a/tests/unit_tests/test_tally_multiply_density.py +++ b/tests/unit_tests/test_tally_multiply_density.py @@ -6,24 +6,24 @@ def test_micro_macro_compare(run_in_tmpdir): # Create simple sphere model with H1 and H2 mat = openmc.Material() - mat.add_components({'H1': 1.0, 'H2': 1.0}) - mat.set_density('g/cm3', 1.0) - sph = openmc.Sphere(r=10.0, boundary_type='vacuum') + mat.add_components({"H1": 1.0, "H2": 1.0}) + mat.set_density("g/cm3", 1.0) + sph = openmc.Sphere(r=10.0, boundary_type="vacuum") cell = openmc.Cell(fill=mat, region=-sph) model = openmc.Model() model.geometry = openmc.Geometry([cell]) - model.settings.run_mode = 'fixed source' + model.settings.run_mode = "fixed source" model.settings.particles = 1000 model.settings.batches = 10 # Set up two reaction rate tallies, one that multplies by density and the # other that doesn't tally_macro = openmc.Tally() - tally_macro.nuclides = ['H1', 'H2', 'H3'] - tally_macro.scores = ['total', 'elastic'] + tally_macro.nuclides = ["H1", "H2", "H3"] + tally_macro.scores = ["total", "elastic"] tally_micro = openmc.Tally() - tally_micro.nuclides = ['H1', 'H2', 'H3'] - tally_micro.scores = ['total', 'elastic'] + tally_micro.nuclides = ["H1", "H2", "H3"] + tally_micro.scores = ["total", "elastic"] tally_micro.multiply_density = False model.tallies = [tally_macro, tally_micro] @@ -38,13 +38,13 @@ def test_micro_macro_compare(run_in_tmpdir): # Dividing macro by density should give micro density = mat.get_nuclide_atom_densities() - for nuc in ('H1', 'H2'): + for nuc in ("H1", "H2"): micro_derived = tally_macro.get_values(nuclides=[nuc]) / density[nuc] micro = tally_micro.get_values(nuclides=[nuc]) assert micro_derived == pytest.approx(micro) # For macro tally, H3 scores should be zero - assert np.all(tally_macro.get_values(nuclides=['H3']) == 0.0) + assert np.all(tally_macro.get_values(nuclides=["H3"]) == 0.0) # For micro tally, H3 scores should be positive - assert np.all(tally_micro.get_values(nuclides=['H3']) > 0.0) + assert np.all(tally_micro.get_values(nuclides=["H3"]) > 0.0) diff --git a/tests/unit_tests/test_temp_interp.py b/tests/unit_tests/test_temp_interp.py index 4566070cfd9..5b8b36db7c8 100644 --- a/tests/unit_tests/test_temp_interp.py +++ b/tests/unit_tests/test_temp_interp.py @@ -19,32 +19,28 @@ def make_fake_cross_section(): def isotropic_angle(E_min, E_max): return openmc.data.AngleDistribution( - [E_min, E_max], - [Uniform(-1., 1.), Uniform(-1., 1.)] + [E_min, E_max], [Uniform(-1.0, 1.0), Uniform(-1.0, 1.0)] ) def cross_section(value): - return openmc.data.Tabulated1D( - energy, - value*np.ones_like(energy) - ) + return openmc.data.Tabulated1D(energy, value * np.ones_like(energy)) temperatures = (300, 600, 900) u235_fake = openmc.data.IncidentNeutron( - 'U235', 92, 235, 0, 233.0248, [T*K_BOLTZMANN for T in temperatures] + "U235", 92, 235, 0, 233.0248, [T * K_BOLTZMANN for T in temperatures] ) # Create energy grids E_min, E_max = 1e-5, 20.0e6 energy = np.logspace(np.log10(E_min), np.log10(E_max)) for T in temperatures: - u235_fake.energy['{}K'.format(T)] = energy + u235_fake.energy["{}K".format(T)] = energy # Create elastic scattering elastic = openmc.data.Reaction(2) for T in temperatures: - elastic.xs['{}K'.format(T)] = cross_section(1.0) + elastic.xs["{}K".format(T)] = cross_section(1.0) elastic_dist = openmc.data.UncorrelatedAngleEnergy(isotropic_angle(E_min, E_max)) product = openmc.data.Product() product.distribution.append(elastic_dist) @@ -55,14 +51,13 @@ def cross_section(value): fission = openmc.data.Reaction(18) fission.center_of_mass = False fission.Q_value = 193.0e6 - fission_xs = (2., 4., 2.) + fission_xs = (2.0, 4.0, 2.0) for T, xs in zip(temperatures, fission_xs): - fission.xs['{}K'.format(T)] = cross_section(xs) + fission.xs["{}K".format(T)] = cross_section(xs) a = openmc.data.Tabulated1D([E_min, E_max], [0.988e6, 0.988e6]) b = openmc.data.Tabulated1D([E_min, E_max], [2.249e-6, 2.249e-6]) fission_dist = openmc.data.UncorrelatedAngleEnergy( - isotropic_angle(E_min, E_max), - openmc.data.WattEnergy(a, b, -E_max) + isotropic_angle(E_min, E_max), openmc.data.WattEnergy(a, b, -E_max) ) product = openmc.data.Product() product.distribution.append(fission_dist) @@ -73,43 +68,45 @@ def cross_section(value): # Create capture capture = openmc.data.Reaction(102) capture.q_value = 6.5e6 - capture_xs = (2., 0., 2.) + capture_xs = (2.0, 0.0, 2.0) for T, xs in zip(temperatures, capture_xs): - capture.xs['{}K'.format(T)] = cross_section(xs) + capture.xs["{}K".format(T)] = cross_section(xs) u235_fake.reactions[102] = capture # Export HDF5 file - u235_fake.export_to_hdf5('U235_fake.h5', 'w') + u235_fake.export_to_hdf5("U235_fake.h5", "w") # Create a fake thermal scattering library attached to the fake U235 data c_U_fake = openmc.data.ThermalScattering("c_U_fake", 1.9968, 4.9, [0.0253]) - c_U_fake.nuclides = ['U235'] + c_U_fake.nuclides = ["U235"] # Create elastic reaction bragg_edges = [0.00370672, 0.00494229] factors = [0.00375735, 0.01386287] coherent_xs = openmc.data.CoherentElastic(bragg_edges, factors) - incoherent_xs_294 = openmc.data.Tabulated1D([0.00370672, 0.00370672], [0.00370672, 0.00370672]) + incoherent_xs_294 = openmc.data.Tabulated1D( + [0.00370672, 0.00370672], [0.00370672, 0.00370672] + ) elastic_xs_base = openmc.data.Sum((coherent_xs, incoherent_xs_294)) - elastic_xs = {'294K': elastic_xs_base, '600K': elastic_xs_base} + elastic_xs = {"294K": elastic_xs_base, "600K": elastic_xs_base} coherent_dist = openmc.data.CoherentElasticAE(coherent_xs) - incoherent_dist_294 = openmc.data.IncoherentElasticAEDiscrete([ - [-0.6, -0.18, 0.18, 0.6], [-0.6, -0.18, 0.18, 0.6] - ]) - incoherent_dist_600 = openmc.data.IncoherentElasticAEDiscrete([ - [-0.1, -0.2, 0.2, 0.1], [-0.1, -0.2, 0.2, 0.1] - ]) + incoherent_dist_294 = openmc.data.IncoherentElasticAEDiscrete( + [[-0.6, -0.18, 0.18, 0.6], [-0.6, -0.18, 0.18, 0.6]] + ) + incoherent_dist_600 = openmc.data.IncoherentElasticAEDiscrete( + [[-0.1, -0.2, 0.2, 0.1], [-0.1, -0.2, 0.2, 0.1]] + ) elastic_dist = { - '294K': openmc.data.MixedElasticAE(coherent_dist, incoherent_dist_294), - '600K': openmc.data.MixedElasticAE(coherent_dist, incoherent_dist_600) - } + "294K": openmc.data.MixedElasticAE(coherent_dist, incoherent_dist_294), + "600K": openmc.data.MixedElasticAE(coherent_dist, incoherent_dist_600), + } c_U_fake.elastic = openmc.data.ThermalScatteringReaction(elastic_xs, elastic_dist) # Create inelastic reaction inelastic_xs = { - '294K': openmc.data.Tabulated1D([1.0e-5, 4.9], [13.4, 3.35]), - '600K': openmc.data.Tabulated1D([1.0e-2, 10], [1.4, 5]) - } + "294K": openmc.data.Tabulated1D([1.0e-5, 4.9], [13.4, 3.35]), + "600K": openmc.data.Tabulated1D([1.0e-2, 10], [1.4, 5]), + } breakpoints = [3] interpolation = [2] energy = [1.0e-5, 4.3e-2, 4.9] @@ -121,12 +118,15 @@ def cross_section(value): for eout in energy_out: eout.normalize() eout.c = eout.cdf() - discrete = openmc.stats.Discrete([-0.9, -0.6, -0.3, -0.1, 0.1, 0.3, 0.6, 0.9], [1/8]*8) + discrete = openmc.stats.Discrete( + [-0.9, -0.6, -0.3, -0.1, 0.1, 0.3, 0.6, 0.9], [1 / 8] * 8 + ) discrete.c = discrete.cdf()[1:] - mu = [[discrete]*4]*3 + mu = [[discrete] * 4] * 3 dist = openmc.data.IncoherentInelasticAE( - breakpoints, interpolation, energy, energy_out, mu) - inelastic_dist = {'294K': dist, '600K': dist} + breakpoints, interpolation, energy, energy_out, mu + ) + inelastic_dist = {"294K": dist, "600K": dist} inelastic = openmc.data.ThermalScatteringReaction(inelastic_xs, inelastic_dist) c_U_fake.inelastic = inelastic @@ -135,12 +135,12 @@ def cross_section(value): # Create a data library of the fake nuclide and its thermal scattering data lib = openmc.data.DataLibrary() - lib.register_file('U235_fake.h5') + lib.register_file("U235_fake.h5") lib.register_file("c_U_fake.h5") - lib.export_to_xml('cross_sections_fake.xml') + lib.export_to_xml("cross_sections_fake.xml") -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def model(tmp_path_factory): tmp_path = tmp_path_factory.mktemp("temp_interp") orig = Path.cwd() @@ -150,11 +150,11 @@ def model(tmp_path_factory): model = openmc.model.Model() mat = openmc.Material() - mat.add_nuclide('U235', 1.0) + mat.add_nuclide("U235", 1.0) model.materials.append(mat) - model.materials.cross_sections = 'cross_sections_fake.xml' + model.materials.cross_sections = "cross_sections_fake.xml" - sph = openmc.Sphere(r=100.0, boundary_type='reflective') + sph = openmc.Sphere(r=100.0, boundary_type="reflective") cell = openmc.Cell(fill=mat, region=-sph) model.geometry = openmc.Geometry([cell]) @@ -163,7 +163,7 @@ def model(tmp_path_factory): model.settings.batches = 10 tally = openmc.Tally() - tally.scores = ['absorption', 'fission', 'scatter', 'nu-fission'] + tally.scores = ["absorption", "fission", "scatter", "nu-fission"] model.tallies = [tally] try: @@ -186,10 +186,14 @@ def model(tmp_path_factory): ("interpolation", 840.0, 0.6, 10), ("interpolation", 295.0, 0.5, 10), ("interpolation", 990.0, 0.5, 100), - ] + ], ) def test_interpolation(model, method, temperature, fission_expected, tolerance): - model.settings.temperature = {'method': method, 'default': temperature, "tolerance": tolerance} + model.settings.temperature = { + "method": method, + "default": temperature, + "tolerance": tolerance, + } sp_filename = model.run() with openmc.StatePoint(sp_filename) as sp: t = sp.tallies[model.tallies[0].id] @@ -197,38 +201,45 @@ def test_interpolation(model, method, temperature, fission_expected, tolerance): absorption_unc, fission_unc, scatter_unc, nu_fission_unc = t.std_dev.ravel() nu = 2.0 - assert abs(absorption_mean - 1) < 3*absorption_unc - assert abs(fission_mean - fission_expected) < 3*fission_unc - assert abs(scatter_mean - 1/4) < 3*scatter_unc - assert abs(nu_fission_mean - nu*fission_expected) < 3*nu_fission_unc + assert abs(absorption_mean - 1) < 3 * absorption_unc + assert abs(fission_mean - fission_expected) < 3 * fission_unc + assert abs(scatter_mean - 1 / 4) < 3 * scatter_unc + assert abs(nu_fission_mean - nu * fission_expected) < 3 * nu_fission_unc # Check that k-effective value matches expected k = sp.keff if isnan(k.s): - assert k.n == pytest.approx(nu*fission_expected) + assert k.n == pytest.approx(nu * fission_expected) else: - assert abs(k.n - nu*fission_expected) <= 3*k.s + assert abs(k.n - nu * fission_expected) <= 3 * k.s def test_temperature_interpolation_tolerance(model): - """Test applying global and cell temperatures with thermal scattering libraries - """ + """Test applying global and cell temperatures with thermal scattering libraries""" model.materials[0].add_s_alpha_beta("c_U_fake") # Default k-effective, using the thermal scattering data's minimum available temperature - model.settings.temperature = {'method': "nearest", 'default': 294, "tolerance": 50} + model.settings.temperature = {"method": "nearest", "default": 294, "tolerance": 50} sp_filename = model.run() with openmc.StatePoint(sp_filename) as sp: default_k = sp.keff.n # Get k-effective with temperature below the minimum but in interpolation mode - model.settings.temperature = {'method': "interpolation", 'default': 255, "tolerance": 50} + model.settings.temperature = { + "method": "interpolation", + "default": 255, + "tolerance": 50, + } sp_filename = model.run() with openmc.StatePoint(sp_filename) as sp: interpolated_k = sp.keff.n # Get the k-effective with the temperature applied to the cell, instead of globally - model.settings.temperature = {'method': "interpolation", 'default': 500, "tolerance": 50} + model.settings.temperature = { + "method": "interpolation", + "default": 500, + "tolerance": 50, + } for cell in model.geometry.get_all_cells().values(): cell.temperature = 275 sp_filename = model.run() @@ -251,16 +262,16 @@ def test_temperature_slightly_above(run_in_tmpdir): model = openmc.Model() mat1 = openmc.Material() - mat1.add_nuclide('U235', 1.0) + mat1.add_nuclide("U235", 1.0) mat1.temperature = 900.1 mat2 = openmc.Material() - mat2.add_nuclide('U235', 1.0) + mat2.add_nuclide("U235", 1.0) mat2.temperature = 600.0 model.materials.extend([mat1, mat2]) - model.materials.cross_sections = 'cross_sections_fake.xml' + model.materials.cross_sections = "cross_sections_fake.xml" sph1 = openmc.Sphere(r=1.0) - sph2 = openmc.Sphere(r=4.0, boundary_type='reflective') + sph2 = openmc.Sphere(r=4.0, boundary_type="reflective") cell1 = openmc.Cell(fill=mat1, region=-sph1) cell2 = openmc.Cell(fill=mat2, region=+sph1 & -sph2) model.geometry = openmc.Geometry([cell1, cell2]) @@ -268,7 +279,7 @@ def test_temperature_slightly_above(run_in_tmpdir): model.settings.particles = 1000 model.settings.inactive = 0 model.settings.batches = 10 - model.settings.temperature = {'method': 'interpolation'} + model.settings.temperature = {"method": "interpolation"} sp_filename = model.run() with openmc.StatePoint(sp_filename) as sp: diff --git a/tests/unit_tests/test_time_filter.py b/tests/unit_tests/test_time_filter.py index b57c80a9758..7d42e3db253 100644 --- a/tests/unit_tests/test_time_filter.py +++ b/tests/unit_tests/test_time_filter.py @@ -18,16 +18,16 @@ def test_time_filter_basics(): # to_xml_element() elem = f.to_xml_element() - assert elem.tag == 'filter' - assert elem.attrib['type'] == 'time' + assert elem.tag == "filter" + assert elem.attrib["type"] == "time" def time(particle, distance, E): """Return the time it takes a particle at a given energy to travel a certain distance""" - if particle == 'neutron': + if particle == "neutron": mass = 939.56542052e6 # eV/c² - elif particle == 'photon': + elif particle == "photon": mass = 0.0 # Calculate speed via v = c * sqrt(1 - γ^-2) @@ -36,34 +36,34 @@ def time(particle, distance, E): return distance / velocity -@pytest.fixture(params=['neutron', 'photon']) +@pytest.fixture(params=["neutron", "photon"]) def model(request): # Select random sphere radius, source position, and source energy - r = uniform(0., 2.) - x = uniform(2., 10.) - E = uniform(0., 20.0e6) + r = uniform(0.0, 2.0) + x = uniform(2.0, 10.0) + E = uniform(0.0, 20.0e6) # Create model model = openmc.Model() mat = openmc.Material() - mat.add_nuclide('Zr90', 1.0) - mat.set_density('g/cm3', 1.0) + mat.add_nuclide("Zr90", 1.0) + mat.set_density("g/cm3", 1.0) model.materials.append(mat) inner_sphere = openmc.Sphere(r=r) - outer_sphere = openmc.Sphere(r=10.0, boundary_type='vacuum') + outer_sphere = openmc.Sphere(r=10.0, boundary_type="vacuum") center_cell = openmc.Cell(fill=mat, region=-inner_sphere) outer_void = openmc.Cell(region=+inner_sphere & -outer_sphere) model.geometry = openmc.Geometry([center_cell, outer_void]) model.settings = openmc.Settings() - model.settings.run_mode = 'fixed source' + model.settings.run_mode = "fixed source" model.settings.particles = 1000 model.settings.batches = 20 particle = request.param model.settings.source = openmc.IndependentSource( - space=openmc.stats.Point((x, 0., 0.)), - angle=openmc.stats.Monodirectional([-1., 0., 0.]), + space=openmc.stats.Point((x, 0.0, 0.0)), + angle=openmc.stats.Monodirectional([-1.0, 0.0, 0.0]), energy=openmc.stats.Discrete([E], [1.0]), - particle=particle + particle=particle, ) # Calculate time it will take neutrons to reach sphere @@ -71,40 +71,40 @@ def model(request): # Create tally with time filter tally = openmc.Tally() - tally.filters = [openmc.TimeFilter([0.0, t0, 2*t0])] - tally.scores = ['total'] + tally.filters = [openmc.TimeFilter([0.0, t0, 2 * t0])] + tally.scores = ["total"] model.tallies.append(tally) return model -@pytest.fixture(params=['neutron', 'photon']) +@pytest.fixture(params=["neutron", "photon"]) def model_surf(request): # Select random distance and source energy - x = uniform(50., 100.) - E = uniform(0., 20.0e6) + x = uniform(50.0, 100.0) + E = uniform(0.0, 20.0e6) # Create model model = openmc.Model() mat = openmc.Material() - mat.add_nuclide('Zr90', 1.0) - mat.set_density('g/cm3', 1.0) + mat.add_nuclide("Zr90", 1.0) + mat.set_density("g/cm3", 1.0) model.materials.append(mat) - left = openmc.XPlane(-1., boundary_type='vacuum') - black_surface = openmc.XPlane(x, boundary_type='vacuum') + left = openmc.XPlane(-1.0, boundary_type="vacuum") + black_surface = openmc.XPlane(x, boundary_type="vacuum") right = openmc.XPlane(x + 1) void_cell = openmc.Cell(region=+left & -black_surface) black_cell = openmc.Cell(region=+black_surface & -right) model.geometry = openmc.Geometry([void_cell, black_cell]) model.settings = openmc.Settings() - model.settings.run_mode = 'fixed source' + model.settings.run_mode = "fixed source" model.settings.particles = 1000 model.settings.batches = 20 particle = request.param model.settings.source = openmc.IndependentSource( - space=openmc.stats.Point((0., 0., 0.)), - angle=openmc.stats.Monodirectional([1., 0., 0.]), + space=openmc.stats.Point((0.0, 0.0, 0.0)), + angle=openmc.stats.Monodirectional([1.0, 0.0, 0.0]), energy=openmc.stats.Discrete([E], [1.0]), - particle=particle + particle=particle, ) # Calculate time it will take neutrons to reach purely-absorbing surface @@ -114,9 +114,9 @@ def model_surf(request): tally = openmc.Tally() tally.filters = [ openmc.SurfaceFilter([black_surface]), - openmc.TimeFilter([0.0, t0*0.999, t0*1.001, 100.0]) + openmc.TimeFilter([0.0, t0 * 0.999, t0 * 1.001, 100.0]), ] - tally.scores = ['current'] + tally.scores = ["current"] model.tallies.append(tally) return model @@ -156,27 +156,26 @@ def test_small_time_interval(run_in_tmpdir): # of the photon, the time intervals are on the order of 1e-9 seconds, which # are effectively 0 when compared to the starting time of the photon. mat = openmc.Material() - mat.add_element('N', 1.0) - mat.set_density('g/cm3', 0.001) - sph = openmc.Sphere(r=5.0, boundary_type='vacuum') + mat.add_element("N", 1.0) + mat.set_density("g/cm3", 0.001) + sph = openmc.Sphere(r=5.0, boundary_type="vacuum") cell = openmc.Cell(fill=mat, region=-sph) model = openmc.Model() model.geometry = openmc.Geometry([cell]) model.settings.particles = 100 model.settings.batches = 10 - model.settings.run_mode = 'fixed source' + model.settings.run_mode = "fixed source" model.settings.source = openmc.IndependentSource( - time=openmc.stats.Discrete([1.0e8], [1.0]), - particle='photon' + time=openmc.stats.Discrete([1.0e8], [1.0]), particle="photon" ) # Add tallies with and without a time filter that should match all particles time_filter = openmc.TimeFilter([0.0, 1.0e100]) tally_with_filter = openmc.Tally() tally_with_filter.filters = [time_filter] - tally_with_filter.scores = ['flux'] + tally_with_filter.scores = ["flux"] tally_without_filter = openmc.Tally() - tally_without_filter.scores = ['flux'] + tally_without_filter.scores = ["flux"] model.tallies.extend([tally_with_filter, tally_without_filter]) # Run the model and make sure the two tallies match diff --git a/tests/unit_tests/test_torus.py b/tests/unit_tests/test_torus.py index 8c413ffe06d..9bd1ffb06db 100644 --- a/tests/unit_tests/test_torus.py +++ b/tests/unit_tests/test_torus.py @@ -7,8 +7,8 @@ def get_torus_keff(cls, R, r, center=(0, 0, 0)): model = openmc.Model() mat = openmc.Material() - mat.add_nuclide('U235', 1.0) - mat.set_density('g/cm3', 10.0) + mat.add_nuclide("U235", 1.0) + mat.set_density("g/cm3", 10.0) x, y, z = center torus = cls(x0=x, y0=y, z0=z, a=R, b=r, c=r) @@ -36,7 +36,7 @@ def test_torus_keff(R, r, run_in_tmpdir): get_torus_keff(openmc.YTorus, R, r), get_torus_keff(openmc.YTorus, R, r, random_point()), get_torus_keff(openmc.ZTorus, R, r), - get_torus_keff(openmc.ZTorus, R, r, random_point()) + get_torus_keff(openmc.ZTorus, R, r, random_point()), ] # For each combination of keff values, their difference should be within @@ -44,4 +44,4 @@ def test_torus_keff(R, r, run_in_tmpdir): for k1, k2 in combinations(keffs, 2): print(k1, k2) diff = k1 - k2 - assert abs(diff.n) < 3*diff.s + assert abs(diff.n) < 3 * diff.s diff --git a/tests/unit_tests/test_tracks.py b/tests/unit_tests/test_tracks.py index fa866415984..17158ce8278 100644 --- a/tests/unit_tests/test_tracks.py +++ b/tests/unit_tests/test_tracks.py @@ -12,15 +12,15 @@ def sphere_model(): openmc.reset_auto_ids() mat = openmc.Material() - mat.add_nuclide('Zr90', 1.0) - mat.set_density('g/cm3', 1.0) + mat.add_nuclide("Zr90", 1.0) + mat.set_density("g/cm3", 1.0) model = openmc.Model() - sph = openmc.Sphere(r=25.0, boundary_type='vacuum') + sph = openmc.Sphere(r=25.0, boundary_type="vacuum") cell = openmc.Cell(fill=mat, region=-sph) model.geometry = openmc.Geometry([cell]) - model.settings.run_mode = 'fixed source' + model.settings.run_mode = "fixed source" model.settings.batches = 2 model.settings.particles = 50 @@ -29,17 +29,17 @@ def sphere_model(): def generate_track_file(model, **kwargs): # If running in MPI mode, setup proper keyword arguments for run() - kwargs.setdefault('openmc_exec', config['exe']) - if config['mpi']: - kwargs['mpi_args'] = [config['mpiexec'], '-n', config['mpi_np']] + kwargs.setdefault("openmc_exec", config["exe"]) + if config["mpi"]: + kwargs["mpi_args"] = [config["mpiexec"], "-n", config["mpi_np"]] model.run(**kwargs) - if config['mpi'] and int(config['mpi_np']) > 1: + if config["mpi"] and int(config["mpi_np"]) > 1: # With MPI, we need to combine track files - track_files = Path.cwd().glob('tracks_p*.h5') - openmc.Tracks.combine(track_files, 'tracks.h5') + track_files = Path.cwd().glob("tracks_p*.h5") + openmc.Tracks.combine(track_files, "tracks.h5") else: - track_file = Path('tracks.h5') + track_file = Path("tracks.h5") assert track_file.is_file() @@ -55,7 +55,7 @@ def test_tracks(sphere_model, particle, run_in_tmpdir): generate_track_file(sphere_model) # Open track file and make sure we have correct number of tracks - tracks = openmc.Tracks('tracks.h5') + tracks = openmc.Tracks("tracks.h5") assert len(tracks) == len(sphere_model.settings.track) for track, identifier in zip(tracks, sphere_model.settings.track): @@ -63,7 +63,7 @@ def test_tracks(sphere_model, particle, run_in_tmpdir): assert isinstance(track, openmc.Track) assert track.identifier == identifier assert isinstance(track.particle_tracks, list) - if particle == 'neutron': + if particle == "neutron": assert len(track.particle_tracks) == 1 # Check attributes on ParticleTrack object @@ -75,38 +75,38 @@ def test_tracks(sphere_model, particle, run_in_tmpdir): # Sanity checks on actual data for state in particle_track.states: - assert np.linalg.norm([*state['r']]) <= 25.0001 - assert np.linalg.norm([*state['u']]) == pytest.approx(1.0) - assert 0.0 <= state['E'] <= 20.0e6 - assert state['time'] >= 0.0 - assert 0.0 <= state['wgt'] <= 1.0 - assert state['cell_id'] == 1 - assert state['material_id'] == 1 + assert np.linalg.norm([*state["r"]]) <= 25.0001 + assert np.linalg.norm([*state["u"]]) == pytest.approx(1.0) + assert 0.0 <= state["E"] <= 20.0e6 + assert state["time"] >= 0.0 + assert 0.0 <= state["wgt"] <= 1.0 + assert state["cell_id"] == 1 + assert state["material_id"] == 1 # Checks on 'sources' property sources = track.sources assert len(sources) == len(track.particle_tracks) x = sources[0] state = particle_track.states[0] - assert x.r == (*state['r'],) - assert x.u == (*state['u'],) - assert x.E == state['E'] - assert x.time == state['time'] - assert x.wgt == state['wgt'] + assert x.r == (*state["r"],) + assert x.u == (*state["u"],) + assert x.E == state["E"] + assert x.time == state["time"] + assert x.wgt == state["wgt"] assert x.particle == particle_track.particle def test_max_tracks(sphere_model, run_in_tmpdir): # Set maximum number of tracks per process to write sphere_model.settings.max_tracks = expected_num_tracks = 10 - if config['mpi']: - expected_num_tracks *= int(config['mpi_np']) + if config["mpi"]: + expected_num_tracks *= int(config["mpi_np"]) # Run OpenMC to generate tracks.h5 file generate_track_file(sphere_model, tracks=True) # Open track file and make sure we have correct number of tracks - tracks = openmc.Tracks('tracks.h5') + tracks = openmc.Tracks("tracks.h5") assert len(tracks) == expected_num_tracks @@ -118,34 +118,34 @@ def test_filter(sphere_model, run_in_tmpdir): # Run OpenMC to generate tracks.h5 file generate_track_file(sphere_model, tracks=True) - tracks = openmc.Tracks('tracks.h5') + tracks = openmc.Tracks("tracks.h5") for track in tracks: # Test filtering by particle - matches = track.filter(particle='photon') + matches = track.filter(particle="photon") for x in matches: assert x.particle == openmc.ParticleType.PHOTON # Test general state filter - matches = track.filter(state_filter=lambda s: s['cell_id'] == 1) + matches = track.filter(state_filter=lambda s: s["cell_id"] == 1) assert isinstance(matches, openmc.Track) assert matches.particle_tracks == track.particle_tracks - matches = track.filter(state_filter=lambda s: s['cell_id'] == 2) + matches = track.filter(state_filter=lambda s: s["cell_id"] == 2) assert matches.particle_tracks == [] - matches = track.filter(state_filter=lambda s: s['E'] < 0.0) + matches = track.filter(state_filter=lambda s: s["E"] < 0.0) assert matches.particle_tracks == [] # Test filter method on Tracks - matches = tracks.filter(particle='neutron') + matches = tracks.filter(particle="neutron") assert isinstance(matches, openmc.Tracks) assert matches == tracks - matches = tracks.filter(state_filter=lambda s: s['E'] > 0.0) + matches = tracks.filter(state_filter=lambda s: s["E"] > 0.0) assert matches == tracks - matches = tracks.filter(particle='bunnytron') + matches = tracks.filter(particle="bunnytron") assert matches == [] def test_write_to_vtk(sphere_model): - vtk = pytest.importorskip('vtk') + vtk = pytest.importorskip("vtk") # Set maximum number of tracks per process to write sphere_model.settings.max_tracks = 25 sphere_model.settings.photon_transport = True @@ -153,11 +153,11 @@ def test_write_to_vtk(sphere_model): # Run OpenMC to generate tracks.h5 file generate_track_file(sphere_model, tracks=True) - tracks = openmc.Tracks('tracks.h5') - polydata = tracks.write_to_vtk('tracks.vtp') + tracks = openmc.Tracks("tracks.h5") + polydata = tracks.write_to_vtk("tracks.vtp") assert isinstance(polydata, vtk.vtkPolyData) - assert Path('tracks.vtp').is_file() + assert Path("tracks.vtp").is_file() def test_restart_track(run_in_tmpdir, sphere_model): @@ -167,26 +167,28 @@ def test_restart_track(run_in_tmpdir, sphere_model): cell.region &= +plane # generate lost particle files - with pytest.raises(RuntimeError, match='Maximum number of lost particles has been reached.'): + with pytest.raises( + RuntimeError, match="Maximum number of lost particles has been reached." + ): sphere_model.run(output=False, threads=1) - lost_particle_files = list(Path.cwd().glob('particle_*.h5')) + lost_particle_files = list(Path.cwd().glob("particle_*.h5")) assert len(lost_particle_files) > 0 particle_file = lost_particle_files[0] - # restart the lost particle with tracks enabled + # restart the lost particle with tracks enabled sphere_model.run(tracks=True, restart_file=particle_file) - tracks_file = Path('tracks.h5') + tracks_file = Path("tracks.h5") assert tracks_file.is_file() # check that the last track of the file matches the lost particle file tracks = openmc.Tracks(tracks_file) initial_state = tracks[0].particle_tracks[0].states[0] - restart_r = np.array(initial_state['r']) - restart_u = np.array(initial_state['u']) + restart_r = np.array(initial_state["r"]) + restart_u = np.array(initial_state["u"]) - with h5py.File(particle_file, 'r') as lost_particle_file: - lost_r = np.array(lost_particle_file['xyz'][()]) - lost_u = np.array(lost_particle_file['uvw'][()]) + with h5py.File(particle_file, "r") as lost_particle_file: + lost_r = np.array(lost_particle_file["xyz"][()]) + lost_u = np.array(lost_particle_file["uvw"][()]) pytest.approx(restart_r, lost_r) pytest.approx(restart_u, lost_u) diff --git a/tests/unit_tests/test_transfer_volumes.py b/tests/unit_tests/test_transfer_volumes.py index b768bc88a2a..0c0fbe980ba 100644 --- a/tests/unit_tests/test_transfer_volumes.py +++ b/tests/unit_tests/test_transfer_volumes.py @@ -22,8 +22,8 @@ def test_transfer_volumes(run_in_tmpdir): res = openmc.deplete.Results(op.output_dir / "depletion_results.h5") # Create a dictionary of volumes to transfer - res[0].volume['1'] = 1.5 - res[0].volume['2'] = 2.5 + res[0].volume["1"] = 1.5 + res[0].volume["2"] = 2.5 # Create dummy geometry mat1 = openmc.Material(material_id=1) diff --git a/tests/unit_tests/test_triggers.py b/tests/unit_tests/test_triggers.py index 14bda0cceb3..945379d4cae 100644 --- a/tests/unit_tests/test_triggers.py +++ b/tests/unit_tests/test_triggers.py @@ -1,6 +1,6 @@ - import openmc + def test_tally_trigger(run_in_tmpdir): pincell = openmc.examples.pwr_pin_cell() @@ -10,10 +10,10 @@ def test_tally_trigger(run_in_tmpdir): # create a tally with triggers applied tally = openmc.Tally() tally.filters = [mat_filter] - tally.scores = ['scatter'] + tally.scores = ["scatter"] - trigger = openmc.Trigger('rel_err', 0.05) - trigger.scores = ['scatter'] + trigger = openmc.Trigger("rel_err", 0.05) + trigger.scores = ["scatter"] tally.triggers = [trigger] @@ -29,7 +29,7 @@ def test_tally_trigger(run_in_tmpdir): # adding other scores to the tally should not change the # number of batches required to satisfy the trigger - tally.scores = ['total', 'absorption', 'scatter'] + tally.scores = ["total", "absorption", "scatter"] sp_file = pincell.run() @@ -42,16 +42,16 @@ def test_tally_trigger(run_in_tmpdir): def test_tally_trigger_null_score(run_in_tmpdir): pincell = openmc.examples.pwr_pin_cell() - # create a tally filter on the materials + # create a tally filter on the materials mat_filter = openmc.MaterialFilter(pincell.materials) # apply a tally with a score that be tallied in this model tally = openmc.Tally() tally.filters = [mat_filter] - tally.scores = ['pair-production'] + tally.scores = ["pair-production"] - trigger = openmc.Trigger('rel_err', 0.05) - trigger.scores = ['pair-production'] + trigger = openmc.Trigger("rel_err", 0.05) + trigger.scores = ["pair-production"] tally.triggers = [trigger] @@ -83,12 +83,12 @@ def test_tally_trigger_zero_ignored(run_in_tmpdir): # create a tally with triggers applied tally = openmc.Tally() tally.filters = [e_filter] - tally.scores = ['(n,p)'] + tally.scores = ["(n,p)"] tally.nuclides = ["O16"] # 100% relative error: should be immediately satisfied in nonzero bin - trigger = openmc.Trigger('rel_err', 1.0) - trigger.scores = ['(n,p)'] + trigger = openmc.Trigger("rel_err", 1.0) + trigger.scores = ["(n,p)"] trigger.ignore_zeros = True tally.triggers = [trigger] @@ -114,13 +114,12 @@ def test_tally_trigger_zero_ignored(run_in_tmpdir): assert total_batches < pincell.settings.trigger_max_batches - def test_trigger_he3_production(run_in_tmpdir): li6 = openmc.Material() - li6.set_density('g/cm3', 1.0) - li6.add_nuclide('Li6', 1.0) + li6.set_density("g/cm3", 1.0) + li6.add_nuclide("Li6", 1.0) - sph = openmc.Sphere(r=20, boundary_type='vacuum') + sph = openmc.Sphere(r=20, boundary_type="vacuum") outer_cell = openmc.Cell(fill=li6, region=-sph) model = openmc.Model() model.geometry = openmc.Geometry([outer_cell]) @@ -129,16 +128,16 @@ def test_trigger_he3_production(run_in_tmpdir): ) model.settings.batches = 10 model.settings.particles = 100 - model.settings.run_mode = 'fixed source' + model.settings.run_mode = "fixed source" model.settings.trigger_active = True model.settings.trigger_batch_interval = 10 model.settings.trigger_max_batches = 30 # Define tally with trigger - trigger = openmc.Trigger(trigger_type='rel_err', threshold=0.0001) - trigger.scores = ['He3-production'] + trigger = openmc.Trigger(trigger_type="rel_err", threshold=0.0001) + trigger.scores = ["He3-production"] he3_production_tally = openmc.Tally() - he3_production_tally.scores = ['He3-production'] + he3_production_tally.scores = ["He3-production"] he3_production_tally.triggers = [trigger] model.tallies = openmc.Tallies([he3_production_tally]) diff --git a/tests/unit_tests/test_uniform_source_sampling.py b/tests/unit_tests/test_uniform_source_sampling.py index 7f805e37d27..e0430419981 100644 --- a/tests/unit_tests/test_uniform_source_sampling.py +++ b/tests/unit_tests/test_uniform_source_sampling.py @@ -5,9 +5,9 @@ @pytest.fixture def sphere_model(): mat = openmc.Material() - mat.add_nuclide('Li6', 1.0) - mat.set_density('g/cm3', 1.0) - sphere = openmc.Sphere(r=1.0, boundary_type='vacuum') + mat.add_nuclide("Li6", 1.0) + mat.set_density("g/cm3", 1.0) + sphere = openmc.Sphere(r=1.0, boundary_type="vacuum") cell = openmc.Cell(region=-sphere, fill=mat) model = openmc.Model() model.geometry = openmc.Geometry([cell]) @@ -15,8 +15,7 @@ def sphere_model(): model.settings.particles = 100 model.settings.batches = 1 model.settings.source = openmc.IndependentSource( - energy=openmc.stats.delta_function(1.0e3), - strength=100.0 + energy=openmc.stats.delta_function(1.0e3), strength=100.0 ) model.settings.run_mode = "fixed source" model.settings.surf_source_write = { @@ -24,7 +23,7 @@ def sphere_model(): } tally = openmc.Tally() - tally.scores = ['flux'] + tally.scores = ["flux"] model.tallies = [tally] return model @@ -34,14 +33,14 @@ def test_source_weight(run_in_tmpdir, sphere_model): # have weight 1 sphere_model.settings.uniform_source_sampling = False sphere_model.run() - particles = openmc.ParticleList.from_hdf5('surface_source.h5') + particles = openmc.ParticleList.from_hdf5("surface_source.h5") assert set(p.wgt for p in particles) == {1.0} # Run with uniform source sampling and check that banked particles have # weight == strength sphere_model.settings.uniform_source_sampling = True sphere_model.run() - particles = openmc.ParticleList.from_hdf5('surface_source.h5') + particles = openmc.ParticleList.from_hdf5("surface_source.h5") strength = sphere_model.settings.source[0].strength assert set(p.wgt for p in particles) == {strength} @@ -65,7 +64,8 @@ def test_tally_mean(run_in_tmpdir, sphere_model): def test_multiple_sources(sphere_model): low_strength_src = openmc.IndependentSource( - energy=openmc.stats.delta_function(1.0e6), strength=1e-7) + energy=openmc.stats.delta_function(1.0e6), strength=1e-7 + ) sphere_model.settings.source.append(low_strength_src) sphere_model.settings.uniform_source_sampling = True diff --git a/tests/unit_tests/test_universe.py b/tests/unit_tests/test_universe.py index 46d4ec3f734..4617f09fb6f 100644 --- a/tests/unit_tests/test_universe.py +++ b/tests/unit_tests/test_universe.py @@ -10,8 +10,8 @@ def test_basic(): c1 = openmc.Cell() c2 = openmc.Cell() c3 = openmc.Cell() - u = openmc.Universe(name='cool', cells=(c1, c2, c3)) - assert u.name == 'cool' + u = openmc.Universe(name="cool", cells=(c1, c2, c3)) + assert u.name == "cool" cells = set(u.cells.values()) assert not (cells ^ {c1, c2, c3}) @@ -40,8 +40,8 @@ def test_bounding_box(): u = openmc.Universe(cells=[c1, c2]) ll, ur = u.bounding_box - assert ll == pytest.approx((-2., -2., -np.inf)) - assert ur == pytest.approx((2., 2., np.inf)) + assert ll == pytest.approx((-2.0, -2.0, -np.inf)) + assert ur == pytest.approx((2.0, 2.0, np.inf)) u = openmc.Universe() assert_unbounded(u) @@ -56,10 +56,10 @@ def test_plot(run_in_tmpdir, sphere_model): mat_colors = { materials[0]: (200, 1, 1), materials[1]: "gray", - materials[2]: "limegreen" + materials[2]: "limegreen", } - for basis in ('xy', 'yz', 'xz'): + for basis in ("xy", "yz", "xz"): plot = pincell.geometry.root_universe.plot( colors=mat_colors, color_by="material", @@ -67,28 +67,28 @@ def test_plot(run_in_tmpdir, sphere_model): pixels=(10, 10), basis=basis, outline=True, - axis_units='m' + axis_units="m", ) - assert plot.xaxis.get_label().get_text() == f'{basis[0]} [m]' - assert plot.yaxis.get_label().get_text() == f'{basis[1]} [m]' + assert plot.xaxis.get_label().get_text() == f"{basis[0]} [m]" + assert plot.yaxis.get_label().get_text() == f"{basis[1]} [m]" # model with no inf values in bounding box m = sphere_model.materials[0] univ = sphere_model.geometry.root_universe - colors = {m: 'limegreen'} + colors = {m: "limegreen"} - for basis in ('xy', 'yz', 'xz'): + for basis in ("xy", "yz", "xz"): plot = univ.plot( colors=colors, color_by="cell", legend=False, pixels=100, basis=basis, - outline=False + outline=False, ) - assert plot.xaxis.get_label().get_text() == f'{basis[0]} [cm]' - assert plot.yaxis.get_label().get_text() == f'{basis[1]} [cm]' + assert plot.xaxis.get_label().get_text() == f"{basis[0]} [cm]" + assert plot.yaxis.get_label().get_text() == f"{basis[1]} [cm]" msg = "Must pass 'colors' dictionary if you are adding a legend via legend=True." # This plot call should fail as legend is True but colors is None @@ -104,7 +104,7 @@ def test_get_nuclides(uo2): c = openmc.Cell(fill=uo2) univ = openmc.Universe(cells=[c]) nucs = univ.get_nuclides() - assert nucs == ['U235', 'O16'] + assert nucs == ["U235", "O16"] def test_cells(): @@ -146,8 +146,8 @@ def test_clone(): c2 = openmc.Cell(cell_id=2) c2.fill = openmc.Material() c3 = openmc.Cell() - u1 = openmc.Universe(name='cool', cells=(c1, c2, c3)) - u1.volume = 1. + u1 = openmc.Universe(name="cool", cells=(c1, c2, c3)) + u1.volume = 1.0 u2 = u1.clone() assert u2.name == u1.name @@ -159,18 +159,17 @@ def test_clone(): assert u2.get_all_materials() == u1.get_all_materials() u3 = u1.clone(clone_regions=False) - assert next(iter(u3.cells.values())).region ==\ - next(iter(u1.cells.values())).region + assert next(iter(u3.cells.values())).region == next(iter(u1.cells.values())).region # Change attributes, make sure clone stays intact - u1.volume = 2. + u1.volume = 2.0 u1.name = "different name" assert u3.volume != u1.volume assert u3.name != u1.name # Test cloning a DAGMC universe dagmc_u = openmc.DAGMCUniverse(filename="", name="DAGMC universe") - dagmc_u.volume = 1. + dagmc_u.volume = 1.0 dagmc_u.auto_geom_ids = True dagmc_u.auto_mat_ids = True dagmc_u1 = dagmc_u.clone() @@ -183,7 +182,7 @@ def test_clone(): dagmc_u.name = "another name" dagmc_u.auto_geom_ids = False dagmc_u.auto_mat_ids = False - dagmc_u.volume = 2. + dagmc_u.volume = 2.0 assert dagmc_u1.name != dagmc_u.name assert dagmc_u1.volume != dagmc_u.volume assert dagmc_u1.auto_geom_ids != dagmc_u.auto_geom_ids @@ -194,13 +193,12 @@ def test_create_xml(cell_with_lattice): cells = [openmc.Cell() for i in range(5)] u = openmc.Universe(cells=cells) - geom = ET.Element('geom') + geom = ET.Element("geom") u.create_xml_subelement(geom) - cell_elems = geom.findall('cell') + cell_elems = geom.findall("cell") assert len(cell_elems) == len(cells) - assert all(c.get('universe') == str(u.id) for c in cell_elems) - assert not (set(c.get('id') for c in cell_elems) ^ - set(str(c.id) for c in cells)) + assert all(c.get("universe") == str(u.id) for c in cell_elems) + assert not (set(c.get("id") for c in cell_elems) ^ set(str(c.id) for c in cells)) def test_get_nuclide_densities(): diff --git a/tests/unit_tests/test_urr_capture.py b/tests/unit_tests/test_urr_capture.py index 34b518e9de8..59e35bfa5da 100644 --- a/tests/unit_tests/test_urr_capture.py +++ b/tests/unit_tests/test_urr_capture.py @@ -9,21 +9,21 @@ def th232_model(): model = openmc.model.Model() th232 = openmc.Material() - th232.add_nuclide('Th232', 1.0) + th232.add_nuclide("Th232", 1.0) - surf = openmc.Sphere(r=100.0, boundary_type='reflective') + 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' + model.settings.run_mode = "fixed source" energies = openmc.stats.Uniform(e_min, e_max) model.settings.source = openmc.IndependentSource(energy=energies) - tally = openmc.Tally(name='rates') + tally = openmc.Tally(name="rates") tally.filters = [openmc.EnergyFilter([e_min, e_max])] - tally.scores = ['(n,gamma)', 'absorption', 'fission'] + tally.scores = ["(n,gamma)", "absorption", "fission"] model.tallies.append(tally) return model @@ -34,8 +34,8 @@ def test_urr_capture(run_in_tmpdir, th232_model): openmc.run() # Get reaction rates from tally - with openmc.StatePoint('statepoint.10.h5') as sp: - t = sp.get_tally(name='rates') + 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 diff --git a/tests/unit_tests/test_volume.py b/tests/unit_tests/test_volume.py index 0993cb66aab..177045d997d 100644 --- a/tests/unit_tests/test_volume.py +++ b/tests/unit_tests/test_volume.py @@ -15,17 +15,18 @@ def test_infinity_handling(): openmc.VolumeCalculation([cell1], 100, lower_left, upper_right) -@pytest.mark.parametrize('cls', [openmc.Cell, openmc.Material, openmc.Universe]) +@pytest.mark.parametrize("cls", [openmc.Cell, openmc.Material, openmc.Universe]) def test_invalid_id(run_in_tmpdir, cls): m = openmc.Material() - m.add_nuclide('U235', 0.02) - sph = openmc.Sphere(boundary_type='vacuum') + m.add_nuclide("U235", 0.02) + sph = openmc.Sphere(boundary_type="vacuum") cell = openmc.Cell(fill=m, region=-sph) model = openmc.Model(geometry=openmc.Geometry([cell])) # Apply volume calculation with unused domains model.settings.volume_calculations = openmc.VolumeCalculation( - [cls()], 10000, *model.geometry.bounding_box) + [cls()], 10000, *model.geometry.bounding_box + ) with pytest.raises(RuntimeError): model.calculate_volumes() @@ -35,11 +36,13 @@ def test_no_bcs(run_in_tmpdir): """Ensure that a model without boundary conditions can be used in a volume calculation""" model = openmc.examples.pwr_pin_cell() for surface in model.geometry.get_all_surfaces().values(): - surface.boundary_type = 'transmission' + surface.boundary_type = "transmission" - bbox = openmc.BoundingBox([-1.]*3, [1.]*3) + bbox = openmc.BoundingBox([-1.0] * 3, [1.0] * 3) cells = list(model.geometry.get_all_cells().values()) - vc = openmc.VolumeCalculation(cells, samples=10, lower_left=bbox[0], upper_right=bbox[1]) + vc = openmc.VolumeCalculation( + cells, samples=10, lower_left=bbox[0], upper_right=bbox[1] + ) model.settings.volume_calculations = [vc] model.calculate_volumes() diff --git a/tests/unit_tests/weightwindows/test.py b/tests/unit_tests/weightwindows/test.py index 79aadbef0de..8c425b7dacf 100644 --- a/tests/unit_tests/weightwindows/test.py +++ b/tests/unit_tests/weightwindows/test.py @@ -16,7 +16,7 @@ def wws(): # weight windows - ww_files = ('ww_n.txt', 'ww_p.txt') + ww_files = ("ww_n.txt", "ww_p.txt") cwd = Path(__file__).parent.absolute() ww_n_file, ww_p_file = [cwd / Path(f) for f in ww_files] @@ -34,21 +34,15 @@ def wws(): # energy bounds matching those of the # generated weight windows - e_bnds = [0.0, 0.5, 2E7] - - ww_n = openmc.WeightWindows(ww_mesh, - ww_n_lower_bnds, - None, - 10.0, - e_bnds, - survival_ratio=1.01) - - ww_p = openmc.WeightWindows(ww_mesh, - ww_p_lower_bnds, - None, - 10.0, - e_bnds, - survival_ratio=1.01) + e_bnds = [0.0, 0.5, 2e7] + + ww_n = openmc.WeightWindows( + ww_mesh, ww_n_lower_bnds, None, 10.0, e_bnds, survival_ratio=1.01 + ) + + ww_p = openmc.WeightWindows( + ww_mesh, ww_p_lower_bnds, None, 10.0, e_bnds, survival_ratio=1.01 + ) return [ww_n, ww_p] @@ -60,8 +54,8 @@ def model(): # materials (M4 steel alloy) m4 = openmc.Material() - m4.set_density('g/cc', 2.3) - m4.add_nuclide('H1', 0.168018676) + m4.set_density("g/cc", 2.3) + m4.add_nuclide("H1", 0.168018676) m4.add_nuclide("H2", 1.93244e-05) m4.add_nuclide("O16", 0.561814465) m4.add_nuclide("O17", 0.00021401) @@ -82,7 +76,7 @@ def model(): m4.add_nuclide("Fe58", 1.19737e-05) s0 = openmc.Sphere(r=240) - s1 = openmc.Sphere(r=250, boundary_type='vacuum') + s1 = openmc.Sphere(r=250, boundary_type="vacuum") c0 = openmc.Cell(fill=m4, region=-s0) c1 = openmc.Cell(region=+s0 & -s1) @@ -91,13 +85,13 @@ def model(): # settings settings = model.settings - settings.run_mode = 'fixed source' + settings.run_mode = "fixed source" settings.particles = 500 settings.batches = 2 settings.max_history_splits = 100 settings.photon_transport = True space = Point((0.001, 0.001, 0.001)) - energy = Discrete([14E6], [1.0]) + energy = Discrete([14e6], [1.0]) settings.source = openmc.IndependentSource(space=space, energy=energy) @@ -109,14 +103,14 @@ def model(): mesh_filter = openmc.MeshFilter(mesh) - e_bnds = [0.0, 0.5, 2E7] + e_bnds = [0.0, 0.5, 2e7] energy_filter = openmc.EnergyFilter(e_bnds) - particle_filter = openmc.ParticleFilter(['neutron', 'photon']) + particle_filter = openmc.ParticleFilter(["neutron", "photon"]) tally = openmc.Tally() tally.filters = [mesh_filter, energy_filter, particle_filter] - tally.scores = ['flux'] + tally.scores = ["flux"] model.tallies.append(tally) @@ -125,7 +119,7 @@ def model(): def test_weightwindows(model, wws): - ww_files = ('ww_n.txt', 'ww_p.txt') + ww_files = ("ww_n.txt", "ww_p.txt") cwd = Path(__file__).parent.absolute() filepaths = [cwd / Path(f) for f in ww_files] @@ -133,7 +127,7 @@ def test_weightwindows(model, wws): # run once with variance reduction off model.settings.weight_windows_on = False analog_sp = model.run() - os.rename(analog_sp, 'statepoint.analog.h5') + os.rename(analog_sp, "statepoint.analog.h5") model.settings.weight_windows = wws @@ -144,32 +138,38 @@ def test_weightwindows(model, wws): # run again with variance reduction on model.settings.weight_windows_on = True ww_sp = model.run() - os.rename(ww_sp, 'statepoint.ww.h5') + os.rename(ww_sp, "statepoint.ww.h5") # load both statepoints and examine results - asp = openmc.StatePoint('statepoint.analog.h5') - wsp = openmc.StatePoint('statepoint.ww.h5') + asp = openmc.StatePoint("statepoint.analog.h5") + wsp = openmc.StatePoint("statepoint.ww.h5") analog_tally = asp.tallies[1] ww_tally = wsp.tallies[1] def compare_results(particle, analog_tally, ww_tally): # get values from each of the tallies - an_mean = analog_tally.get_values(filters=[openmc.ParticleFilter], - filter_bins=[(particle,)]) - ww_mean = ww_tally.get_values(filters=[openmc.ParticleFilter], - filter_bins=[(particle,)]) + an_mean = analog_tally.get_values( + filters=[openmc.ParticleFilter], filter_bins=[(particle,)] + ) + ww_mean = ww_tally.get_values( + filters=[openmc.ParticleFilter], filter_bins=[(particle,)] + ) # expect that more bins were scored with weight windows than # the analog run assert np.count_nonzero(an_mean) < np.count_nonzero(ww_mean) - an_rel_err = analog_tally.get_values(filters=[openmc.ParticleFilter], - filter_bins=[(particle,)], - value='rel_err') - ww_rel_err = ww_tally.get_values(filters=[openmc.ParticleFilter], - filter_bins=[(particle,)], - value='rel_err') + an_rel_err = analog_tally.get_values( + filters=[openmc.ParticleFilter], + filter_bins=[(particle,)], + value="rel_err", + ) + ww_rel_err = ww_tally.get_values( + filters=[openmc.ParticleFilter], + filter_bins=[(particle,)], + value="rel_err", + ) an_rel_err[an_mean == 0.0] = 1.0 ww_rel_err[ww_mean == 0.0] = 1.0 @@ -183,29 +183,31 @@ def compare_results(particle, analog_tally, ww_tally): # ensure that the value of the mesh bin containing the # source is statistically similar in both runs - an_std_dev = analog_tally.get_values(filters=[openmc.ParticleFilter], - filter_bins=[(particle,)], - value='std_dev') - ww_std_dev = ww_tally.get_values(filters=[openmc.ParticleFilter], - filter_bins=[(particle,)], - value='std_dev') + an_std_dev = analog_tally.get_values( + filters=[openmc.ParticleFilter], + filter_bins=[(particle,)], + value="std_dev", + ) + ww_std_dev = ww_tally.get_values( + filters=[openmc.ParticleFilter], + filter_bins=[(particle,)], + value="std_dev", + ) # index of the mesh bin containing the source for the higher # energy group - source_bin_idx = (an_mean.shape[0]//2, 0, 0) + source_bin_idx = (an_mean.shape[0] // 2, 0, 0) - an_source_bin = ufloat(an_mean[source_bin_idx], - an_std_dev[source_bin_idx]) - ww_source_bin = ufloat(ww_mean[source_bin_idx], - ww_std_dev[source_bin_idx]) + an_source_bin = ufloat(an_mean[source_bin_idx], an_std_dev[source_bin_idx]) + ww_source_bin = ufloat(ww_mean[source_bin_idx], ww_std_dev[source_bin_idx]) diff = an_source_bin - ww_source_bin # check that values are within two combined standard deviations assert abs(diff.nominal_value) / diff.std_dev < 2.0 - compare_results('neutron', analog_tally, ww_tally) - compare_results('photon', analog_tally, ww_tally) + compare_results("neutron", analog_tally, ww_tally) + compare_results("photon", analog_tally, ww_tally) def test_lower_ww_bounds_shape(): @@ -217,9 +219,9 @@ def test_lower_ww_bounds_shape(): ww = openmc.WeightWindows( mesh=ww_mesh, - lower_ww_bounds=[1]*24, + lower_ww_bounds=[1] * 24, upper_bound_ratio=5, - energy_bounds=(1, 1e40) + energy_bounds=(1, 1e40), ) assert ww.lower_ww_bounds.shape == (2, 3, 4, 1) @@ -233,12 +235,11 @@ def test_roundtrip(run_in_tmpdir, model, wws): # ensure that they can be read successfully from XML and that they match the input values model_read = openmc.Model.from_xml() - zipped_wws = zip(model.settings.weight_windows, - model_read.settings.weight_windows) + zipped_wws = zip(model.settings.weight_windows, model_read.settings.weight_windows) # ensure the lower bounds read in from the XML match those of the for ww_out, ww_in in zipped_wws: - assert(ww_out == ww_in) + assert ww_out == ww_in def test_ww_attrs_python(model): @@ -255,6 +256,7 @@ def test_ww_attrs_python(model): assert wwg.energy_bounds == None + def test_ww_attrs_capi(run_in_tmpdir, model): model.export_to_xml() @@ -279,11 +281,11 @@ def test_ww_attrs_capi(run_in_tmpdir, model): wws.particle = 1 assert wws.particle == openmc.ParticleType.PHOTON - wws.particle = 'photon' + wws.particle = "photon" assert wws.particle == openmc.ParticleType.PHOTON with pytest.raises(ValueError): - wws.particle = '🌠' + wws.particle = "🌠" energy_filter = tally.find_filter(openmc.lib.EnergyFilter) np.testing.assert_allclose(np.unique(energy_filter.bins), wws.energy_bounds) @@ -292,34 +294,36 @@ def test_ww_attrs_capi(run_in_tmpdir, model): assert all(wws.bounds[0] == -1) assert all(wws.bounds[1] == -1) - wws = openmc.lib.WeightWindows.from_tally(tally, particle='photon') + wws = openmc.lib.WeightWindows.from_tally(tally, particle="photon") assert wws.id == 2 assert wws.particle == openmc.ParticleType.PHOTON openmc.lib.finalize() -@pytest.mark.parametrize('library', ('libmesh', 'moab')) +@pytest.mark.parametrize("library", ("libmesh", "moab")) def test_unstructured_mesh_applied_wws(request, run_in_tmpdir, library): """ Ensure that weight windows on unstructured mesh work when they aren't part of a tally or weight window generator """ - if library == 'libmesh' and not openmc.lib._libmesh_enabled(): - pytest.skip('LibMesh not enabled in this build.') - if library == 'moab' and not openmc.lib._dagmc_enabled(): - pytest.skip('DAGMC (and MOAB) mesh not enabled in this build.') - - water = openmc.Material(name='water') - water.add_nuclide('H1', 2.0) - water.add_nuclide('O16', 1.0) - water.set_density('g/cc', 1.0) - box = openmc.model.RectangularParallelepiped(*(3*[-10, 10]), boundary_type='vacuum') + if library == "libmesh" and not openmc.lib._libmesh_enabled(): + pytest.skip("LibMesh not enabled in this build.") + if library == "moab" and not openmc.lib._dagmc_enabled(): + pytest.skip("DAGMC (and MOAB) mesh not enabled in this build.") + + water = openmc.Material(name="water") + water.add_nuclide("H1", 2.0) + water.add_nuclide("O16", 1.0) + water.set_density("g/cc", 1.0) + box = openmc.model.RectangularParallelepiped( + *(3 * [-10, 10]), boundary_type="vacuum" + ) cell = openmc.Cell(region=-box, fill=water) geometry = openmc.Geometry([cell]) - mesh_file = str(request.fspath.dirpath() / 'test_mesh_tets.exo') + mesh_file = str(request.fspath.dirpath() / "test_mesh_tets.exo") mesh = openmc.UnstructuredMesh(mesh_file, library) dummy_wws = np.ones((12_000,)) @@ -329,7 +333,7 @@ def test_unstructured_mesh_applied_wws(request, run_in_tmpdir, library): model = openmc.Model(geometry) model.settings.weight_windows = wws model.settings.weight_windows_on = True - model.settings.run_mode = 'fixed source' + model.settings.run_mode = "fixed source" model.settings.particles = 100 model.settings.batches = 2 model.run() diff --git a/tests/unit_tests/weightwindows/test_ww_gen.py b/tests/unit_tests/weightwindows/test_ww_gen.py index 555421461b7..0b02a73767f 100644 --- a/tests/unit_tests/weightwindows/test_ww_gen.py +++ b/tests/unit_tests/weightwindows/test_ww_gen.py @@ -15,21 +15,21 @@ def model(): ### Materials ### water = openmc.Material() - water.set_density('g/cc', 1.0) + water.set_density("g/cc", 1.0) water.add_nuclide("H1", 2) water.add_nuclide("O16", 1) steel = openmc.Material() - steel.set_density('g/cc', 8.0) + steel.set_density("g/cc", 8.0) steel.add_nuclide("Fe56", 1.0) air = openmc.Material() - air.set_density('g/cc', 0.001205) + air.set_density("g/cc", 0.001205) air.add_nuclide("N14", 0.781557629247) air.add_nuclide("O16", 0.210668126508) boron = openmc.Material() - boron.set_density('g/cc', 2.52) + boron.set_density("g/cc", 2.52) boron.add_nuclide("B10", 0.15856) boron.add_nuclide("B11", 0.64144) boron.add_nuclide("C0", 0.2) @@ -39,7 +39,7 @@ def model(): surfs = [openmc.Sphere(r=r) for r in radii] - surfs[-1].boundary_type = 'vacuum' + surfs[-1].boundary_type = "vacuum" regions = openmc.model.subdivide(surfs) @@ -52,11 +52,11 @@ def model(): ### Settings ### settings = openmc.Settings( - run_mode='fixed source', + run_mode="fixed source", particles=100, batches=10, max_history_splits=10, - survival_biasing=False + survival_biasing=False, ) # 10 keV neutron point source at the origin @@ -79,7 +79,7 @@ def model(): ef = openmc.EnergyFilter([0.0, 1e7]) -pf = openmc.ParticleFilter(['neutron', 'photon']) +pf = openmc.ParticleFilter(["neutron", "photon"]) filters = [mf, ef, pf] @@ -92,12 +92,12 @@ def labels(params): out = [] for p in params: if isinstance(p, openmc.ParticleFilter): - out.append('particle') + out.append("particle") elif isinstance(p, openmc.MeshFilter): - out.append('mesh') + out.append("mesh") elif isinstance(p, openmc.EnergyFilter): - out.append('energy') - return "filters:" + '-'.join(out) + out.append("energy") + return "filters:" + "-".join(out) @pytest.mark.parametrize("filters", test_cases, ids=labels) @@ -105,7 +105,7 @@ def test_ww_gen(filters, run_in_tmpdir, model): tally = openmc.Tally() tally.filters = list(filters) - tally.scores = ['flux'] + tally.scores = ["flux"] model.tallies = openmc.Tallies([tally]) model.export_to_model_xml() @@ -177,11 +177,11 @@ def test_ww_import_export(run_in_tmpdir, model): e_groups = np.logspace(0, 7, 8) ef = openmc.EnergyFilter(e_groups) - pf = openmc.ParticleFilter(['neutron', 'photon']) + pf = openmc.ParticleFilter(["neutron", "photon"]) tally = openmc.Tally() tally.filters = [mf, ef, pf] - tally.scores = ['flux'] + tally.scores = ["flux"] model.tallies = openmc.Tallies([tally]) @@ -221,9 +221,9 @@ def test_ww_import_export(run_in_tmpdir, model): openmc.lib.export_weight_windows() - assert Path('weight_windows.h5').exists() + assert Path("weight_windows.h5").exists() - openmc.lib.import_weight_windows('weight_windows.h5') + openmc.lib.import_weight_windows("weight_windows.h5") imported_ww = openmc.lib.weight_windows[2] @@ -244,12 +244,10 @@ def test_ww_gen_roundtrip(run_in_tmpdir, model): mesh = openmc.RegularMesh.from_domain(model.geometry.root_universe) energy_bounds = np.linspace(0.0, 1e8, 11) - particle_type = 'neutron' + particle_type = "neutron" wwg = openmc.WeightWindowGenerator(mesh, energy_bounds, particle_type) - wwg.update_parameters = {'ratio' : 5.0, - 'threshold': 0.8, - 'value' : 'mean'} + wwg.update_parameters = {"ratio": 5.0, "threshold": 0.8, "value": "mean"} model.settings.weight_window_generators = wwg model.export_to_xml() @@ -270,10 +268,10 @@ def test_ww_gen_roundtrip(run_in_tmpdir, model): assert wwg_in.update_parameters == wwg.update_parameters with pytest.raises(ValueError): - wwg.method = 'šŸ¦šŸ’' + wwg.method = "šŸ¦šŸ’" with pytest.raises(TypeError): - wwg.update_parameters = {'ratio' : 'one-to-one'} + wwg.update_parameters = {"ratio": "one-to-one"} with pytest.raises(ValueError): wwg.max_realizations = -1 @@ -316,7 +314,7 @@ def test_python_hdf5_roundtrip(run_in_tmpdir, model): def test_ww_bounds_set_in_memory(run_in_tmpdir, model): tally = openmc.Tally() tally.filters = filters - tally.scores = ['flux'] + tally.scores = ["flux"] model.tallies = [tally] bounds = np.arange(ef.num_bins * np.prod(mf.mesh.dimension)) diff --git a/tests/unit_tests/weightwindows/test_wwinp_reader.py b/tests/unit_tests/weightwindows/test_wwinp_reader.py index 434a36753ab..1976984dfb1 100644 --- a/tests/unit_tests/weightwindows/test_wwinp_reader.py +++ b/tests/unit_tests/weightwindows/test_wwinp_reader.py @@ -17,42 +17,32 @@ # expected retults - neutron data only n_mesh = openmc.RectilinearMesh() -n_mesh.x_grid = np.array([-100.0, - -99.0, - -97.0, - -79.3636, - -61.7273, - -44.0909, - -26.4546, - -8.81818, - 8.81818, - 26.4546, - 44.0909, - 61.7273, - 79.3636, - 97.0, - 99.0, - 100]) -n_mesh.y_grid = np.array([-100.0, - -50.0, - -13.3333, - 23.3333, - 60.0, - 70.0, - 80.0, - 90.0, - 100.0]) -n_mesh.z_grid = np.array([-100.0, - -66.6667, - -33.3333, - 0.0, - 33.3333, - 66.6667, - 100.0]) -n_e_bounds = (np.array([0.0, - 100000.0, - 146780.0]),) -n_particles = ('neutron',) +n_mesh.x_grid = np.array( + [ + -100.0, + -99.0, + -97.0, + -79.3636, + -61.7273, + -44.0909, + -26.4546, + -8.81818, + 8.81818, + 26.4546, + 44.0909, + 61.7273, + 79.3636, + 97.0, + 99.0, + 100, + ] +) +n_mesh.y_grid = np.array( + [-100.0, -50.0, -13.3333, 23.3333, 60.0, 70.0, 80.0, 90.0, 100.0] +) +n_mesh.z_grid = np.array([-100.0, -66.6667, -33.3333, 0.0, 33.3333, 66.6667, 100.0]) +n_e_bounds = (np.array([0.0, 100000.0, 146780.0]),) +n_particles = ("neutron",) # expected results - neutron and photon data np_mesh = openmc.RectilinearMesh() @@ -61,9 +51,8 @@ np_mesh.y_grid = n_mesh.y_grid np_mesh.z_grid = n_mesh.z_grid -np_e_bounds = (np.array([0.0, 100000.0, 146780.0, 215440.0]), - np.array([0.0, 1.0E8])) -np_particles = ('neutron', 'photon') +np_e_bounds = (np.array([0.0, 100000.0, 146780.0, 215440.0]), np.array([0.0, 1.0e8])) +np_particles = ("neutron", "photon") # expected results - photon data only p_mesh = openmc.RectilinearMesh() @@ -74,25 +63,27 @@ p_mesh.z_grid = np.array([-50.0, 50.0]) p_e_bounds = (np.array([0.0, 100000.0, 146780.0, 215440.0, 316230.0]),) -p_particles = ('photon',) +p_particles = ("photon",) -expected_results = [('wwinp_n', n_mesh, n_particles, n_e_bounds), - ('wwinp_np', np_mesh, np_particles, np_e_bounds), - ('wwinp_p', p_mesh, p_particles, p_e_bounds)] +expected_results = [ + ("wwinp_n", n_mesh, n_particles, n_e_bounds), + ("wwinp_np", np_mesh, np_particles, np_e_bounds), + ("wwinp_p", p_mesh, p_particles, p_e_bounds), +] # function for printing readable test labels def id_fn(params): - suffix = params[0].split('_')[-1] - if suffix == 'n': - return 'neutron-only' - elif suffix == 'np': - return 'neutron-photon' - elif suffix == 'p': - return 'photon-only' + suffix = params[0].split("_")[-1] + if suffix == "n": + return "neutron-only" + elif suffix == "np": + return "neutron-photon" + elif suffix == "p": + return "photon-only" -@pytest.mark.parametrize('wwinp_data', expected_results, ids=id_fn) +@pytest.mark.parametrize("wwinp_data", expected_results, ids=id_fn) def test_wwinp_reader(wwinp_data, request): wwinp_file, mesh, particle_types, energy_bounds = wwinp_data @@ -124,18 +115,17 @@ def test_wwinp_reader(wwinp_data, request): # check expected failures def fail_id_fn(params): - suffix = params[0].split('_')[-1] - if suffix == 't': - return 'time-steps-failure' - elif suffix == 'cyl': - return 'cyl-mesh-failure' + suffix = params[0].split("_")[-1] + if suffix == "t": + return "time-steps-failure" + elif suffix == "cyl": + return "cyl-mesh-failure" -expected_failure_data = (('wwinp_t', ValueError), - ('wwinp_cyl', NotImplementedError)) +expected_failure_data = (("wwinp_t", ValueError), ("wwinp_cyl", NotImplementedError)) -@pytest.mark.parametrize('wwinp_data', expected_failure_data, ids=fail_id_fn) +@pytest.mark.parametrize("wwinp_data", expected_failure_data, ids=fail_id_fn) def test_wwinp_reader_failures(wwinp_data, request): filename, expected_failure = wwinp_data