Repository: idaholab/MontePy

Issue:
Bug with no default universe

**Describe the bug**

When a cell is made from scratch, has no universe, and universes are used in a problem (I think) an `AttributeError` is raised. 

**To Reproduce**


``` python
import montepy

problem = montepy.read_input("demo/pin_cell.imcnp")

universe = montepy.Universe(100)
problem.universes.append(universe)
universe.claim(problem.cells)

for surf in problem.surfaces:
    surf.is_reflecting = False

surfs = problem.surfaces
right_surf = surfs[104]
left_surf = surfs[103]
y_top_surf = surfs[106]
y_bot_surf = surfs[105]
z_top_surf = surfs[102]
z_bot_surf = surfs[101]

unit_cell = montepy.Cell()
unit_cell.number = problem.cells.request_number()
problem.cells.append(unit_cell)
unit_cell.geometry = -right_surf & +left_surf
unit_cell.geometry &= -y_top_surf & +y_bot_surf
unit_cell.geometry &= -z_top_surf & +z_bot_surf
unit_cell.importance.neutron = 1.0

problem.write_problem("test.imcnp", overwrite=True)
```

**Error Message (if any)**

Leads to 

<details> 

``` python
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[8], line 1
----> 1 problem.write_problem("test.imcnp", overwrite=True)

File [~/mambaforge/lib/python3.12/site-packages/montepy/mcnp_problem.py:542](http://localhost:8888/home/mgale/mambaforge/lib/python3.12/site-packages/montepy/mcnp_problem.py#line=541), in MCNP_Problem.write_problem(self, destination, overwrite)
    540     new_file = MCNP_InputFile(destination, overwrite=overwrite)
    541     with new_file.open("w") as fh:
--> 542         self._write_to_stream(fh)
    543 else:
    544     raise TypeError(
    545         f"destination f{destination} is not a file path or writable object"
    546     )

File [~/mambaforge/lib/python3.12/site-packages/montepy/mcnp_problem.py:607](http://localhost:8888/home/mgale/mambaforge/lib/python3.12/site-packages/montepy/mcnp_problem.py#line=606), in MCNP_Problem._write_to_stream(self, inp)
    605     if terminate:
    606         inp.write("\n")
--> 607 for line in self.cells._run_children_format_for_mcnp(
    608     self.data_inputs, self.mcnp_version
    609 ):
    610     inp.write(line + "\n")
    612 inp.write("\n")

File [~/mambaforge/lib/python3.12/site-packages/montepy/cells.py:192](http://localhost:8888/home/mgale/mambaforge/lib/python3.12/site-packages/montepy/cells.py#line=191), in Cells._run_children_format_for_mcnp(self, data_inputs, mcnp_version)
    190 for attr, _ in montepy.Cell._INPUTS_TO_PROPERTY.values():
    191     if getattr(self, attr) not in data_inputs:
--> 192         if buf := getattr(self, attr).format_for_mcnp_input(mcnp_version):
    193             ret += buf
    194 return ret

File [~/mambaforge/lib/python3.12/site-packages/montepy/mcnp_object.py:51](http://localhost:8888/home/mgale/mambaforge/lib/python3.12/site-packages/montepy/mcnp_object.py#line=50), in _ExceptionContextAdder._wrap_attr_call.<locals>.wrapped(*args, **kwargs)
     49 self._handling_exception = True
     50 try:
---> 51     add_line_number_to_exception(e, self)
     52 finally:
     53     del self._handling_exception

File [~/mambaforge/lib/python3.12/site-packages/montepy/errors.py:227](http://localhost:8888/home/mgale/mambaforge/lib/python3.12/site-packages/montepy/errors.py#line=226), in add_line_number_to_exception(error, broken_robot)
    225 # avoid calling this n times recursively
    226 if hasattr(error, "montepy_handled"):
--> 227     raise error
    228 error.montepy_handled = True
    229 args = error.args

File [~/mambaforge/lib/python3.12/site-packages/montepy/mcnp_object.py:43](http://localhost:8888/home/mgale/mambaforge/lib/python3.12/site-packages/montepy/mcnp_object.py#line=42), in _ExceptionContextAdder._wrap_attr_call.<locals>.wrapped(*args, **kwargs)
     40 @functools.wraps(func)
     41 def wrapped(*args, **kwargs):
     42     try:
---> 43         return func(*args, **kwargs)
     44     except Exception as e:
     45         if len(args) > 0 and isinstance(args[0], MCNP_Object):

File [~/mambaforge/lib/python3.12/site-packages/montepy/data_inputs/cell_modifier.py:279](http://localhost:8888/home/mgale/mambaforge/lib/python3.12/site-packages/montepy/data_inputs/cell_modifier.py#line=278), in CellModifierInput.format_for_mcnp_input(self, mcnp_version, has_following)
    277 # print in either block
    278 if (self.in_cell_block != print_in_data_block) and self._is_worth_printing:
--> 279     self._update_values()
    280     return self.wrap_string_for_mcnp(
    281         self._format_tree(),
    282         mcnp_version,
    283         True,
    284         suppress_blank_end=not self.in_cell_block,
    285     )
    286 return []

File [~/mambaforge/lib/python3.12/site-packages/montepy/mcnp_object.py:51](http://localhost:8888/home/mgale/mambaforge/lib/python3.12/site-packages/montepy/mcnp_object.py#line=50), in _ExceptionContextAdder._wrap_attr_call.<locals>.wrapped(*args, **kwargs)
     49 self._handling_exception = True
     50 try:
---> 51     add_line_number_to_exception(e, self)
     52 finally:
     53     del self._handling_exception

File [~/mambaforge/lib/python3.12/site-packages/montepy/errors.py:227](http://localhost:8888/home/mgale/mambaforge/lib/python3.12/site-packages/montepy/errors.py#line=226), in add_line_number_to_exception(error, broken_robot)
    225 # avoid calling this n times recursively
    226 if hasattr(error, "montepy_handled"):
--> 227     raise error
    228 error.montepy_handled = True
    229 args = error.args

File [~/mambaforge/lib/python3.12/site-packages/montepy/mcnp_object.py:43](http://localhost:8888/home/mgale/mambaforge/lib/python3.12/site-packages/montepy/mcnp_object.py#line=42), in _ExceptionContextAdder._wrap_attr_call.<locals>.wrapped(*args, **kwargs)
     40 @functools.wraps(func)
     41 def wrapped(*args, **kwargs):
     42     try:
---> 43         return func(*args, **kwargs)
     44     except Exception as e:
     45         if len(args) > 0 and isinstance(args[0], MCNP_Object):

File [~/mambaforge/lib/python3.12/site-packages/montepy/data_inputs/cell_modifier.py:226](http://localhost:8888/home/mgale/mambaforge/lib/python3.12/site-packages/montepy/data_inputs/cell_modifier.py#line=225), in CellModifierInput._update_values(self)
    224     self._update_cell_values()
    225 else:
--> 226     new_vals = self._collect_new_values()
    227     self.data.update_with_new_values(new_vals)

File [~/mambaforge/lib/python3.12/site-packages/montepy/mcnp_object.py:51](http://localhost:8888/home/mgale/mambaforge/lib/python3.12/site-packages/montepy/mcnp_object.py#line=50), in _ExceptionContextAdder._wrap_attr_call.<locals>.wrapped(*args, **kwargs)
     49 self._handling_exception = True
     50 try:
---> 51     add_line_number_to_exception(e, self)
     52 finally:
     53     del self._handling_exception

File [~/mambaforge/lib/python3.12/site-packages/montepy/errors.py:227](http://localhost:8888/home/mgale/mambaforge/lib/python3.12/site-packages/montepy/errors.py#line=226), in add_line_number_to_exception(error, broken_robot)
    225 # avoid calling this n times recursively
    226 if hasattr(error, "montepy_handled"):
--> 227     raise error
    228 error.montepy_handled = True
    229 args = error.args

File [~/mambaforge/lib/python3.12/site-packages/montepy/mcnp_object.py:43](http://localhost:8888/home/mgale/mambaforge/lib/python3.12/site-packages/montepy/mcnp_object.py#line=42), in _ExceptionContextAdder._wrap_attr_call.<locals>.wrapped(*args, **kwargs)
     40 @functools.wraps(func)
     41 def wrapped(*args, **kwargs):
     42     try:
---> 43         return func(*args, **kwargs)
     44     except Exception as e:
     45         if len(args) > 0 and isinstance(args[0], MCNP_Object):

File [~/mambaforge/lib/python3.12/site-packages/montepy/data_inputs/universe_input.py:152](http://localhost:8888/home/mgale/mambaforge/lib/python3.12/site-packages/montepy/data_inputs/universe_input.py#line=151), in UniverseInput._collect_new_values(self)
    149 ret = []
    150 for cell in self._problem.cells:
    151     # force value update here with _tree_value
--> 152     if cell._universe._tree_value.value == 0:
    153         # access ValueNode directly to avoid override with _tree_value
    154         cell._universe._old_number._value = None
    155     ret.append(cell._universe._old_number)

File [~/mambaforge/lib/python3.12/site-packages/montepy/mcnp_object.py:51](http://localhost:8888/home/mgale/mambaforge/lib/python3.12/site-packages/montepy/mcnp_object.py#line=50), in _ExceptionContextAdder._wrap_attr_call.<locals>.wrapped(*args, **kwargs)
     49 self._handling_exception = True
     50 try:
---> 51     add_line_number_to_exception(e, self)
     52 finally:
     53     del self._handling_exception

File [~/mambaforge/lib/python3.12/site-packages/montepy/errors.py:251](http://localhost:8888/home/mgale/mambaforge/lib/python3.12/site-packages/montepy/errors.py#line=250), in add_line_number_to_exception(error, broken_robot)
    249 args = (message,) + args[1:]
    250 error.args = args
--> 251 raise error.with_traceback(trace)

File [~/mambaforge/lib/python3.12/site-packages/montepy/mcnp_object.py:43](http://localhost:8888/home/mgale/mambaforge/lib/python3.12/site-packages/montepy/mcnp_object.py#line=42), in _ExceptionContextAdder._wrap_attr_call.<locals>.wrapped(*args, **kwargs)
     40 @functools.wraps(func)
     41 def wrapped(*args, **kwargs):
     42     try:
---> 43         return func(*args, **kwargs)
     44     except Exception as e:
     45         if len(args) > 0 and isinstance(args[0], MCNP_Object):

File [~/mambaforge/lib/python3.12/site-packages/montepy/data_inputs/universe_input.py:144](http://localhost:8888/home/mgale/mambaforge/lib/python3.12/site-packages/montepy/data_inputs/universe_input.py#line=143), in UniverseInput._tree_value(self)
    141 @property
    142 def _tree_value(self):
    143     val = self._old_number
--> 144     val.value = self.universe.number
    145     val.is_negative = self.not_truncated
    146     return val

AttributeError: 'NoneType' object has no attribute 'number'

Error came from  from an unknown file.
```
</details>



