Skip to content
Open
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
5 changes: 3 additions & 2 deletions demo/0_IntroToMontePy.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -228,8 +228,9 @@
},
"outputs": [],
"source": [
"#note this install_montepy is only necessary for jupyterlite; You don't need to use it locally\n",
"# note this install_montepy is only necessary for jupyterlite; You don't need to use it locally\n",
"from _config import install_montepy\n",
"\n",
"install_montepy()\n",
"\n",
"import montepy\n",
Expand Down Expand Up @@ -428,7 +429,7 @@
},
"outputs": [],
"source": [
"#note this %pip is only needed for running in jupyterlite online\n",
"# note this %pip is only needed for running in jupyterlite online\n",
"%pip install ipython\n",
"from _config import IFrame\n",
"\n",
Expand Down
15 changes: 11 additions & 4 deletions demo/1_PinCellCorrection_inter.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,9 @@
},
"outputs": [],
"source": [
"#note this install_montepy is only necessary for jupyterlite; You don't need to use it locally\n",
"# note this install_montepy is only necessary for jupyterlite; You don't need to use it locally\n",
"from _config import install_montepy\n",
"\n",
"install_montepy()\n",
"\n",
"# actually needed\n",
Expand Down Expand Up @@ -234,7 +235,9 @@
},
"outputs": [],
"source": [
"IFrame(\"https://www.montepy.org/en/stable/api/generated/montepy.MCNP_Problem.html#montepy.MCNP_Problem\")"
"IFrame(\n",
" \"https://www.montepy.org/en/stable/api/generated/montepy.MCNP_Problem.html#montepy.MCNP_Problem\"\n",
")"
]
},
{
Expand Down Expand Up @@ -304,7 +307,9 @@
},
"outputs": [],
"source": [
"IFrame(\"https://www.montepy.org/en/stable/api/generated/montepy.Surfaces.html#montepy.Surfaces\")"
"IFrame(\n",
" \"https://www.montepy.org/en/stable/api/generated/montepy.Surfaces.html#montepy.Surfaces\"\n",
")"
]
},
{
Expand Down Expand Up @@ -374,7 +379,9 @@
},
"outputs": [],
"source": [
"IFrame(\"https://www.montepy.org/en/stable/api/generated/montepy.Surface.html#montepy.Surface\")"
"IFrame(\n",
" \"https://www.montepy.org/en/stable/api/generated/montepy.Surface.html#montepy.Surface\"\n",
")"
]
},
{
Expand Down
1 change: 1 addition & 0 deletions demo/2_BuildAssembly_inter.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
"outputs": [],
"source": [
"from _config import install_montepy\n",
"\n",
"install_montepy()\n",
"\n",
"import montepy\n",
Expand Down
1 change: 1 addition & 0 deletions demo/3_AddingGuideTubes_inter.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@
"outputs": [],
"source": [
"from _config import install_montepy\n",
"\n",
"install_montepy()\n",
"\n",
"import montepy\n",
Expand Down
1 change: 1 addition & 0 deletions demo/4_AxialDiscretize_inter.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
"outputs": [],
"source": [
"from _config import install_montepy\n",
"\n",
"install_montepy()\n",
"\n",
"import montepy\n",
Expand Down
21 changes: 16 additions & 5 deletions demo/answers/1_PinCellCorrection.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,9 @@
},
"outputs": [],
"source": [
"#note this install_montepy is only necessary for jupyterlite; You don't need to use it locally\n",
"# note this install_montepy is only necessary for jupyterlite; You don't need to use it locally\n",
"from _config import install_montepy\n",
"\n",
"install_montepy()\n",
"\n",
"# actually needed\n",
Expand Down Expand Up @@ -233,7 +234,11 @@
},
"outputs": [],
"source": [
"IFrame(\"https://www.montepy.org/en/stable/api/generated/montepy.MCNP_Problem.html#montepy.MCNP_Problem\", 800, 600)"
"IFrame(\n",
" \"https://www.montepy.org/en/stable/api/generated/montepy.MCNP_Problem.html#montepy.MCNP_Problem\",\n",
" 800,\n",
" 600,\n",
")"
]
},
{
Expand Down Expand Up @@ -317,7 +322,9 @@
},
"outputs": [],
"source": [
"IFrame(\"https://www.montepy.org/en/stable/api/generated/montepy.Surfaces.html#montepy.Surfaces\")"
"IFrame(\n",
" \"https://www.montepy.org/en/stable/api/generated/montepy.Surfaces.html#montepy.Surfaces\"\n",
")"
]
},
{
Expand Down Expand Up @@ -402,7 +409,9 @@
},
"outputs": [],
"source": [
"IFrame(\"https://www.montepy.org/en/stable/api/generated/montepy.Surface.html#montepy.Surface\")"
"IFrame(\n",
" \"https://www.montepy.org/en/stable/api/generated/montepy.Surface.html#montepy.Surface\"\n",
")"
]
},
{
Expand Down Expand Up @@ -1561,7 +1570,9 @@
},
"outputs": [],
"source": [
"IFrame(\"https://www.montepy.org/en/stable/api/generated/montepy.MCNP_Problem.html#montepy.MCNP_Problem\")"
"IFrame(\n",
" \"https://www.montepy.org/en/stable/api/generated/montepy.MCNP_Problem.html#montepy.MCNP_Problem\"\n",
")"
]
},
{
Expand Down
3 changes: 2 additions & 1 deletion demo/answers/2_BuildAssembly.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,9 @@
},
"outputs": [],
"source": [
"#note this install_montepy is only necessary for jupyterlite; You don't need to use it locally\n",
"# note this install_montepy is only necessary for jupyterlite; You don't need to use it locally\n",
"from _config import install_montepy\n",
"\n",
"install_montepy()\n",
"\n",
"import montepy\n",
Expand Down
1 change: 1 addition & 0 deletions demo/answers/3_AddingGuideTubes.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
"outputs": [],
"source": [
"from _config import install_montepy\n",
"\n",
"install_montepy()\n",
"\n",
"import montepy\n",
Expand Down
1 change: 1 addition & 0 deletions demo/answers/4_AxialDiscretize.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
"outputs": [],
"source": [
"from _config import install_montepy\n",
"\n",
"install_montepy()\n",
"\n",
"import montepy\n",
Expand Down
7 changes: 7 additions & 0 deletions doc/source/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ MontePy Changelog
1.2 releases
============

#Next Version#
--------------

**Performance Improvement**

* Implement _collection_ref to link objects to their NumberedObjectCollection parent (:issue:`867`).

1.2.0
--------------

Expand Down
4 changes: 4 additions & 0 deletions montepy/cell.py
Original file line number Diff line number Diff line change
Expand Up @@ -927,6 +927,10 @@ def num(obj):
for key in keys:
attr = getattr(self, key)
setattr(result, key, copy.deepcopy(attr, memo))
# Clear weakrefs so the cloned cell isn't linked to original collection/problem
# This prevents number conflict checks against the original collection
result._collection_ref = None
result._problem_ref = None
# copy geometry
for special in special_keys:
new_objs = []
Expand Down
51 changes: 37 additions & 14 deletions montepy/numbered_mcnp_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import itertools
from typing import Union
from numbers import Integral
import weakref

from montepy.mcnp_object import MCNP_Object, InitInput
import montepy
Expand All @@ -14,20 +15,10 @@
def _number_validator(self, number):
if number < 0:
raise ValueError("number must be >= 0")
if self._problem:
obj_map = montepy.MCNP_Problem._NUMBERED_OBJ_MAP
try:
collection_type = obj_map[type(self)]
except KeyError as e:
found = False
for obj_class in obj_map:
if isinstance(self, obj_class):
collection_type = obj_map[obj_class]
found = True
break
if not found:
raise e
collection = getattr(self._problem, collection_type.__name__.lower())

# Only validate against collection if linked to a collection
if self._collection is not None:
collection = self._collection
collection.check_number(number)
collection._update_number(self.number, number, self)

Expand Down Expand Up @@ -58,6 +49,7 @@ def __init__(
if not input:
self._number = self._generate_default_node(int, -1)
super().__init__(input, parser)
self._collection_ref = None
self._load_init_num(number)

def _load_init_num(self, number):
Expand Down Expand Up @@ -123,6 +115,37 @@ def _add_children_objs(self, problem):
except (TypeError, AssertionError):
prob_collect.append(child_collect)

@property
def _collection(self):
"""Returns the parent collection this object belongs to, if any."""
if self._collection_ref is not None:
return self._collection_ref()
return None

def _link_to_collection(self, collection):
"""Links this object to the given collection via a weakref.

Parameters
----------
collection : NumberedObjectCollection
The collection to link this object to.
"""
self._collection_ref = weakref.ref(collection)

def _unlink_from_collection(self):
"""Unlinks this object from its collection."""
self._collection_ref = None

def __getstate__(self):
state = super().__getstate__()
# Remove _collection_ref weakref as it can't be pickled
state.pop("_collection_ref", None)
return state

def __setstate__(self, crunchy_data):
crunchy_data["_collection_ref"] = None
super().__setstate__(crunchy_data)

def clone(self, starting_number=None, step=None):
"""Create a new independent instance of this object with a new number.

Expand Down
35 changes: 31 additions & 4 deletions montepy/numbered_object_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ def __init__(
)
)
self.__num_cache[obj.number] = obj
self._link_to_collection(obj)
self._objects = objects

def link_to_problem(self, problem):
Expand All @@ -198,13 +199,38 @@ def link_to_problem(self, problem):
self._problem_ref = weakref.ref(problem)
for obj in self:
obj.link_to_problem(problem)
# the _collection_ref that points to the main cells collection.
if problem is not None:
existing_coll = obj._collection
if existing_coll is None or existing_coll._problem is not problem:
self._link_to_collection(obj)

@property
def _problem(self):
if self._problem_ref is not None:
return self._problem_ref()
return None

def _link_to_collection(self, obj):
"""Links the given object to this collection via a weakref.

Parameters
----------
obj : Numbered_MCNP_Object
The object to link to this collection.
"""
obj._link_to_collection(self)

def _unlink_from_collection(self, obj):
"""Unlinks the given object from this collection.

Parameters
----------
obj : Numbered_MCNP_Object
The object to unlink from this collection.
"""
obj._unlink_from_collection()

def __getstate__(self):
state = self.__dict__.copy()
weakref_key = "_problem_ref"
Expand Down Expand Up @@ -341,10 +367,8 @@ def extend(self, other_list):
)
if obj.number in nums:
raise NumberConflictError(
(
f"When adding to {type(self).__name__} there was a number collision due to "
f"adding {obj} which conflicts with {self[obj.number]}"
)
f"When adding to {type(self).__name__} there was a number collision due to "
f"adding {obj} which conflicts with existing object number {obj.number}"
)
nums.add(obj.number)
for obj in other_list:
Expand Down Expand Up @@ -496,6 +520,7 @@ def __internal_append(self, obj, **kwargs):
)
self.__num_cache[obj.number] = obj
self._objects.append(obj)
self._link_to_collection(obj)
self._append_hook(obj, **kwargs)
if self._problem:
obj.link_to_problem(self._problem)
Expand All @@ -507,6 +532,7 @@ def __internal_delete(self, obj, **kwargs):
"""
self.__num_cache.pop(obj.number, None)
self._objects.remove(obj)
self._unlink_from_collection(obj)
self._delete_hook(obj, **kwargs)

def add(self, obj: Numbered_MCNP_Object):
Expand Down Expand Up @@ -613,6 +639,7 @@ def append_renumber(self, obj, step=1):
number = obj.number if obj.number > 0 else 1
if self._problem:
obj.link_to_problem(self._problem)
self._unlink_from_collection(obj)
try:
self.append(obj)
except (NumberConflictError, ValueError) as e:
Expand Down
2 changes: 2 additions & 0 deletions tests/test_numbered_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ def test_extend(self, cp_simple_problem):
extender = copy.deepcopy(extender)
for surf in extender:
surf._problem = None
surf._collection_ref = None
surfaces[1000].number = 1
extender[0].number = 1000
extender[1].number = 70
Expand Down Expand Up @@ -161,6 +162,7 @@ def test_append_renumber(self, cp_simple_problem):
cells.append_renumber(cell, "hi")
cell = copy.deepcopy(cell)
cell._problem = None
cell._collection_ref = None
cell.number = 1
cells.append_renumber(cell)
assert cell.number == 4
Expand Down