Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Marked material_components for deprecation. #528

Merged
merged 16 commits into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion doc/source/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ MontePy Changelog

**Performance Improvement**

* Fixed method of linking ``Material`` to ``ThermalScattering`` objects, avoiding a very expensive O(N:sup:`2`) (:issue:`510`).
* Fixed method of linking ``Material`` to ``ThermalScattering`` objects, avoiding a very expensive O(N :sup:`2`) (:issue:`510`).

**Deprecations**

* Marked ``Material.material_components`` as deprecated, and created migration plan describing what to expect moving forward (:issue:`506`).

0.4.0
--------------
Expand Down
54 changes: 54 additions & 0 deletions doc/source/migrations/migrate0_1.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
.. _migrate 0 1:

Migration plan for MontePy 1.0.0
================================

.. meta::
:description: Migration plan for moving from MontePy 0.x to MontePy 1.0.0

Necessity
---------

The MCNP 6.2 and 6.3 manuals are ambiguously worded around whether nuclides can be repeated in a material definition.
Due to this the authors of MontePy in MontePy 0.x.x assumed that repeated nuclides were not allowed.
Due to this assumption material composition data were stored in python dictionary,
where the keys were the nuclide and their library.
Due to this if duplicate nuclides are present only the last instance of that nuclide will have its information preserved in the model.
This is clearly not a desired outcome.

However, it has been confirmed that duplicate nuclides are allowed in MCNP,
and can be advantageous.
See :issue:`504` for more details.
Due to this it was decided that the best way forward was to abandon the old design,
and to create a brand new data structure.
This means that backwards compatibility *will* be broken,
and so this fix is leading to a major version release.


Deprecations
------------
The following properties and objects are currently deprecated,
and will be removed in MontePy 1.0.0.

* :func:`montepy.data_inputs.material.Material.material_components`.
This is the dictionary that caused this design problem.

* :class:`montepy.data_inputs.material_components.MaterialComponents`
This is the class that stores information in the above dictionary.
It is largely excess object wrapping, that makes the material interface
overly complex.

* :class:`montepy.data_inputs.Isotope` will be renamed to ``Nuclide``.
This is to better align with MCNP documentation,
and better reflect that the nuclear data for a nuclide can represent
isotopic, isomeric, or atomic data.


New Interface
-------------
Currently the replacement interface has not been fully designed yet.
If you have input you can `join the discussion <https://github.com/idaholab/MontePy/discussions/475>`_.
There will also be some alpha-testing announced in that discussion.

Once MontePy 1.0.0 is released this will be updated with information about the new interface,
and how to migrate to it.
1 change: 1 addition & 0 deletions doc/source/users.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ User Guide
utilities
tricks
faq
migrations/migrate0_1
changelog
publications
17 changes: 16 additions & 1 deletion montepy/data_inputs/isotope.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,21 @@
from montepy.errors import *
from montepy.input_parser.syntax_node import PaddingNode, ValueNode

import warnings


class Isotope:
"""
A class to represent an MCNP isotope

.. deprecated:: 0.4.1
This will class is deprecated, and will be renamed: ``Nuclde``.
For more details see the :ref:`migrate 0 1`.

:param ZAID: the MCNP isotope identifier
:type ZAID: str
:param suppress_warning: Whether to suppress the ``FutureWarning``.
:type suppress_warning: bool
"""

# Cl-52 Br-101 Xe-150 Os-203 Cm-251 Og-296
Expand All @@ -22,7 +30,14 @@ class Isotope:
Points on bounding curve for determining if "valid" isotope
"""

def __init__(self, ZAID="", node=None):
def __init__(self, ZAID="", node=None, suppress_warning=False):
if not suppress_warning:
warnings.warn(
"montepy.data_inputs.isotope.Isotope is deprecated and will be renamed: Nuclide.\n"
"See <https://www.montepy.org/migrations/migrate0_1.html> for more information ",
FutureWarning,
)

if node is not None and isinstance(node, ValueNode):
if node.type == float:
node = ValueNode(node.token, str, node.padding)
Expand Down
39 changes: 26 additions & 13 deletions montepy/data_inputs/material.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
import itertools
import re

import warnings


def _number_validator(self, number):
if number <= 0:
Expand Down Expand Up @@ -51,7 +53,7 @@ def __init__(self, input=None):
f"Material definitions for material: {self.number} is not valid.",
)
for isotope_node, fraction in iterator:
isotope = Isotope(node=isotope_node)
isotope = Isotope(node=isotope_node, suppress_warning=True)
fraction.is_negatable_float = True
if not set_atom_frac:
set_atom_frac = True
Expand All @@ -68,8 +70,9 @@ def __init__(self, input=None):
input,
f"Material definitions for material: {self.number} cannot use atom and mass fraction at the same time",
)

self._material_components[isotope] = MaterialComponent(
isotope, fraction
isotope, fraction, suppress_warning=True
)

@make_prop_val_node("_old_number")
Expand Down Expand Up @@ -102,13 +105,23 @@ def is_atom_fraction(self):
@property
def material_components(self):
"""
The internal dictionary containing all the components of this material.
The internal dictionary containing all the components of this material.

