Repository: openmc-dev/openmc

Issue:
Potential for error check when region uses intersection or union operations between surface objects and integers
I mistakenly tried to create a region that was the intersection of an `openmc.ZCylinder` and two integers when I actually meant to intersect with two `openmc.ZPlane` objects at those integer heights.

OpenMC allowed me to
- create a cell with this defective region
- create a geometry using that cell 

but then failed when I attempted to export the geometry. I believe that maybe some error checking could be added to [region.py](https://github.com/openmc-dev/openmc/blob/610a5d8c3485034626ce54ce87a88eaa8071cc23/openmc/region.py)

I created an MWE
```python
import openmc

cyl = openmc.ZCylinder(r=1.0)
z_lower_coord = 1
z_upper_coord = 2
region = -cyl & +z_lower_coord & +z_upper_coord
finite_cylinder = openmc.Cell(region=region)
geom = openmc.Geometry([finite_cylinder])
geom.export_to_xml()
```
which generates this error for me
```
Traceback (most recent call last):
  File "fake_region_mwe.py", line 9, in <module>
    geom.export_to_xml()
  File "openmc/openmc/geometry.py", line 158, in export_to_xml
    root_element = self.to_xml_element(remove_surfs)
  File "openmc/openmc/geometry.py", line 132, in to_xml_element
    self.root_universe.create_xml_subelement(element, memo=set())
  File "openmc/openmc/universe.py", line 689, in create_xml_subelement
    cell_element = cell.create_xml_subelement(xml_element, memo)
  File "openmc/openmc/cell.py", line 630, in create_xml_subelement
    create_surface_elements(self.region, xml_element, memo)
  File "openmc/openmc/cell.py", line 627, in create_surface_elements
    create_surface_elements(subnode, element, memo)
  File "openmc/openmc/cell.py", line 626, in create_surface_elements
    for subnode in node:
TypeError: 'int' object is not iterable
```





Tagging @pshriwise who understands the inner workings better in case anyone wants to have a discussion on more error checking.


Discussion:

Resolving patch:
diff --git a/openmc/region.py b/openmc/region.py
--- a/openmc/region.py
+++ b/openmc/region.py
@@ -4,7 +4,6 @@
 
 import numpy as np
 
-from .checkvalue import check_type
 from .bounding_box import BoundingBox
 
 
@@ -362,6 +361,9 @@ class Intersection(Region, MutableSequence):
 
     def __init__(self, nodes):
         self._nodes = list(nodes)
+        for node in nodes:
+            if not isinstance(node, Region):
+                raise ValueError('Intersection operands must be of type Region')
 
     def __and__(self, other):
         new = Intersection(self)
@@ -450,6 +452,9 @@ class Union(Region, MutableSequence):
 
     def __init__(self, nodes):
         self._nodes = list(nodes)
+        for node in nodes:
+            if not isinstance(node, Region):
+                raise ValueError('Union operands must be of type Region')
 
     def __or__(self, other):
         new = Union(self)
@@ -566,7 +571,8 @@ def node(self):
 
     @node.setter
     def node(self, node):
-        check_type('node', node, Region)
+        if not isinstance(node, Region):
+            raise ValueError('Complement operand must be of type Region')
         self._node = node
 
     @property


Test patch:
diff --git a/tests/unit_tests/test_region.py b/tests/unit_tests/test_region.py
--- a/tests/unit_tests/test_region.py
+++ b/tests/unit_tests/test_region.py
@@ -222,3 +222,21 @@ def test_translate_inplace():
     # Translating a region in-place should *not* produce new surfaces
     region3 = region.translate((0.5, -6.7, 3.9), inplace=True)
     assert str(region) == str(region3)
+
+
+def test_invalid_operands():
+    s = openmc.Sphere()
+    z = 3
+
+    # Intersection with invalid operand
+    with pytest.raises(ValueError, match='must be of type Region'):
+        -s & +z
+
+    # Union with invalid operand
+    with pytest.raises(ValueError, match='must be of type Region'):
+        -s | +z
+
+    # Complement with invalid operand
+    with pytest.raises(ValueError, match='must be of type Region'):
+        openmc.Complement(z)
+