**Version**

 - Version 1.0.0a3




Discussion:

Patch:
diff --git a/doc/source/changelog.rst b/doc/source/changelog.rst
index 573dd5cb..953c134f 100644
--- a/doc/source/changelog.rst
+++ b/doc/source/changelog.rst
@@ -36,6 +36,7 @@ MontePy Changelog
 * Fixed bug where ``surf.is_reflecting`` would put an extra space in the output e.g., ``* 1 PZ...`` (:issue:`697`).
 * Fixed bug where setting a lattice would print as ``LAT=None``. Also switched ``CellModifier`` to print in the cell block by default (:issue:`699`). 
 * Fixed bug that wouldn't allow cloning most surfaces (:issue:`704`).
+* Fixed bug that crashed when some cells were not assigned to any universes (:issue:`705`).
  
 **Breaking Changes**
 
diff --git a/montepy/data_inputs/universe_input.py b/montepy/data_inputs/universe_input.py
index 2d8ed8f9..05964ab6 100644
--- a/montepy/data_inputs/universe_input.py
+++ b/montepy/data_inputs/universe_input.py
@@ -141,7 +141,9 @@ class UniverseInput(CellModifierInput):
     @property
     def _tree_value(self):
         val = self._old_number
-        val.value = self.universe.number
+        val.value = 0
+        if self.universe is not None:
+            val.value = self.universe.number
         val.is_negative = self.not_truncated
         return val
 


