From d0c9b43b995fc68e68744fc56f9acada6c31da7c Mon Sep 17 00:00:00 2001 From: GuySten Date: Thu, 22 Jan 2026 17:38:50 +0200 Subject: [PATCH 1/2] fix for plotting model with multigroup cross sections --- openmc/material.py | 37 +++++++++++++++++++++++++ openmc/model/model.py | 11 ++++++++ openmc/universe.py | 1 + tests/unit_tests/test_universe.py | 46 +++++++++++++++++++++++++++++++ 4 files changed, 95 insertions(+) diff --git a/openmc/material.py b/openmc/material.py index 735a0574326..9d52fe73704 100644 --- a/openmc/material.py +++ b/openmc/material.py @@ -163,6 +163,8 @@ def __init__( # The single instance of Macroscopic data present in this material # (only one is allowed, hence this is different than _nuclides, etc) self._macroscopic = None + + self._cross_sections = None # If specified, a list of table names self._sab = [] @@ -208,6 +210,15 @@ def __repr__(self) -> str: return string + @property + def cross_sections(self) -> Path | None: + return self._cross_sections + + @cross_sections.setter + def cross_sections(self, cross_sections): + if cross_sections is not None: + self._cross_sections = input_path(cross_sections) + @property def name(self) -> str | None: return self._name @@ -1918,6 +1929,30 @@ def cross_sections(self) -> Path | None: def cross_sections(self, cross_sections): if cross_sections is not None: self._cross_sections = input_path(cross_sections) + for mat in self: + mat.cross_sections = self.cross_sections + + def _setup_cross_sections(self, material): + """Copy cross_sections to material if exists + and copy cross_sections from material if not exists + + Parameters + ---------- + material : openmc.Material + + + """ + if self.cross_sections is not None: + if material.cross_sections is not None: + if material.cross_sections != self.cross_sections: + warn("Material {material.id} cross_sections value has been overriden.") + material.cross_sections = self.cross_sections + elif material.cross_sections is not None: + self.cross_sections = material.cross_sections + + def __setitem__(index: int, material): + self._setup_cross_sections(material) + super().__setitem__(index, material) def append(self, material): """Append material to collection @@ -1928,6 +1963,7 @@ def append(self, material): Material to append """ + self._setup_cross_sections(material) super().append(material) def insert(self, index: int, material): @@ -1941,6 +1977,7 @@ def insert(self, index: int, material): Material to insert """ + self._setup_cross_sections(material) super().insert(index, material) def make_isotropic_in_lab(self): diff --git a/openmc/model/model.py b/openmc/model/model.py index 6e4c1c5856d..3c362eb0213 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -1159,6 +1159,17 @@ def plot( 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] + + # Determine whether any materials contains macroscopic data and if so, + # set energy mode accordingly + for mat in self.geometry.get_all_materials().values(): + if mat._macroscopic is not None: + self.settings.energy_mode = 'multi-group' + break + + # Convert cross_section path to absolute + if self.materials.cross_sections is not None: + self.materials.cross_sections = Path(self.materials.cross_sections).resolve() # Get ID map from the C API id_map = self.id_map( diff --git a/openmc/universe.py b/openmc/universe.py index 0e64693ba8e..3ed6a27bca3 100644 --- a/openmc/universe.py +++ b/openmc/universe.py @@ -337,6 +337,7 @@ def plot(self, *args, **kwargs): """ model = openmc.Model() model.geometry = openmc.Geometry(self) + model.materials = openmc.Materials(self.get_all_materials().values()) return model.plot(*args, **kwargs) def get_nuclides(self): diff --git a/tests/unit_tests/test_universe.py b/tests/unit_tests/test_universe.py index efe8552a648..0fe10e3649e 100644 --- a/tests/unit_tests/test_universe.py +++ b/tests/unit_tests/test_universe.py @@ -6,6 +6,46 @@ from tests.unit_tests import assert_unbounded +@pytest.fixture +def mg_lib(): + groups = openmc.mgxs.EnergyGroups(group_edges=[1e-5, 20.0e6]) + h2o_xsdata = openmc.XSdata('LWTR', groups) + h2o_xsdata.order = 0 + h2o_xsdata.set_total([1.0]) + h2o_xsdata.set_absorption([0.5]) + scatter_matrix = np.array([[[0.5]]]) + scatter_matrix = np.rollaxis(scatter_matrix, 0, 3) + h2o_xsdata.set_scatter_matrix(scatter_matrix) + mg_cross_sections_file = openmc.MGXSLibrary(groups) + mg_cross_sections_file.add_xsdatas([h2o_xsdata]) + mg_cross_sections_file.export_to_hdf5() + return "mgxs.h5" + + +@pytest.fixture +def mg_model(mg_lib): + model = openmc.Model() + + # Create materials for the problem + h2o_data = openmc.Macroscopic('LWTR') + + water = openmc.Material(name='Water') + water.set_density('macro', 1.0) + water.add_macroscopic(h2o_data) + + # Instantiate a Materials collection and export to XML + model.materials = openmc.Materials([water]) + model.materials.cross_sections = mg_lib + H = 1.0 + L = 100 + + sph00 = openmc.Sphere(r=10, boundary_type = "vacuum") + cell00 = openmc.Cell(region = -sph00 , fill = water) + univ = openmc.Universe(cells = [cell00], universe_id = 1) + model.geometry = openmc.Geometry(univ) + return model + + def test_basic(): c1 = openmc.Cell() c2 = openmc.Cell() @@ -102,6 +142,12 @@ def test_plot(run_in_tmpdir, sphere_model): # Close plots to avoid warning import matplotlib.pyplot as plt plt.close('all') + + +def test_mg_plot(mg_model): + univ = mg_model.geometry.root_universe + univ.plot(width=(200, 200), basis='yz', color_by='cell') + univ.plot(width=(200, 200), basis='yz', color_by='material') def test_get_nuclides(uo2): From 6ffa03808dccb6721ae7d2d4aa0d138b74b4f6cb Mon Sep 17 00:00:00 2001 From: GuySten <62616591+GuySten@users.noreply.github.com> Date: Thu, 22 Jan 2026 19:00:19 +0200 Subject: [PATCH 2/2] Fix __setitem__ method definition in material.py --- openmc/material.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmc/material.py b/openmc/material.py index 9d52fe73704..b6c5eb4b88a 100644 --- a/openmc/material.py +++ b/openmc/material.py @@ -1950,7 +1950,7 @@ def _setup_cross_sections(self, material): elif material.cross_sections is not None: self.cross_sections = material.cross_sections - def __setitem__(index: int, material): + def __setitem__(self, index: int, material): self._setup_cross_sections(material) super().__setitem__(index, material)