From c1c6865026b687a2904e08eea38dc9329e8d40de Mon Sep 17 00:00:00 2001 From: Christina Cai Date: Wed, 12 Apr 2023 14:00:48 -0400 Subject: [PATCH 01/29] added type hinting for weight_windows.py --- openmc/weight_windows.py | 56 ++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/openmc/weight_windows.py b/openmc/weight_windows.py index 65b9b7bb0..b825796dc 100644 --- a/openmc/weight_windows.py +++ b/openmc/weight_windows.py @@ -1,10 +1,12 @@ from __future__ import annotations from collections.abc import Iterable from numbers import Real, Integral -from typing import Iterable, List +from typing import Iterable, List, Optional, Union +import pathlib from xml.etree import ElementTree as ET import numpy as np +import h5py from openmc.filter import _PARTICLES from openmc.mesh import MeshBase, RectilinearMesh, UnstructuredMesh @@ -18,7 +20,7 @@ class WeightWindows(IDManagerMixin): """Mesh-based weight windows This class enables you to specify weight window parameters that are used in - a simulation. Multiple sets of weight windows can be defined for different + a simulation. Multiple sets of weight windows can be defined for differen t meshes and different particles. An iterable of :class:`WeightWindows` instances can be assigned to the :attr:`openmc.Settings.weight_windows` attribute, which is then exported to XML. @@ -103,16 +105,20 @@ class WeightWindows(IDManagerMixin): next_id = 1 used_ids = set() - def __init__(self, mesh, lower_ww_bounds, - upper_ww_bounds=None, - upper_bound_ratio=None, - energy_bounds=None, - particle_type='neutron', - survival_ratio=3, - max_lower_bound_ratio=None, - max_split=10, - weight_cutoff=1.e-38, - id=None): + def __init__( + self, + mesh: MeshBase, + lower_ww_bounds: np.ndarray, + upper_ww_bounds: Optional[np.ndarray] = None, + upper_bound_ratio: Optional[float] = None, + energy_bounds: Optional[Iterable[Real]] = None, + particle_type: str = 'neutron', + survival_ratio: float = 3, + max_lower_bound_ratio: Optional[float] = None, + max_split: int = 10, + weight_cutoff: float = 1.e-38, + id: Optional[int] = None + ): self.mesh = mesh self.id = id self.particle_type = particle_type @@ -161,7 +167,7 @@ class WeightWindows(IDManagerMixin): string += '{: <16}=\t{}\n'.format('\tWeight Cutoff', self._weight_cutoff) return string - def __eq__(self, other) -> bool: + def __eq__(self, other: WeightWindows) -> bool: # ensure that `other` is a WeightWindows object if not isinstance(other, WeightWindows): return False @@ -195,7 +201,7 @@ class WeightWindows(IDManagerMixin): return self._mesh @mesh.setter - def mesh(self, mesh): + def mesh(self, mesh: MeshBase): cv.check_type('Weight window mesh', mesh, MeshBase) self._mesh = mesh @@ -204,7 +210,7 @@ class WeightWindows(IDManagerMixin): return self._particle_type @particle_type.setter - def particle_type(self, pt): + def particle_type(self, pt: str): cv.check_value('Particle type', pt, _PARTICLES) self._particle_type = pt @@ -213,7 +219,7 @@ class WeightWindows(IDManagerMixin): return self._energy_bounds @energy_bounds.setter - def energy_bounds(self, bounds): + def energy_bounds(self, bounds: Iterable[Real]): cv.check_type('Energy bounds', bounds, Iterable, Real) self._energy_bounds = np.asarray(bounds) @@ -228,7 +234,7 @@ class WeightWindows(IDManagerMixin): return self._lower_ww_bounds @lower_ww_bounds.setter - def lower_ww_bounds(self, bounds): + def lower_ww_bounds(self, bounds: Iterable[Real]): cv.check_iterable_type('Lower WW bounds', bounds, Real, @@ -247,7 +253,7 @@ class WeightWindows(IDManagerMixin): return self._upper_ww_bounds @upper_ww_bounds.setter - def upper_ww_bounds(self, bounds): + def upper_ww_bounds(self, bounds: Iterable[Real]): cv.check_iterable_type('Upper WW bounds', bounds, Real, @@ -266,7 +272,7 @@ class WeightWindows(IDManagerMixin): return self._survival_ratio @survival_ratio.setter - def survival_ratio(self, val): + def survival_ratio(self, val: float): cv.check_type('Survival ratio', val, Real) cv.check_greater_than('Survival ratio', val, 1.0, True) self._survival_ratio = val @@ -276,7 +282,7 @@ class WeightWindows(IDManagerMixin): return self._max_lower_bound_ratio @max_lower_bound_ratio.setter - def max_lower_bound_ratio(self, val): + 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) self._max_lower_bound_ratio = val @@ -286,7 +292,7 @@ class WeightWindows(IDManagerMixin): return self._max_split @max_split.setter - def max_split(self, val): + def max_split(self, val: int): cv.check_type('Max split', val, Integral) self._max_split = val @@ -295,7 +301,7 @@ class WeightWindows(IDManagerMixin): return self._weight_cutoff @weight_cutoff.setter - def weight_cutoff(self, cutoff): + def weight_cutoff(self, cutoff: float): cv.check_type('Weight cutoff', cutoff, Real) cv.check_greater_than('Weight cutoff', cutoff, 0.0, True) self._weight_cutoff = cutoff @@ -343,7 +349,7 @@ class WeightWindows(IDManagerMixin): return element @classmethod - def from_xml_element(cls, elem, root) -> WeightWindows: + def from_xml_element(cls, elem: ET.Element, root: ET.Element) -> WeightWindows: """Generate weight window settings from an XML element Parameters @@ -398,7 +404,7 @@ class WeightWindows(IDManagerMixin): ) @classmethod - def from_hdf5(cls, group, meshes) -> WeightWindows: + def from_hdf5(cls, group: h5py.Group, meshes: dict) -> WeightWindows: """Create weight windows from HDF5 group Parameters @@ -443,7 +449,7 @@ class WeightWindows(IDManagerMixin): ) -def wwinp_to_wws(path) -> List[WeightWindows]: +def wwinp_to_wws(path: Union[str, pathlib.Path]) -> List[WeightWindows]: """Create WeightWindows instances from a wwinp file .. versionadded:: 0.13.1 From 1a0f442bba581b17e87b57b1adabdfede43d04f7 Mon Sep 17 00:00:00 2001 From: Christina Cai Date: Wed, 12 Apr 2023 14:22:17 -0400 Subject: [PATCH 02/29] added return types for model.py --- openmc/model/model.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/openmc/model/model.py b/openmc/model/model.py index 4202cf60b..dd9251804 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -7,6 +7,7 @@ from numbers import Integral from tempfile import NamedTemporaryFile import warnings from xml.etree import ElementTree as ET +from typing import Optional import h5py @@ -16,6 +17,7 @@ from openmc.dummy_comm import DummyCommunicator from openmc.executor import _process_CLI_arguments from openmc.checkvalue import check_type, check_value from openmc.exceptions import InvalidIDError +from openmc.model import Model @contextmanager @@ -93,27 +95,27 @@ class Model: self.plots = plots @property - def geometry(self): + def geometry(self) -> Optional[openmc.Geometry]: return self._geometry @property - def materials(self): + def materials(self) -> Optional[openmc.Materials]: return self._materials @property - def settings(self): + def settings(self) -> Optional[openmc.Settings]: return self._settings @property - def tallies(self): + def tallies(self) -> Optional[openmc.Tallies]: return self._tallies @property - def plots(self): + def plots(self) -> Optional[openmc.Plots]: return self._plots @property - def is_initialized(self): + def is_initialized(self) -> bool: try: import openmc.lib return openmc.lib.is_initialized @@ -122,7 +124,7 @@ class Model: @property @lru_cache(maxsize=None) - def _materials_by_id(self): + def _materials_by_id(self) -> dict: """Dictionary mapping material ID --> material""" if self.materials: mats = self.materials @@ -132,14 +134,14 @@ class Model: @property @lru_cache(maxsize=None) - def _cells_by_id(self): + def _cells_by_id(self) -> dict: """Dictionary mapping cell ID --> cell""" cells = self.geometry.get_all_cells() return {cell.id: cell for cell in cells.values()} @property @lru_cache(maxsize=None) - def _cells_by_name(self): + def _cells_by_name(self) -> dict: # Get the names maps, but since names are not unique, store a set for # each name key. In this way when the user requests a change by a name, # the change will be applied to all of the same name. @@ -152,7 +154,7 @@ class Model: @property @lru_cache(maxsize=None) - def _materials_by_name(self): + def _materials_by_name(self) -> dict: if self.materials is None: mats = self.geometry.get_all_materials().values() else: @@ -207,7 +209,7 @@ class Model: @classmethod def from_xml(cls, geometry='geometry.xml', materials='materials.xml', settings='settings.xml', tallies='tallies.xml', - plots='plots.xml'): + plots='plots.xml') -> Model: """Create model from existing XML files Parameters @@ -597,7 +599,7 @@ class Model: 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_model_xml=True) -> Optional[Path]: """Runs OpenMC. If the C API has been initialized, then the C API is used, otherwise, this method creates the XML files and runs OpenMC via a system call. In both cases this method returns the path to the last From 3692e7331fc3315b208b9e794f4ef534b7fd4664 Mon Sep 17 00:00:00 2001 From: Christina Cai Date: Wed, 12 Apr 2023 14:41:14 -0400 Subject: [PATCH 03/29] added return types for geometry.py --- openmc/geometry.py | 57 ++++++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/openmc/geometry.py b/openmc/geometry.py index ad86cc56b..88871c544 100644 --- a/openmc/geometry.py +++ b/openmc/geometry.py @@ -1,12 +1,15 @@ +from __future__ import annotations import os -import typing +import typing from collections import OrderedDict, defaultdict from collections.abc import Iterable from copy import deepcopy from pathlib import Path -from xml.etree import ElementTree as ET import warnings +from xml.etree import ElementTree as ET +import numpy as np + import openmc import openmc._xml as xml from .checkvalue import check_type, check_less_than, check_greater_than, PathLike @@ -51,19 +54,19 @@ class Geometry: self._root_universe = univ @property - def root_universe(self): + def root_universe(self) -> openmc.UniverseBase: return self._root_universe @property - def bounding_box(self): + def bounding_box(self) -> np.ndarray: return self.root_universe.bounding_box @property - def merge_surfaces(self): + def merge_surfaces(self) -> bool: return self._merge_surfaces @property - def surface_precision(self): + def surface_precision(self) -> int: return self._surface_precision @root_universe.setter @@ -105,7 +108,7 @@ class Geometry: if universe.id in volume_calc.volumes: universe.add_volume_information(volume_calc) - def to_xml_element(self, remove_surfs=False): + def to_xml_element(self, remove_surfs=False) -> ET.Element: """Creates a 'geometry' element to be written to an XML file. Parameters @@ -164,7 +167,7 @@ class Geometry: tree.write(str(p), xml_declaration=True, encoding='utf-8') @classmethod - def from_xml_element(cls, elem, materials=None): + def from_xml_element(cls, elem, materials=None) -> openmc.Geometry: """Generate geometry from an XML element Parameters @@ -260,7 +263,7 @@ class Geometry: cls, path: PathLike = 'geometry.xml', materials: typing.Optional[typing.Union[PathLike, 'openmc.Materials']] = 'materials.xml' - ): + ) -> openmc.Geometry: """Generate geometry from XML file Parameters @@ -290,7 +293,7 @@ class Geometry: return cls.from_xml_element(root, materials) - def find(self, point): + def find(self, point) -> list: """Find cells/universes/lattices which contain a given point Parameters @@ -307,7 +310,7 @@ class Geometry: """ return self.root_universe.find(point) - def get_instances(self, paths): + def get_instances(self, paths) -> typing.Union[int, typing.List[int]]: """Return the instance number(s) for a cell/material in a geometry path. The instance numbers are used as indices into distributed @@ -354,7 +357,7 @@ class Geometry: return indices if return_list else indices[0] - def get_all_cells(self): + def get_all_cells(self) -> OrderedDict: """Return all cells in the geometry. Returns @@ -368,7 +371,7 @@ class Geometry: else: return OrderedDict() - def get_all_universes(self): + def get_all_universes(self) -> OrderedDict: """Return all universes in the geometry. Returns @@ -383,7 +386,7 @@ class Geometry: universes.update(self.root_universe.get_all_universes()) return universes - def get_all_materials(self): + def get_all_materials(self) -> OrderedDict: """Return all materials within the geometry. Returns @@ -398,7 +401,7 @@ class Geometry: else: return OrderedDict() - def get_all_material_cells(self): + def get_all_material_cells(self) -> OrderedDict: """Return all cells filled by a material Returns @@ -417,7 +420,7 @@ class Geometry: return material_cells - def get_all_material_universes(self): + def get_all_material_universes(self) -> OrderedDict: """Return all universes having at least one material-filled cell. This method can be used to find universes that have at least one cell @@ -440,7 +443,7 @@ class Geometry: return material_universes - def get_all_lattices(self): + def get_all_lattices(self) -> OrderedDict: """Return all lattices defined Returns @@ -458,7 +461,7 @@ class Geometry: return lattices - def get_all_surfaces(self): + def get_all_surfaces(self) -> OrderedDict: """ Return all surfaces used in the geometry @@ -475,7 +478,7 @@ class Geometry: surfaces = cell.region.get_surfaces(surfaces) return surfaces - def _get_domains_by_name(self, name, case_sensitive, matching, domain_type): + def _get_domains_by_name(self, name, case_sensitive, matching, domain_type) -> list: if not case_sensitive: name = name.lower() @@ -492,7 +495,7 @@ class Geometry: domains.sort(key=lambda x: x.id) return domains - def get_materials_by_name(self, name, case_sensitive=False, matching=False): + def get_materials_by_name(self, name, case_sensitive=False, matching=False) -> typing.List[openmc.Material]: """Return a list of materials with matching names. Parameters @@ -513,7 +516,7 @@ class Geometry: """ return self._get_domains_by_name(name, case_sensitive, matching, 'material') - def get_cells_by_name(self, name, case_sensitive=False, matching=False): + def get_cells_by_name(self, name, case_sensitive=False, matching=False) -> typing.List[openmc.Cell]: """Return a list of cells with matching names. Parameters @@ -534,7 +537,7 @@ class Geometry: """ return self._get_domains_by_name(name, case_sensitive, matching, 'cell') - def get_surfaces_by_name(self, name, case_sensitive=False, matching=False): + def get_surfaces_by_name(self, name, case_sensitive=False, matching=False) -> typing.List[openmc.Surface]: """Return a list of surfaces with matching names. .. versionadded:: 0.13.3 @@ -557,7 +560,7 @@ class Geometry: """ return self._get_domains_by_name(name, case_sensitive, matching, 'surface') - def get_cells_by_fill_name(self, name, case_sensitive=False, matching=False): + def get_cells_by_fill_name(self, name, case_sensitive=False, matching=False) -> typing.List[openmc.Cell]: """Return a list of cells with fills with matching names. Parameters @@ -602,7 +605,7 @@ class Geometry: return sorted(cells, key=lambda x: x.id) - def get_universes_by_name(self, name, case_sensitive=False, matching=False): + def get_universes_by_name(self, name, case_sensitive=False, matching=False) -> typing.List[openmc.Universe]: """Return a list of universes with matching names. Parameters @@ -623,7 +626,7 @@ class Geometry: """ return self._get_domains_by_name(name, case_sensitive, matching, 'universe') - def get_lattices_by_name(self, name, case_sensitive=False, matching=False): + def get_lattices_by_name(self, name, case_sensitive=False, matching=False) -> typing.List[openmc.Lattice]: """Return a list of lattices with matching names. Parameters @@ -644,7 +647,7 @@ class Geometry: """ return self._get_domains_by_name(name, case_sensitive, matching, 'lattice') - def remove_redundant_surfaces(self): + def remove_redundant_surfaces(self) -> dict: """Remove and return all of the redundant surfaces. Uses surface_precision attribute of Geometry instance for rounding and @@ -707,7 +710,7 @@ class Geometry: # Recursively traverse the CSG tree to count all cell instances self.root_universe._determine_paths(instances_only=instances_only) - def clone(self): + def clone(self) -> Geometry: """Create a copy of this geometry with new unique IDs for all of its enclosed materials, surfaces, cells, universes and lattices.""" From f7457d6427aa708f7140782b9447af44b80150c5 Mon Sep 17 00:00:00 2001 From: Christina Cai Date: Wed, 12 Apr 2023 15:11:13 -0400 Subject: [PATCH 04/29] fixed circular import error --- openmc/model/model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmc/model/model.py b/openmc/model/model.py index dd9251804..caf630180 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -1,3 +1,4 @@ +from __future__ import annotations from collections.abc import Iterable from contextlib import contextmanager from functools import lru_cache @@ -17,7 +18,6 @@ from openmc.dummy_comm import DummyCommunicator from openmc.executor import _process_CLI_arguments from openmc.checkvalue import check_type, check_value from openmc.exceptions import InvalidIDError -from openmc.model import Model @contextmanager From 5fc51209ce3faf0761ac2e697cde0c921fd9e181 Mon Sep 17 00:00:00 2001 From: Christina Cai Date: Wed, 12 Apr 2023 15:21:49 -0400 Subject: [PATCH 05/29] added return types for material.py --- openmc/material.py | 73 +++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/openmc/material.py b/openmc/material.py index a2c6138d2..66a626668 100644 --- a/openmc/material.py +++ b/openmc/material.py @@ -1,3 +1,4 @@ +from __future__ import annotations from collections import OrderedDict, defaultdict, namedtuple, Counter from collections.abc import Iterable from copy import deepcopy @@ -6,7 +7,7 @@ from pathlib import Path import re import typing # imported separately as py3.8 requires typing.Iterable import warnings -from typing import Optional +from typing import Optional, List, Union from xml.etree import ElementTree as ET import h5py @@ -134,7 +135,7 @@ class Material(IDManagerMixin): # If specified, a list of table names self._sab = [] - def __repr__(self): + def __repr__(self) -> str: string = 'Material\n' string += '{: <16}=\t{}\n'.format('\tID', self._id) string += '{: <16}=\t{}\n'.format('\tName', self._name) @@ -166,34 +167,34 @@ class Material(IDManagerMixin): return string @property - def name(self): + def name(self) -> Optional[str]: return self._name @property - def temperature(self): + def temperature(self) -> Optional[float]: return self._temperature @property - def density(self): + def density(self) -> float: return self._density @property - def density_units(self): + def density_units(self) -> str: return self._density_units @property - def depletable(self): + def depletable(self) -> bool: return self._depletable @property - def paths(self): + 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.') return self._paths @property - def num_instances(self): + def num_instances(self) -> int: if self._num_instances is None: raise ValueError( 'Number of material instances have not been determined. Call ' @@ -201,15 +202,15 @@ class Material(IDManagerMixin): return self._num_instances @property - def nuclides(self): + def nuclides(self) -> List[namedtuple]: return self._nuclides @property - def isotropic(self): + def isotropic(self) -> List[str]: return self._isotropic @property - def average_molar_mass(self): + 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. @@ -226,11 +227,11 @@ class Material(IDManagerMixin): return mass / moles @property - def volume(self): + def volume(self) -> float: return self._volume @property - def ncrystal_cfg(self): + def ncrystal_cfg(self) -> str: return self._ncrystal_cfg @name.setter @@ -267,7 +268,7 @@ class Material(IDManagerMixin): self._isotropic = list(isotropic) @property - def fissionable_mass(self): + def fissionable_mass(self) -> float: if self.volume is None: raise ValueError("Volume must be set in order to determine mass.") density = 0.0 @@ -291,7 +292,7 @@ class Material(IDManagerMixin): return openmc.data.combine_distributions(dists, probs) if dists else None @classmethod - def from_hdf5(cls, group: h5py.Group): + def from_hdf5(cls, group: h5py.Group) -> openmc.Material: """Create material from HDF5 group Parameters @@ -346,7 +347,7 @@ class Material(IDManagerMixin): return material @classmethod - def from_ncrystal(cls, cfg, **kwargs): + def from_ncrystal(cls, cfg, **kwargs) -> openmc.Material: """Create material from NCrystal configuration string. Density, temperature, and material composition, and (ultimately) thermal @@ -881,7 +882,7 @@ class Material(IDManagerMixin): def make_isotropic_in_lab(self): self.isotropic = [x.name for x in self._nuclides] - def get_elements(self): + def get_elements(self) -> List[str]: """Returns all elements in the material .. versionadded:: 0.12 @@ -895,7 +896,7 @@ class Material(IDManagerMixin): return sorted({re.split(r'(\d+)', i)[0] for i in self.get_nuclides()}) - def get_nuclides(self, element: Optional[str] = None): + def get_nuclides(self, element: Optional[str] = None) -> List[str]: """Returns a list of all nuclides in the material, if the element argument is specified then just nuclides of that element are returned. @@ -925,7 +926,7 @@ class Material(IDManagerMixin): return matching_nuclides - def get_nuclide_densities(self): + def get_nuclide_densities(self) -> dict: """Returns all nuclides in the material and their densities Returns @@ -944,7 +945,7 @@ class Material(IDManagerMixin): return nuclides - def get_nuclide_atom_densities(self, nuclide: Optional[str] = None): + def get_nuclide_atom_densities(self, nuclide: Optional[str] = None) -> dict: """Returns one or all nuclides in the material and their atomic densities in units of atom/b-cm @@ -1030,7 +1031,7 @@ class Material(IDManagerMixin): return nuclides def get_activity(self, units: str = 'Bq/cm3', by_nuclide: bool = False, - volume: Optional[float] = None): + volume: Optional[float] = None) -> Union[dict, float]: """Returns the activity of the material or for each nuclide in the material in units of [Bq], [Bq/g] or [Bq/cm3]. @@ -1077,7 +1078,7 @@ class Material(IDManagerMixin): return activity if by_nuclide else sum(activity.values()) def get_decay_heat(self, units: str = 'W', by_nuclide: bool = False, - volume: Optional[float] = None): + volume: Optional[float] = None) -> Union[dict, float]: """Returns the decay heat of the material or for each nuclide in the material in units of [W], [W/g] or [W/cm3]. @@ -1125,7 +1126,7 @@ class Material(IDManagerMixin): return decayheat if by_nuclide else sum(decayheat.values()) - def get_nuclide_atoms(self, volume: Optional[float] = None): + def get_nuclide_atoms(self, volume: Optional[float] = None) -> dict: """Return number of atoms of each nuclide in the material .. versionadded:: 0.13.1 @@ -1154,7 +1155,7 @@ class Material(IDManagerMixin): atoms[nuclide] = 1.0e24 * atom_per_bcm * volume return atoms - def get_mass_density(self, nuclide: Optional[str] = None): + def get_mass_density(self, nuclide: Optional[str] = None) -> float: """Return mass density of one or all nuclides Parameters @@ -1176,7 +1177,7 @@ class Material(IDManagerMixin): mass_density += density_i return mass_density - def get_mass(self, nuclide: Optional[str] = None, volume: Optional[float] = None): + def get_mass(self, nuclide: Optional[str] = None, volume: Optional[float] = None) -> float: """Return mass of one or all nuclides. Note that this method requires that the :attr:`Material.volume` has @@ -1206,7 +1207,7 @@ class Material(IDManagerMixin): raise ValueError("Volume must be set in order to determine mass.") return volume*self.get_mass_density(nuclide) - def clone(self, memo: Optional[dict] = None): + def clone(self, memo: Optional[dict] = None) -> Material: """Create a copy of this material with a new unique ID. Parameters @@ -1245,7 +1246,7 @@ class Material(IDManagerMixin): return memo[self] - def _get_nuclide_xml(self, nuclide: str): + def _get_nuclide_xml(self, nuclide: str) -> ET.Element: xml_element = ET.Element("nuclide") xml_element.set("name", nuclide.name) @@ -1256,19 +1257,19 @@ class Material(IDManagerMixin): return xml_element - def _get_macroscopic_xml(self, macroscopic: str): + def _get_macroscopic_xml(self, macroscopic: str) -> ET.Element: xml_element = ET.Element("macroscopic") xml_element.set("name", macroscopic) return xml_element - def _get_nuclides_xml(self, nuclides: typing.Iterable[str]): + def _get_nuclides_xml(self, nuclides: typing.Iterable[str]) -> List[ET.Element]: xml_elements = [] for nuclide in nuclides: xml_elements.append(self._get_nuclide_xml(nuclide)) return xml_elements - def to_xml_element(self): + def to_xml_element(self) -> ET.Element: """Return XML representation of the material Returns @@ -1338,7 +1339,7 @@ class Material(IDManagerMixin): @classmethod def mix_materials(cls, materials, fracs: typing.Iterable[float], - percent_type: str = 'ao', name: Optional[str] = None): + percent_type: str = 'ao', name: Optional[str] = None) -> Material: """Mix materials together based on atom, weight, or volume fractions .. versionadded:: 0.12 @@ -1436,7 +1437,7 @@ class Material(IDManagerMixin): return new_mat @classmethod - def from_xml_element(cls, elem: ET.Element): + def from_xml_element(cls, elem: ET.Element) -> Material: """Generate material from an XML element Parameters @@ -1530,7 +1531,7 @@ class Materials(cv.CheckedList): self += materials @property - def cross_sections(self): + def cross_sections(self) -> Optional[Union[str, Path]]: return self._cross_sections @cross_sections.setter @@ -1638,7 +1639,7 @@ class Materials(cv.CheckedList): self._write_xml(fh) @classmethod - def from_xml_element(cls, elem): + def from_xml_element(cls, elem) -> Material: """Generate materials collection from XML file Parameters @@ -1665,7 +1666,7 @@ class Materials(cv.CheckedList): return materials @classmethod - def from_xml(cls, path: PathLike = 'materials.xml'): + def from_xml(cls, path: PathLike = 'materials.xml') -> Material: """Generate materials collection from XML file Parameters From 6b03287d52aa8b55320ee99f63d0d993b93eb375 Mon Sep 17 00:00:00 2001 From: Gavin Ridley Date: Thu, 13 Apr 2023 16:09:07 -0400 Subject: [PATCH 06/29] add auto legend to universe.plot --- openmc/universe.py | 50 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 7 deletions(-) diff --git a/openmc/universe.py b/openmc/universe.py index 2ed96f4bc..a75af3b0d 100644 --- a/openmc/universe.py +++ b/openmc/universe.py @@ -87,7 +87,8 @@ class UniverseBase(ABC, IDManagerMixin): 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.') @@ -168,7 +169,7 @@ class UniverseBase(ABC, IDManagerMixin): clone._cells = OrderedDict() for cell in self._cells.values(): clone.add_cell(cell.clone(clone_materials, clone_regions, - memo)) + memo)) # Memoize the clone memo[self] = clone @@ -295,7 +296,7 @@ class Universe(UniverseBase): def plot(self, origin=(0., 0., 0.), width=(1., 1.), pixels=(200, 200), basis='xy', color_by='cell', colors=None, seed=None, - openmc_exec='openmc', axes=None, **kwargs): + openmc_exec='openmc', axes=None, legend=False, **kwargs): """Display a slice plot of the universe. Parameters @@ -329,6 +330,8 @@ class Universe(UniverseBase): Axes to draw to .. versionadded:: 0.13.1 + legend : bool + whether a legend showing material or cell names should be drawn **kwargs Keyword arguments passed to :func:`matplotlib.pyplot.imshow` @@ -339,6 +342,7 @@ class Universe(UniverseBase): """ import matplotlib.image as mpimg + import matplotlib.patches as mpatches import matplotlib.pyplot as plt # Determine extents of plot @@ -396,6 +400,36 @@ class Universe(UniverseBase): height = pixels[0]*px/(params.top - params.bottom) fig.set_size_inches(width, height) + # add legend showing which colors represent which material + # or cell if that was requested + if legend: + if plot.colors is None: + raise Exception("Must set plot color dictionary if you would" + " like to have an automatic legend.") + + if color_by == "cell": + expected_key_type = openmc.Cell + elif color_by == "material": + expected_key_type = openmc.Material + else: + raise Exception("wtf?") + + patches = [] + for key, color in plot.colors.items(): + + if isinstance(key, int): + raise Exception( + "Cannot use IDs in colors dict for auto legend.") + elif not isinstance(key, expected_key_type): + raise Exception( + "Color dict key type does not match color_by") + + # this works whether we're doing cells or materials + key_patch = mpatches.Patch(color=color, label=key.name) + patches.append(key_patch) + + axes.legend(handles=patches) + # Plot image and return the axes return axes.imshow(img, extent=(x_min, x_max, y_min, y_max), **kwargs) @@ -738,8 +772,9 @@ class DAGMCUniverse(UniverseBase): @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_ascii=[] + 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', '') # tags might be for temperature or reflective surfaces @@ -905,7 +940,8 @@ class DAGMCUniverse(UniverseBase): openmc.Universe Universe instance """ - bounding_cell = openmc.Cell(fill=self, cell_id=bounding_cell_id, region=self.bounding_region(**kwargs)) + bounding_cell = openmc.Cell( + fill=self, cell_id=bounding_cell_id, region=self.bounding_region(**kwargs)) return openmc.Universe(cells=[bounding_cell]) @classmethod @@ -972,4 +1008,4 @@ class DAGMCUniverse(UniverseBase): clone.volume = self.volume clone.auto_geom_ids = self.auto_geom_ids clone.auto_mat_ids = self.auto_mat_ids - return clone \ No newline at end of file + return clone From 966f3e51a886d1c5f5b0a3e062d832680781a127 Mon Sep 17 00:00:00 2001 From: christinacai123 <63215816+christinacai123@users.noreply.github.com> Date: Thu, 13 Apr 2023 17:26:26 -0400 Subject: [PATCH 07/29] Apply suggestions from code review Co-authored-by: Jonathan Shimwell --- openmc/weight_windows.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmc/weight_windows.py b/openmc/weight_windows.py index b825796dc..c08e5b35a 100644 --- a/openmc/weight_windows.py +++ b/openmc/weight_windows.py @@ -20,7 +20,7 @@ class WeightWindows(IDManagerMixin): """Mesh-based weight windows This class enables you to specify weight window parameters that are used in - a simulation. Multiple sets of weight windows can be defined for differen t + a simulation. Multiple sets of weight windows can be defined for different meshes and different particles. An iterable of :class:`WeightWindows` instances can be assigned to the :attr:`openmc.Settings.weight_windows` attribute, which is then exported to XML. From 8bc434a67d0579b8af89cb5560289b2416d411cd Mon Sep 17 00:00:00 2001 From: Gavin Ridley Date: Fri, 14 Apr 2023 12:36:24 -0400 Subject: [PATCH 08/29] move legend outside image Co-authored-by: Jonathan Shimwell --- openmc/universe.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmc/universe.py b/openmc/universe.py index a75af3b0d..a7f9e3d6e 100644 --- a/openmc/universe.py +++ b/openmc/universe.py @@ -428,7 +428,7 @@ class Universe(UniverseBase): key_patch = mpatches.Patch(color=color, label=key.name) patches.append(key_patch) - axes.legend(handles=patches) + axes.legend(handles=patches, bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.0) # Plot image and return the axes return axes.imshow(img, extent=(x_min, x_max, y_min, y_max), **kwargs) From 405717a3cf4b298e0367832faa90078cb75f77a2 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Fri, 14 Apr 2023 16:41:51 -0500 Subject: [PATCH 09/29] Modifying batch estimation message if the uncertainty ratio is inf --- src/tallies/trigger.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/tallies/trigger.cpp b/src/tallies/trigger.cpp index 79af65879..590a54773 100644 --- a/src/tallies/trigger.cpp +++ b/src/tallies/trigger.cpp @@ -211,13 +211,18 @@ void check_triggers() auto n_pred_batches = static_cast(n_active * max_ratio * max_ratio) + settings::n_inactive + 1; - std::string msg = - fmt::format("The estimated number of batches is {}", n_pred_batches); - if (n_pred_batches > settings::n_max_batches) { - msg.append(" --- greater than max batches"); - warning(msg); - } else { + if (max_ratio == INFINITY) { + std::string msg = fmt::format("One or more tallies with triggers have no scores. Unable to estimate the number of remaining batches."); write_message(msg, 7); + } else { + std::string msg = + fmt::format("The estimated number of batches is {}", n_pred_batches); + if (n_pred_batches > settings::n_max_batches) { + msg.append(" --- greater than max batches"); + warning(msg); + } else { + write_message(msg, 7); + } } } } From 6b20df9652827ec5603d5ccecf34c5db23fd5d50 Mon Sep 17 00:00:00 2001 From: christinacai123 <63215816+christinacai123@users.noreply.github.com> Date: Sat, 15 Apr 2023 15:44:02 -0400 Subject: [PATCH 10/29] Apply suggestions from code review Co-authored-by: Paul Romano --- openmc/weight_windows.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/openmc/weight_windows.py b/openmc/weight_windows.py index c08e5b35a..29b92a7ab 100644 --- a/openmc/weight_windows.py +++ b/openmc/weight_windows.py @@ -1,7 +1,7 @@ from __future__ import annotations from collections.abc import Iterable from numbers import Real, Integral -from typing import Iterable, List, Optional, Union +from typing import Iterable, List, Optional, Union, Dict import pathlib from xml.etree import ElementTree as ET @@ -108,8 +108,8 @@ class WeightWindows(IDManagerMixin): def __init__( self, mesh: MeshBase, - lower_ww_bounds: np.ndarray, - upper_ww_bounds: Optional[np.ndarray] = None, + lower_ww_bounds: Iterable[float], + upper_ww_bounds: Optional[Iterable[float]] = None, upper_bound_ratio: Optional[float] = None, energy_bounds: Optional[Iterable[Real]] = None, particle_type: str = 'neutron', @@ -219,7 +219,7 @@ class WeightWindows(IDManagerMixin): return self._energy_bounds @energy_bounds.setter - def energy_bounds(self, bounds: Iterable[Real]): + def energy_bounds(self, bounds: Iterable[float]): cv.check_type('Energy bounds', bounds, Iterable, Real) self._energy_bounds = np.asarray(bounds) @@ -234,7 +234,7 @@ class WeightWindows(IDManagerMixin): return self._lower_ww_bounds @lower_ww_bounds.setter - def lower_ww_bounds(self, bounds: Iterable[Real]): + def lower_ww_bounds(self, bounds: Iterable[float]): cv.check_iterable_type('Lower WW bounds', bounds, Real, @@ -253,7 +253,7 @@ class WeightWindows(IDManagerMixin): return self._upper_ww_bounds @upper_ww_bounds.setter - def upper_ww_bounds(self, bounds: Iterable[Real]): + def upper_ww_bounds(self, bounds: Iterable[float]): cv.check_iterable_type('Upper WW bounds', bounds, Real, @@ -404,7 +404,7 @@ class WeightWindows(IDManagerMixin): ) @classmethod - def from_hdf5(cls, group: h5py.Group, meshes: dict) -> WeightWindows: + def from_hdf5(cls, group: h5py.Group, meshes: Dict[int, MeshBase]) -> WeightWindows: """Create weight windows from HDF5 group Parameters From b4b4ff79484740417c59249d148ac1e6d29be1f0 Mon Sep 17 00:00:00 2001 From: Christina Cai Date: Sat, 15 Apr 2023 15:49:16 -0400 Subject: [PATCH 11/29] added Pathlike to wwinp_to_wws --- openmc/weight_windows.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openmc/weight_windows.py b/openmc/weight_windows.py index 29b92a7ab..fa384f7cc 100644 --- a/openmc/weight_windows.py +++ b/openmc/weight_windows.py @@ -1,6 +1,7 @@ from __future__ import annotations from collections.abc import Iterable from numbers import Real, Integral +import typing from typing import Iterable, List, Optional, Union, Dict import pathlib @@ -11,6 +12,7 @@ import h5py from openmc.filter import _PARTICLES from openmc.mesh import MeshBase, RectilinearMesh, UnstructuredMesh import openmc.checkvalue as cv +from openmc.checkvalue import PathLike from ._xml import get_text from .mixin import IDManagerMixin @@ -449,7 +451,7 @@ class WeightWindows(IDManagerMixin): ) -def wwinp_to_wws(path: Union[str, pathlib.Path]) -> List[WeightWindows]: +def wwinp_to_wws(path: PathLike) -> List[WeightWindows]: """Create WeightWindows instances from a wwinp file .. versionadded:: 0.13.1 From 55b4df553c43cc0d25645621cac6927ba2e287c7 Mon Sep 17 00:00:00 2001 From: christinacai123 <63215816+christinacai123@users.noreply.github.com> Date: Sat, 15 Apr 2023 15:54:41 -0400 Subject: [PATCH 12/29] Apply suggestions from code review Co-authored-by: Paul Romano --- openmc/model/model.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openmc/model/model.py b/openmc/model/model.py index 1da6b3bf2..ba44777dd 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -8,7 +8,7 @@ from numbers import Integral from tempfile import NamedTemporaryFile import warnings from xml.etree import ElementTree as ET -from typing import Optional +from typing import Optional, Dict import h5py @@ -141,7 +141,7 @@ class Model: @property @lru_cache(maxsize=None) - def _cells_by_name(self) -> dict: + def _cells_by_name(self) -> Dict[int, openmc.Cell]: # Get the names maps, but since names are not unique, store a set for # each name key. In this way when the user requests a change by a name, # the change will be applied to all of the same name. @@ -154,7 +154,7 @@ class Model: @property @lru_cache(maxsize=None) - def _materials_by_name(self) -> dict: + def _materials_by_name(self) -> Dict[int, openmc.Material]: if self.materials is None: mats = self.geometry.get_all_materials().values() else: From c9a64e6fe0941e8a4bdd25b03473e1f7715e5a21 Mon Sep 17 00:00:00 2001 From: christinacai123 <63215816+christinacai123@users.noreply.github.com> Date: Sat, 15 Apr 2023 16:01:04 -0400 Subject: [PATCH 13/29] Apply suggestions from code review Co-authored-by: Paul Romano --- openmc/geometry.py | 46 +++++++++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/openmc/geometry.py b/openmc/geometry.py index 88871c544..fae50520d 100644 --- a/openmc/geometry.py +++ b/openmc/geometry.py @@ -1,6 +1,6 @@ from __future__ import annotations import os -import typing +import typing from collections import OrderedDict, defaultdict from collections.abc import Iterable from copy import deepcopy @@ -167,7 +167,7 @@ class Geometry: tree.write(str(p), xml_declaration=True, encoding='utf-8') @classmethod - def from_xml_element(cls, elem, materials=None) -> openmc.Geometry: + def from_xml_element(cls, elem, materials=None) -> Geometry: """Generate geometry from an XML element Parameters @@ -263,7 +263,7 @@ class Geometry: cls, path: PathLike = 'geometry.xml', materials: typing.Optional[typing.Union[PathLike, 'openmc.Materials']] = 'materials.xml' - ) -> openmc.Geometry: + ) -> Geometry: """Generate geometry from XML file Parameters @@ -357,7 +357,7 @@ class Geometry: return indices if return_list else indices[0] - def get_all_cells(self) -> OrderedDict: + def get_all_cells(self) -> typing.OrderedDict[int, openmc.Cell]: """Return all cells in the geometry. Returns @@ -371,7 +371,7 @@ class Geometry: else: return OrderedDict() - def get_all_universes(self) -> OrderedDict: + def get_all_universes(self) -> typing.OrderedDict[int, openmc.Universe]: """Return all universes in the geometry. Returns @@ -386,7 +386,7 @@ class Geometry: universes.update(self.root_universe.get_all_universes()) return universes - def get_all_materials(self) -> OrderedDict: + def get_all_materials(self) -> typing.OrderedDict[int, openmc.Material]: """Return all materials within the geometry. Returns @@ -401,7 +401,7 @@ class Geometry: else: return OrderedDict() - def get_all_material_cells(self) -> OrderedDict: + def get_all_material_cells(self) -> typing.OrderedDict[int, openmc.Cell]: """Return all cells filled by a material Returns @@ -420,7 +420,7 @@ class Geometry: return material_cells - def get_all_material_universes(self) -> OrderedDict: + def get_all_material_universes(self) -> typing.OrderedDict[int, openmc.Universe]: """Return all universes having at least one material-filled cell. This method can be used to find universes that have at least one cell @@ -443,7 +443,7 @@ class Geometry: return material_universes - def get_all_lattices(self) -> OrderedDict: + def get_all_lattices(self) -> typing.OrderedDict[int, openmc.Lattice]: """Return all lattices defined Returns @@ -461,7 +461,7 @@ class Geometry: return lattices - def get_all_surfaces(self) -> OrderedDict: + def get_all_surfaces(self) -> typing.OrderedDict[int, openmc.Surface]: """ Return all surfaces used in the geometry @@ -495,7 +495,9 @@ class Geometry: domains.sort(key=lambda x: x.id) return domains - def get_materials_by_name(self, name, case_sensitive=False, matching=False) -> typing.List[openmc.Material]: + def get_materials_by_name( + self, name, case_sensitive=False, matching=False + ) -> typing.List[openmc.Material]: """Return a list of materials with matching names. Parameters @@ -516,7 +518,9 @@ class Geometry: """ return self._get_domains_by_name(name, case_sensitive, matching, 'material') - def get_cells_by_name(self, name, case_sensitive=False, matching=False) -> typing.List[openmc.Cell]: + def get_cells_by_name( + self, name, case_sensitive=False, matching=False + ) -> typing.List[openmc.Cell]: """Return a list of cells with matching names. Parameters @@ -537,7 +541,9 @@ class Geometry: """ return self._get_domains_by_name(name, case_sensitive, matching, 'cell') - def get_surfaces_by_name(self, name, case_sensitive=False, matching=False) -> typing.List[openmc.Surface]: + def get_surfaces_by_name( + self, name, case_sensitive=False, matching=False + ) -> typing.List[openmc.Surface]: """Return a list of surfaces with matching names. .. versionadded:: 0.13.3 @@ -560,7 +566,9 @@ class Geometry: """ return self._get_domains_by_name(name, case_sensitive, matching, 'surface') - def get_cells_by_fill_name(self, name, case_sensitive=False, matching=False) -> typing.List[openmc.Cell]: + def get_cells_by_fill_name( + self, name, case_sensitive=False, matching=False + ) -> typing.List[openmc.Cell]: """Return a list of cells with fills with matching names. Parameters @@ -605,7 +613,9 @@ class Geometry: return sorted(cells, key=lambda x: x.id) - def get_universes_by_name(self, name, case_sensitive=False, matching=False) -> typing.List[openmc.Universe]: + def get_universes_by_name( + self, name, case_sensitive=False, matching=False + ) -> typing.List[openmc.Universe]: """Return a list of universes with matching names. Parameters @@ -626,7 +636,9 @@ class Geometry: """ return self._get_domains_by_name(name, case_sensitive, matching, 'universe') - def get_lattices_by_name(self, name, case_sensitive=False, matching=False) -> typing.List[openmc.Lattice]: + def get_lattices_by_name( + self, name, case_sensitive=False, matching=False + ) -> typing.List[openmc.Lattice]: """Return a list of lattices with matching names. Parameters @@ -647,7 +659,7 @@ class Geometry: """ return self._get_domains_by_name(name, case_sensitive, matching, 'lattice') - def remove_redundant_surfaces(self) -> dict: + def remove_redundant_surfaces(self) -> typing.Dict[int, openmc.Surface]: """Remove and return all of the redundant surfaces. Uses surface_precision attribute of Geometry instance for rounding and From 5658cd88b184df9340eceb1718932ec0198df951 Mon Sep 17 00:00:00 2001 From: Christina Cai Date: Sat, 15 Apr 2023 16:02:59 -0400 Subject: [PATCH 14/29] Added xml.etree to first block of imports --- openmc/geometry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmc/geometry.py b/openmc/geometry.py index fae50520d..275ca45dd 100644 --- a/openmc/geometry.py +++ b/openmc/geometry.py @@ -6,8 +6,8 @@ from collections.abc import Iterable from copy import deepcopy from pathlib import Path import warnings - from xml.etree import ElementTree as ET + import numpy as np import openmc From 8819b5c1c456371852b08b1b8cd106de023d541a Mon Sep 17 00:00:00 2001 From: christinacai123 <63215816+christinacai123@users.noreply.github.com> Date: Sat, 15 Apr 2023 16:12:38 -0400 Subject: [PATCH 15/29] Apply suggestions from code review Co-authored-by: Paul Romano --- openmc/material.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/openmc/material.py b/openmc/material.py index 66a626668..fa088d7f0 100644 --- a/openmc/material.py +++ b/openmc/material.py @@ -7,7 +7,7 @@ from pathlib import Path import re import typing # imported separately as py3.8 requires typing.Iterable import warnings -from typing import Optional, List, Union +from typing import Optional, List, Union, Dict from xml.etree import ElementTree as ET import h5py @@ -175,7 +175,7 @@ class Material(IDManagerMixin): return self._temperature @property - def density(self) -> float: + def density(self) -> Optional[float]: return self._density @property @@ -227,11 +227,11 @@ class Material(IDManagerMixin): return mass / moles @property - def volume(self) -> float: + def volume(self) -> Optional[float]: return self._volume @property - def ncrystal_cfg(self) -> str: + def ncrystal_cfg(self) -> Optional[str]: return self._ncrystal_cfg @name.setter @@ -292,7 +292,7 @@ class Material(IDManagerMixin): return openmc.data.combine_distributions(dists, probs) if dists else None @classmethod - def from_hdf5(cls, group: h5py.Group) -> openmc.Material: + def from_hdf5(cls, group: h5py.Group) -> Material: """Create material from HDF5 group Parameters @@ -347,7 +347,7 @@ class Material(IDManagerMixin): return material @classmethod - def from_ncrystal(cls, cfg, **kwargs) -> openmc.Material: + def from_ncrystal(cls, cfg, **kwargs) -> Material: """Create material from NCrystal configuration string. Density, temperature, and material composition, and (ultimately) thermal @@ -926,7 +926,7 @@ class Material(IDManagerMixin): return matching_nuclides - def get_nuclide_densities(self) -> dict: + def get_nuclide_densities(self) -> Dict[str, tuple]: """Returns all nuclides in the material and their densities Returns @@ -945,7 +945,7 @@ class Material(IDManagerMixin): return nuclides - def get_nuclide_atom_densities(self, nuclide: Optional[str] = None) -> dict: + def get_nuclide_atom_densities(self, nuclide: Optional[str] = None) -> Dict[str, float]: """Returns one or all nuclides in the material and their atomic densities in units of atom/b-cm @@ -1031,7 +1031,7 @@ class Material(IDManagerMixin): return nuclides def get_activity(self, units: str = 'Bq/cm3', by_nuclide: bool = False, - volume: Optional[float] = None) -> Union[dict, float]: + volume: Optional[float] = None) -> Union[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]. @@ -1078,7 +1078,7 @@ class Material(IDManagerMixin): return activity if by_nuclide else sum(activity.values()) def get_decay_heat(self, units: str = 'W', by_nuclide: bool = False, - volume: Optional[float] = None) -> Union[dict, float]: + volume: Optional[float] = None) -> Union[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]. @@ -1126,7 +1126,7 @@ class Material(IDManagerMixin): return decayheat if by_nuclide else sum(decayheat.values()) - def get_nuclide_atoms(self, volume: Optional[float] = None) -> dict: + def get_nuclide_atoms(self, volume: Optional[float] = None) -> Dict[str, float]: """Return number of atoms of each nuclide in the material .. versionadded:: 0.13.1 @@ -1531,7 +1531,7 @@ class Materials(cv.CheckedList): self += materials @property - def cross_sections(self) -> Optional[Union[str, Path]]: + def cross_sections(self) -> Optional[Path]: return self._cross_sections @cross_sections.setter From 3880d1cec86f12b8289a7f67c1ef3b8364777e43 Mon Sep 17 00:00:00 2001 From: Gavin Ridley Date: Mon, 17 Apr 2023 12:14:52 -0400 Subject: [PATCH 16/29] line length suggestion Co-authored-by: Jonathan Shimwell --- openmc/universe.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openmc/universe.py b/openmc/universe.py index a7f9e3d6e..32768c305 100644 --- a/openmc/universe.py +++ b/openmc/universe.py @@ -404,8 +404,8 @@ class Universe(UniverseBase): # or cell if that was requested if legend: if plot.colors is None: - raise Exception("Must set plot color dictionary if you would" - " like to have an automatic legend.") + raise Exception("Must set plot color dictionary if you " + "would like to have an automatic legend.") if color_by == "cell": expected_key_type = openmc.Cell From e4c332360175b66c8261c9d41643913cb9958e65 Mon Sep 17 00:00:00 2001 From: Gavin Ridley Date: Mon, 17 Apr 2023 12:15:29 -0400 Subject: [PATCH 17/29] remove unnecessary error checking Co-authored-by: Jonathan Shimwell --- openmc/universe.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/openmc/universe.py b/openmc/universe.py index 32768c305..a4efb5f73 100644 --- a/openmc/universe.py +++ b/openmc/universe.py @@ -409,10 +409,8 @@ class Universe(UniverseBase): if color_by == "cell": expected_key_type = openmc.Cell - elif color_by == "material": - expected_key_type = openmc.Material else: - raise Exception("wtf?") + expected_key_type = openmc.Material patches = [] for key, color in plot.colors.items(): From f65de85f9fba7e63e8c4db12157e0fa3f89f2d3d Mon Sep 17 00:00:00 2001 From: Gavin Ridley Date: Mon, 17 Apr 2023 12:17:09 -0400 Subject: [PATCH 18/29] add versionadded for legend kwarg Co-authored-by: Jonathan Shimwell --- openmc/universe.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openmc/universe.py b/openmc/universe.py index a4efb5f73..ed693b84a 100644 --- a/openmc/universe.py +++ b/openmc/universe.py @@ -330,11 +330,12 @@ class Universe(UniverseBase): Axes to draw to .. versionadded:: 0.13.1 - legend : bool - whether a legend showing material or cell names should be drawn **kwargs Keyword arguments passed to :func:`matplotlib.pyplot.imshow` + .. versionadded:: 0.13.4 + legend : bool + Whether a legend showing material or cell names should be drawn Returns ------- matplotlib.image.AxesImage From ad33834e0f659f90efe3ae1154b30a254b61ecf4 Mon Sep 17 00:00:00 2001 From: Gavin Ridley Date: Mon, 17 Apr 2023 12:18:03 -0400 Subject: [PATCH 19/29] default to legend label using ID if no name given Co-authored-by: Jonathan Shimwell --- openmc/universe.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openmc/universe.py b/openmc/universe.py index ed693b84a..ec9507531 100644 --- a/openmc/universe.py +++ b/openmc/universe.py @@ -424,7 +424,8 @@ class Universe(UniverseBase): "Color dict key type does not match color_by") # this works whether we're doing cells or materials - key_patch = mpatches.Patch(color=color, label=key.name) + label = key.name if key.name != '' else key.id + key_patch = mpatches.Patch(color=color, label=label) patches.append(key_patch) axes.legend(handles=patches, bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.0) From f08510119c5a6cc6d9333e5153983dfdffe662ea Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Tue, 18 Apr 2023 00:38:33 -0500 Subject: [PATCH 20/29] Applying code formatter --- src/tallies/trigger.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/tallies/trigger.cpp b/src/tallies/trigger.cpp index 590a54773..87f298baa 100644 --- a/src/tallies/trigger.cpp +++ b/src/tallies/trigger.cpp @@ -39,7 +39,8 @@ std::pair get_tally_uncertainty( auto mean = sum / n; // if the result has no contributions, return an invalid pair - if (mean == 0) return {-1 , -1}; + if (mean == 0) + return {-1, -1}; double std_dev = std::sqrt((sum_sq / n - mean * mean) / (n - 1)); double rel_err = (mean != 0.) ? std_dev / std::abs(mean) : 0.; @@ -193,8 +194,10 @@ void check_triggers() keff_ratio); } else { if (tally_ratio == INFINITY) { - msg = fmt::format("Triggers unsatisfied, no result tallied for score {} in tally {}", reaction_name(score), tally_id); - } else{ + msg = fmt::format( + "Triggers unsatisfied, no result tallied for score {} in tally {}", + reaction_name(score), tally_id); + } else { msg = fmt::format( "Triggers unsatisfied, max unc./thresh. is {} for {} in tally {}", tally_ratio, reaction_name(score), tally_id); @@ -212,7 +215,9 @@ void check_triggers() settings::n_inactive + 1; if (max_ratio == INFINITY) { - std::string msg = fmt::format("One or more tallies with triggers have no scores. Unable to estimate the number of remaining batches."); + std::string msg = + fmt::format("One or more tallies with triggers have no scores. Unable " + "to estimate the number of remaining batches."); write_message(msg, 7); } else { std::string msg = From 8606e07a6770fd18c86492e86a48c0319abb21f1 Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Tue, 18 Apr 2023 16:18:03 +0100 Subject: [PATCH 21/29] added plot axis labels --- openmc/universe.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/openmc/universe.py b/openmc/universe.py index 2ed96f4bc..c98b8ea4e 100644 --- a/openmc/universe.py +++ b/openmc/universe.py @@ -344,10 +344,13 @@ class Universe(UniverseBase): # Determine extents of plot if basis == 'xy': x, y = 0, 1 + xlabel, ylabel = 'x [cm]', 'y [cm]' elif basis == 'yz': x, y = 1, 2 + xlabel, ylabel = 'y [cm]', 'z [cm]' elif basis == 'xz': x, y = 0, 2 + xlabel, ylabel = 'x [cm]', 'z [cm]' x_min = origin[x] - 0.5*width[0] x_max = origin[x] + 0.5*width[0] y_min = origin[y] - 0.5*width[1] @@ -391,6 +394,8 @@ class Universe(UniverseBase): if axes is None: 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[0]*px/(params.top - params.bottom) From a02d583ac18925927e02b831efba0b2fcf96ffae Mon Sep 17 00:00:00 2001 From: Gavin Ridley Date: Tue, 18 Apr 2023 11:24:20 -0400 Subject: [PATCH 22/29] review comments addressed --- openmc/universe.py | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/openmc/universe.py b/openmc/universe.py index ec9507531..b85d36895 100644 --- a/openmc/universe.py +++ b/openmc/universe.py @@ -6,6 +6,7 @@ from numbers import Integral, Real from pathlib import Path from tempfile import TemporaryDirectory from xml.etree import ElementTree as ET +from warnings import warn import h5py import numpy as np @@ -294,9 +295,15 @@ class Universe(UniverseBase): return [self, cell] + cell.fill.find(p) return [] + # default kwargs that are passed to plt.legend in the plot method below. + # If you change this, be sure to change it in the docstring for plot below. + _default_legend_kwargs = {'bbox_to_anchor': ( + 1.05, 1), 'loc': 2, 'borderaxespad': 0.0} + def plot(self, origin=(0., 0., 0.), width=(1., 1.), pixels=(200, 200), basis='xy', color_by='cell', colors=None, seed=None, - openmc_exec='openmc', axes=None, legend=False, **kwargs): + openmc_exec='openmc', axes=None, legend=False, + legend_kwargs=_default_legend_kwargs, **kwargs): """Display a slice plot of the universe. Parameters @@ -330,12 +337,21 @@ class Universe(UniverseBase): Axes to draw to .. versionadded:: 0.13.1 + legend : bool + Whether a legend showing material or cell names should be drawn + + .. versionadded:: 0.13.4 + legend_kwargs : dict + Keyword arguments passed to :func:`matplotlib.pyplot.legend`. + The default is: + + {'bbox_to_anchor': (1.05, 1), 'loc':2, 'borderaxespad': 0.0} + + .. versionadded:: 0.13.4 **kwargs Keyword arguments passed to :func:`matplotlib.pyplot.imshow` .. versionadded:: 0.13.4 - legend : bool - Whether a legend showing material or cell names should be drawn Returns ------- matplotlib.image.AxesImage @@ -405,8 +421,8 @@ class Universe(UniverseBase): # or cell if that was requested if legend: if plot.colors is None: - raise Exception("Must set plot color dictionary if you " - "would like to have an automatic legend.") + raise ValueError("Must pass 'colors' dictionary if you " + "are adding a legend via legend=True.") if color_by == "cell": expected_key_type = openmc.Cell @@ -417,10 +433,10 @@ class Universe(UniverseBase): for key, color in plot.colors.items(): if isinstance(key, int): - raise Exception( + raise TypeError( "Cannot use IDs in colors dict for auto legend.") elif not isinstance(key, expected_key_type): - raise Exception( + raise TypeError( "Color dict key type does not match color_by") # this works whether we're doing cells or materials @@ -428,7 +444,7 @@ class Universe(UniverseBase): key_patch = mpatches.Patch(color=color, label=label) patches.append(key_patch) - axes.legend(handles=patches, bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.0) + axes.legend(handles=patches, **legend_kwargs) # Plot image and return the axes return axes.imshow(img, extent=(x_min, x_max, y_min, y_max), **kwargs) From f8abaf687af6670387af539f9f22e6aebdec085c Mon Sep 17 00:00:00 2001 From: christinacai123 <63215816+christinacai123@users.noreply.github.com> Date: Tue, 18 Apr 2023 13:45:51 -0400 Subject: [PATCH 23/29] Apply suggestions from code review Co-authored-by: Paul Romano --- openmc/weight_windows.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmc/weight_windows.py b/openmc/weight_windows.py index fa384f7cc..2993db089 100644 --- a/openmc/weight_windows.py +++ b/openmc/weight_windows.py @@ -1,9 +1,9 @@ from __future__ import annotations from collections.abc import Iterable from numbers import Real, Integral +import pathlib import typing from typing import Iterable, List, Optional, Union, Dict -import pathlib from xml.etree import ElementTree as ET import numpy as np From 0280631fe7ee1e6b6ad5f85c8f0f44b51d3ff331 Mon Sep 17 00:00:00 2001 From: christinacai123 <63215816+christinacai123@users.noreply.github.com> Date: Tue, 18 Apr 2023 13:46:33 -0400 Subject: [PATCH 24/29] Apply suggestions from code review Co-authored-by: Paul Romano --- openmc/model/model.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openmc/model/model.py b/openmc/model/model.py index ba44777dd..933e1172e 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -606,7 +606,6 @@ class Model: this method creates the XML files and runs OpenMC via a system call. In both cases this method returns the path to the last statepoint file generated. - .. versionchanged:: 0.12 Instead of returning the final k-effective value, this function now returns the path to the final statepoint written. From 293f02d9912e9d67d91e5ef17d2f968a32350a5c Mon Sep 17 00:00:00 2001 From: christinacai123 <63215816+christinacai123@users.noreply.github.com> Date: Tue, 18 Apr 2023 13:46:57 -0400 Subject: [PATCH 25/29] Apply suggestions from code review Co-authored-by: Paul Romano --- openmc/geometry.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openmc/geometry.py b/openmc/geometry.py index 275ca45dd..01c0b2714 100644 --- a/openmc/geometry.py +++ b/openmc/geometry.py @@ -3,7 +3,6 @@ import os import typing from collections import OrderedDict, defaultdict from collections.abc import Iterable -from copy import deepcopy from pathlib import Path import warnings from xml.etree import ElementTree as ET From dd9f03abd22ec6eb0d946290836445dca31cbdc0 Mon Sep 17 00:00:00 2001 From: Christina Cai Date: Tue, 18 Apr 2023 13:49:36 -0400 Subject: [PATCH 26/29] trivial change for CI --- openmc/material.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmc/material.py b/openmc/material.py index 66a626668..dc2ab0f8b 100644 --- a/openmc/material.py +++ b/openmc/material.py @@ -10,8 +10,8 @@ import warnings from typing import Optional, List, Union from xml.etree import ElementTree as ET -import h5py import numpy as np +import h5py import openmc import openmc.data From 2f87ce7f2789b5a5a75ae70099cfcb2d12bae30c Mon Sep 17 00:00:00 2001 From: Gavin Ridley Date: Wed, 19 Apr 2023 12:22:29 -0400 Subject: [PATCH 27/29] Update openmc/universe.py Co-authored-by: Paul Romano --- openmc/universe.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openmc/universe.py b/openmc/universe.py index b85d36895..3f4982547 100644 --- a/openmc/universe.py +++ b/openmc/universe.py @@ -296,7 +296,6 @@ class Universe(UniverseBase): return [] # default kwargs that are passed to plt.legend in the plot method below. - # If you change this, be sure to change it in the docstring for plot below. _default_legend_kwargs = {'bbox_to_anchor': ( 1.05, 1), 'loc': 2, 'borderaxespad': 0.0} From 22f4fc0902b0ed708a245c6b82a341dbbfee99b5 Mon Sep 17 00:00:00 2001 From: Gavin Ridley Date: Wed, 19 Apr 2023 12:22:39 -0400 Subject: [PATCH 28/29] Update openmc/universe.py Co-authored-by: Paul Romano --- openmc/universe.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/openmc/universe.py b/openmc/universe.py index 3f4982547..d5ae07717 100644 --- a/openmc/universe.py +++ b/openmc/universe.py @@ -342,9 +342,6 @@ class Universe(UniverseBase): .. versionadded:: 0.13.4 legend_kwargs : dict Keyword arguments passed to :func:`matplotlib.pyplot.legend`. - The default is: - - {'bbox_to_anchor': (1.05, 1), 'loc':2, 'borderaxespad': 0.0} .. versionadded:: 0.13.4 **kwargs From bdd34fc64dbe0cd9d034d1531ef5d17237bd342d Mon Sep 17 00:00:00 2001 From: Christina Cai Date: Wed, 19 Apr 2023 14:21:12 -0400 Subject: [PATCH 29/29] added deepcopy back in geometry.py --- openmc/geometry.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openmc/geometry.py b/openmc/geometry.py index 01c0b2714..ee4a58a3b 100644 --- a/openmc/geometry.py +++ b/openmc/geometry.py @@ -2,6 +2,7 @@ from __future__ import annotations import os import typing from collections import OrderedDict, defaultdict +from copy import deepcopy from collections.abc import Iterable from pathlib import Path import warnings