diff --git a/openmc/material.py b/openmc/material.py index c7b954b6666..5922c044717 100644 --- a/openmc/material.py +++ b/openmc/material.py @@ -524,7 +524,10 @@ def set_density(self, units: str, density: float | None = None): self._density = density def add_nuclide(self, nuclide: str, percent: float, percent_type: str = 'ao'): - """Add a nuclide to the material + """Add a nuclide to the material. + + If nuclide with the same name and percent_type already exists in the + material, its percentage will be updated. Parameters ---------- @@ -559,7 +562,24 @@ def add_nuclide(self, nuclide: str, percent: float, percent_type: str = 'ao'): if Z >= 89: self.depletable = True - self._nuclides.append(NuclideTuple(nuclide, percent, percent_type)) + # Flag to mark if we squashed the nuclide with an existing one + squashed = False + + for i, nt in enumerate(self._nuclides): + if nt.name == nuclide and nt.percent_type == percent_type: + # add the percentage the two matching nuclides + self._nuclides[i] = NuclideTuple(nuclide, nt.percent + percent, percent_type) + squashed = True + break + elif nt.name == nuclide and nt.percent_type != percent_type: + warnings.warn( + f"Nuclide '{nuclide}' already present with percent_type " + f"'{nt.percent_type}'. Keeping separate entry for " + f"new percent_type '{percent_type}'." + ) + + if not squashed: + 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 diff --git a/tests/unit_tests/test_material.py b/tests/unit_tests/test_material.py index eae814a755b..3de0f88d49f 100644 --- a/tests/unit_tests/test_material.py +++ b/tests/unit_tests/test_material.py @@ -93,15 +93,19 @@ def test_nuclides_to_ignore(run_in_tmpdir): 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)]: - m.add_nuclide(nuc, percent) + for nuc, percent, percent_type in [ + ('H1', 1.0, 'ao'), + ('H2', 1.0, 'ao'), + ('H1', 2.0, 'wo'), + ('H2', 2.0, 'wo') + ]: + m.add_nuclide(nuc, percent=percent, percent_type=percent_type) m.remove_nuclide('H1') assert len(m.nuclides) == 2 assert all(nuc.name == 'H2' for nuc in m.nuclides) assert m.nuclides[0].percent == 1.0 assert m.nuclides[1].percent == 2.0 - def test_remove_elements(): """Test removing elements.""" m = openmc.Material() @@ -766,3 +770,34 @@ def test_mean_free_path(): mat2.add_nuclide('Pb208', 1.0) mat2.set_density('g/cm3', 11.34) assert mat2.mean_free_path(energy=14e6) == pytest.approx(5.65, abs=1e-2) + + +def test_add_nuclide_squash_same_type(): + mat1 = openmc.Material() + mat1.add_nuclide('Li6', 0.02, 'ao') + mat1.add_nuclide('Li6', 0.03, 'ao') + # Should be a single entry with 0.05 ao + entries = [nt for nt in mat1.nuclides if nt.name == 'Li6' and nt.percent_type == 'ao'] + assert len(entries) == 1 + assert entries[0].percent == pytest.approx(0.05) + + mat2 = openmc.Material() + mat2.add_nuclide('Li6', 0.04, 'wo') + mat2.add_nuclide('Li6', 0.05, 'wo') + # Should be a single entry with 0.09 wo + entries = [nt for nt in mat2.nuclides if nt.name == 'Li6' and nt.percent_type == 'wo'] + assert len(entries) == 1 + assert entries[0].percent == pytest.approx(0.09) + + +def test_add_nuclide_keep_different_type_warns(): + mat = openmc.Material() + mat.add_nuclide('Li6', 0.02, 'ao') + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + mat.add_nuclide('Li6', 0.01, 'wo') + assert any("Keeping separate entry" in str(rec.message) for rec in w) + # Should have two entries: one ao and one wo + aos = [nt for nt in mat.nuclides if nt.name == 'Li6' and nt.percent_type == 'ao'] + wos = [nt for nt in mat.nuclides if nt.name == 'Li6' and nt.percent_type == 'wo'] + assert len(aos) == 1 and len(wos) == 1 \ No newline at end of file