diff --git a/doc/source/changelog.rst b/doc/source/changelog.rst index ade76187..aec92356 100644 --- a/doc/source/changelog.rst +++ b/doc/source/changelog.rst @@ -8,6 +8,9 @@ MontePy Changelog #Next Version# -------------- +**Features Added** + +* Allow clearing default libraries by assigning ``None`` at material and problem scopes; unset values are omitted from serialization. **Features Added** diff --git a/montepy/data_inputs/material.py b/montepy/data_inputs/material.py index 828e376f..df980a29 100644 --- a/montepy/data_inputs/material.py +++ b/montepy/data_inputs/material.py @@ -59,12 +59,37 @@ def __getitem__(self, key): except KeyError: return None + def update(self, other=None, **kwargs): + """Update the default libraries with the key/value pairs from other, overwriting existing keys. + + Parameters + ---------- + other : dict or dict-like + Another dict or dict-like object to update from + **kwargs + Additional key-value pairs to update + """ + if other is not None: + if hasattr(other, "items"): + for key, value in other.items(): + self[key] = value + else: + for key, value in other: + self[key] = value + for key, value in kwargs.items(): + self[key] = value + def __setitem__(self, key, value): key = self._validate_key(key) + if value is None: + # Setting to None means unset/delete the library + try: + del self[key] + except KeyError: + pass # Already unset, nothing to do + return if not isinstance(value, (Library, str)): - raise TypeError( - f"Value must be a library or str. {value} of type: {type(value).__name__} given." - ) + raise TypeError("Default library value must be a Library, str, or None") if isinstance(value, str): value = Library(value) try: @@ -252,16 +277,22 @@ class Material(data_input.DataInputAbstract, Numbered_MCNP_Object): :func:`~montepy.data_inputs.material.Material.default_libraries` acts like a dictionary, and can accept a string or a :class:`~montepy.particle.LibraryType` as keys. + To clear a default library, assign ``None``: + .. testcode:: print(mat.default_libraries["plib"]) mat.default_libraries[montepy.LibraryType.NEUTRON] = "00c" print(mat.default_libraries["nlib"]) + # Clear/unset the plib default + mat.default_libraries["plib"] = None + print(mat.default_libraries["plib"]) .. testoutput:: 80p 00c + None See Also -------- @@ -437,16 +468,22 @@ def default_libraries(self): :func:`~montepy.data_inputs.material.Material.default_libraries` acts like a dictionary, and can accept a string or a :class:`~montepy.particle.LibraryType` as keys. + To clear a default library, assign ``None``: + .. testcode:: print(mat.default_libraries["plib"]) mat.default_libraries[montepy.LibraryType.NEUTRON] = "00c" print(mat.default_libraries["nlib"]) + # Clear/unset the plib default + mat.default_libraries["plib"] = None + print(mat.default_libraries["plib"]) .. testoutput:: None 00c + None .. versionadded:: 1.0.0 diff --git a/montepy/materials.py b/montepy/materials.py index 99479326..603667ee 100644 --- a/montepy/materials.py +++ b/montepy/materials.py @@ -218,7 +218,7 @@ def default_libraries(self) -> dict[montepy.LibraryType, montepy.Library]: ^^^^^^^^ To set the default libraries for a problem you need to set this dictionary - to a Library or string. + to a Library or string. To clear a default library, assign ``None``. .. testcode:: python @@ -229,6 +229,8 @@ def default_libraries(self) -> dict[montepy.LibraryType, montepy.Library]: problem.materials.default_libraries["nlib"] = "00c" # set photo-atomic problem.materials.default_libraries[montepy.LibraryType.PHOTO_ATOMIC] = montepy.Library("80p") + # clear/unset neutron default + problem.materials.default_libraries["nlib"] = None .. versionadded:: 1.0.0 diff --git a/pyproject.toml b/pyproject.toml index 4879f7f1..abfdda62 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,6 +14,7 @@ authors = [ {name = "Brenna Carbno", email="brenna.carbno@inl.gov"}, {name = "Benjaminas Marcinkevicius", email="BenjaminasDev@outlook.com"}, {name = "Paul Ferney", email="Paul.Ferney@inl.gov"}, + {name = "Vikranth Udandarao", email="vikranth22570@iiitd.ac.in"}, {name = "Digvijay Yeware", email="yewaredigvijay@gmail.com"} ] keywords = ["MCNP", "neutronics", "imcnp", "input file", "monte carlo", "radiation transport"] diff --git a/tests/test_material.py b/tests/test_material.py index 0283ea58..bec1ec59 100644 --- a/tests/test_material.py +++ b/tests/test_material.py @@ -877,3 +877,30 @@ def test_bad_set_get(_, dl): def test_dl_str(_, dl): str(dl) + + def test_default_libraries_none_setting(_, dl): + """Test that setting default libraries to None unsets them.""" + # Set a library first + dl["plib"] = "80p" + assert dl["plib"] == Library("80p") + + # Set to None to unset + dl["plib"] = None + assert dl["plib"] is None + + # Setting None on unset library should not error + dl["nlib"] = None + assert dl["nlib"] is None + + # Test with enum keys + dl[LibraryType.PHOTO_ATOMIC] = "90p" + assert dl["plib"] == Library("90p") + dl[LibraryType.PHOTO_ATOMIC] = None + assert dl["plib"] is None + + # Test update method with None + dl["nlib"] = "00c" + dl["plib"] = "80p" + dl.update({"plib": None}) + assert dl["plib"] is None + assert dl["nlib"] == Library("00c")