From 5a5f36507a72e7af92234802c6dd150a59ae8375 Mon Sep 17 00:00:00 2001 From: shimwell Date: Thu, 25 Jun 2026 15:46:02 +0200 Subject: [PATCH 1/2] Raise on duplicate material names in convert_to_multigroup convert_to_multigroup generates one MGXS per material, keyed by material name. Two distinct materials that share a name (for example the same-named clones from Model.differentiate_mats(), since Material.clone() preserves the name) cannot be written as separate cross sections -- one would silently overwrite the other, dropping a material's data. Names are also sanitised to alphanumeric plus underscore, which can make otherwise distinct names collide. Raise a ValueError instead of silently producing an incorrect library, leaving user-assigned names unchanged. --- openmc/model/model.py | 21 ++++++++++++++++++++- tests/unit_tests/test_model.py | 22 ++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/openmc/model/model.py b/openmc/model/model.py index d927b65ae64..f7aed5802c9 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -2704,7 +2704,12 @@ def convert_to_multigroup( Parameters ---------- method : {"material_wise", "stochastic_slab", "infinite_medium"}, optional - Method to generate the MGXS. + Method to generate the MGXS. One cross section is produced per + material (keyed by material name), so a material that fills several + cells with differing spectra is represented by a single, + spectrum-averaged cross section. To resolve location-specific + spectra, give each region its own material with a distinct name; + conversion raises an error if distinct materials share a name. groups : openmc.mgxs.EnergyGroups, str, or sequence of float, optional Energy group structure for the MGXS. Can be an :class:`openmc.mgxs.EnergyGroups` object, a string name of a @@ -2779,6 +2784,20 @@ def convert_to_multigroup( material.name = f"material {material.id}" material.name = re.sub(r'[^a-zA-Z0-9]', '_', material.name) + # The MGXS library keys cross section data by material name, so + # distinct materials that share a name cannot be written separately. + name_to_ids = {} + for material in self.materials: + name_to_ids.setdefault(material.name, set()).add(material.id) + shared = sorted( + name for name, ids in name_to_ids.items() if len(ids) > 1 + ) + if shared: + raise ValueError( + "Each material must have a unique name to be written to the " + f"MGXS library, but these names are shared: {shared}." + ) + # If needed, generate the needed MGXS data library file if not Path(mgxs_path).is_file() or overwrite_mgxs_library: if method == "infinite_medium": diff --git a/tests/unit_tests/test_model.py b/tests/unit_tests/test_model.py index 9234b2d2721..c6b797d058e 100644 --- a/tests/unit_tests/test_model.py +++ b/tests/unit_tests/test_model.py @@ -1038,3 +1038,25 @@ def test_id_map_to_rgb(): ) # Check that overlap region is green assert np.allclose(rgb_overlap[5:, 5:], [0.0, 1.0, 0.0]) + + +def test_convert_to_multigroup_raises_on_duplicate_material_names(run_in_tmpdir): + """Distinct materials that share a name must raise rather than silently + collapse to a single cross section in the MGXS library.""" + steel_a = openmc.Material(name="steel") + steel_a.add_element("Fe", 1.0) + steel_a.set_density("g/cm3", 7.9) + steel_b = openmc.Material(name="steel") + steel_b.add_element("Fe", 1.0) + steel_b.set_density("g/cm3", 7.9) + + s1 = openmc.Sphere(r=1.0) + s2 = openmc.Sphere(r=2.0, boundary_type="vacuum") + c1 = openmc.Cell(fill=steel_a, region=-s1) + c2 = openmc.Cell(fill=steel_b, region=+s1 & -s2) + model = openmc.Model( + openmc.Geometry([c1, c2]), openmc.Materials([steel_a, steel_b]) + ) + + with pytest.raises(ValueError, match="unique name"): + model.convert_to_multigroup(method="material_wise") From f378a65beb133129d8a75503c6253c38f8da5012 Mon Sep 17 00:00:00 2001 From: Jon Shimwell Date: Thu, 25 Jun 2026 16:35:42 +0200 Subject: [PATCH 2/2] Apply suggestion from @jon-proximafusion --- openmc/model/model.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/openmc/model/model.py b/openmc/model/model.py index f7aed5802c9..ec2e81fbf60 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -2705,11 +2705,7 @@ def convert_to_multigroup( ---------- method : {"material_wise", "stochastic_slab", "infinite_medium"}, optional Method to generate the MGXS. One cross section is produced per - material (keyed by material name), so a material that fills several - cells with differing spectra is represented by a single, - spectrum-averaged cross section. To resolve location-specific - spectra, give each region its own material with a distinct name; - conversion raises an error if distinct materials share a name. + material (keyed by material name). groups : openmc.mgxs.EnergyGroups, str, or sequence of float, optional Energy group structure for the MGXS. Can be an :class:`openmc.mgxs.EnergyGroups` object, a string name of a