The keys are :class:`~montepy.data_inputs.isotope.Isotope` instances, and the values are
:class:`~montepy.data_inputs.material_component.MaterialComponent` instances.
.. deprecated:: 0.4.1
MaterialComponent has been deprecated as part of a redesign for the material
interface due to a critical bug in how MontePy handles duplicate nuclides.
See :ref:`migrate 0 1`.

:rtype: dict
The keys are :class:`~montepy.data_inputs.isotope.Isotope` instances, and the values are
:class:`~montepy.data_inputs.material_component.MaterialComponent` instances.

:rtype: dict
"""
warnings.warn(
f"""material_components is deprecated, and will be removed in MontePy 1.0.0.
See <https://www.montepy.org/migrations/migrate0_1.html> for more information """,
DeprecationWarning,
tjlaboss marked this conversation as resolved.
Show resolved Hide resolved
)
return self._material_components

@make_prop_pointer("_thermal_scattering", thermal_scattering.ThermalScatteringLaw)
Expand Down Expand Up @@ -149,7 +162,7 @@ def format_for_mcnp_input(self, mcnp_version):

def _update_values(self):
new_list = syntax_node.IsotopesNode("new isotope list")
for isotope, component in self.material_components.items():
for isotope, component in self._material_components.items():
isotope._tree.value = isotope.mcnp_str()
new_list.append(("_", isotope._tree, component._tree))
self._tree.nodes["data"] = new_list
Expand Down Expand Up @@ -198,8 +211,8 @@ def __repr__(self):
else:
ret += "mass\n"

for component in self.material_components:
ret += repr(self.material_components[component]) + "\n"
for component in self._material_components:
ret += repr(self._material_components[component]) + "\n"
if self.thermal_scattering:
ret += f"Thermal Scattering: {self.thermal_scattering}"

Expand All @@ -212,7 +225,7 @@ def __str__(self):
def _get_material_elements(self):
sortable_components = [
(iso, component.fraction)
for iso, component in self.material_components.items()
for iso, component in self._material_components.items()
]
sorted_comps = sorted(sortable_components)
elements_set = set()
Expand All @@ -224,7 +237,7 @@ def _get_material_elements(self):
return elements

def validate(self):
if len(self.material_components) == 0:
if len(self._material_components) == 0:
raise IllegalState(
f"Material: {self.number} does not have any components defined."
)
Expand All @@ -237,10 +250,10 @@ def __hash__(self):

"""
temp_hash = ""
sorted_isotopes = sorted(list(self.material_components.keys()))
sorted_isotopes = sorted(list(self._material_components.keys()))
for isotope in sorted_isotopes:
temp_hash = hash(
(temp_hash, str(isotope), self.material_components[isotope].fraction)
(temp_hash, str(isotope), self._material_components[isotope].fraction)
)

return hash((temp_hash, self.number))
Expand Down
17 changes: 16 additions & 1 deletion montepy/data_inputs/material_component.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
from montepy.input_parser.syntax_node import PaddingNode, ValueNode
from montepy.utilities import make_prop_val_node

import warnings


def _enforce_positive(self, val):
if val <= 0:
Expand All @@ -15,13 +17,26 @@ class MaterialComponent:

For example: this may be H-1 in water: like 1001.80c — 0.6667

.. deprecated:: 0.4.1
MaterialComponent has been deprecated as part of a redesign for the material
interface due to a critical bug in how MontePy handles duplicate nuclides.
See :ref:`migrate 0 1`.

:param isotope: the Isotope object representing this isotope
:type isotope: Isotope
:param fraction: the fraction of this component in the material
:type fraction: ValueNode
:param suppress_warning: Whether to suppress the ``DeprecationWarning``.
:type suppress_warning: bool
"""

def __init__(self, isotope, fraction):
def __init__(self, isotope, fraction, suppress_warning=False):
if not suppress_warning:
warnings.warn(
f"""MaterialComponent is deprecated, and will be removed in MontePy 1.0.0.
See <https://www.montepy.org/migrations/migrate0_1.html> for more information """,
DeprecationWarning,
)
if not isinstance(isotope, Isotope):
raise TypeError(f"Isotope must be an Isotope. {isotope} given")
if isinstance(fraction, (float, int)):
Expand Down
Loading