Test patch:
diff --git a/tests/test_universe_integration.py b/tests/test_universe_integration.py
index 891dbbea..8f77be30 100644
--- a/tests/test_universe_integration.py
+++ b/tests/test_universe_integration.py
@@ -1,3 +1,4 @@
+import io
 from pathlib import Path
 import pytest
 from tests.test_cell_problem import verify_export as cell_verify
@@ -26,6 +27,16 @@ def cells(basic_parsed_cell, basic_cell):
     return (basic_parsed_cell, basic_cell)
 
 
+@pytest.fixture(scope="module")
+def simple_problem():
+    return montepy.read_input(Path("tests") / "inputs" / "test.imcnp")
+
+
+@pytest.fixture
+def cp_simple_problem(simple_problem):
+    return simple_problem.clone()
+
+
 def test_universe_setter(cells):
     for basic_cell in cells:
         uni = Universe(5)
@@ -85,3 +96,14 @@ def test_mc_workshop_edge_case():
     problem.universes.append(lat_universe)
     unit_cell.universe = lat_universe
     cell_verify(unit_cell)
+
+
+def test_no_universe(cp_simple_problem):
+    prob = cp_simple_problem
+    prob.cells[2].universe = montepy.Universe(5)
+    new_cell = montepy.Cell(number=55)
+    new_cell.geometry = -prob.surfaces[1000]
+    prob.cells.append(new_cell)
+    prob.print_in_data_block["u"] = True
+    with io.StringIO() as fh:
+        prob.write_problem(fh)