We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Implement contains for BoundingBox
## Description
Implement `__contains__` for `BoundingBox` containing either a point, or another `BoundingBox`. This means that users could then write:

`if point in box:` or `if little_box in big_box`.


## Alternatives
It is possible for users to accomplish this currently but requires some clever coding to avoid becoming difficult to read:

``` python
def in_box(point, box):
   for min_p, p, max_p in zip(box.lower_left, point, box.upper_right):
     if p < min_p or > max_p:
        return False 
   return True
```


## Compatibility
This would be an enhancement, and would not alter the behavior of the existing API. 

There is a risk though that users will misinterpret the results. A point in the bounding box of a volume *may* be in the volume, but not necessarily. A user could misuse this information and create problems for themselves. Also a small volume's bounding box can be completely contained in another volume's bounding box, and be completely outside that other volume. 

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `openmc/bounding_box.py`
Content:
```
1 from __future__ import annotations
2 from typing import Iterable
3 
4 import numpy as np
5 
6 from .checkvalue import check_length
7 
8 
9 class BoundingBox:
10     """Axis-aligned bounding box.
11 
12     .. versionadded:: 0.14.0
13 
14     Parameters
15     ----------
16     lower_left : iterable of float
17         The x, y, z coordinates of the lower left corner of the bounding box in [cm]
18     upper_right : iterable of float
19         The x, y, z coordinates of the upper right corner of the bounding box in [cm]
20 
21     Attributes
22     ----------
23     center : numpy.ndarray
24         x, y, z coordinates of the center of the bounding box in [cm]
25     lower_left : numpy.ndarray
26         The x, y, z coordinates of the lower left corner of the bounding box in [cm]
27     upper_right : numpy.ndarray
28         The x, y, z coordinates of the upper right corner of the bounding box in [cm]
29     volume : float
30         The volume of the bounding box in [cm^3]
31     extent : dict
32         A dictionary of basis as keys and the extent (left, right, bottom, top)
33         as values. Intended use in Matplotlib plots when setting extent
34     width : iterable of float
35         The width of the x, y and z axis in [cm]
36     """
37 
38     def __init__(self, lower_left: Iterable[float], upper_right: Iterable[float]):
39         check_length("lower_left", lower_left, 3, 3)
40         check_length("upper_right", upper_right, 3, 3)
41         self._bounds = np.asarray([lower_left, upper_right], dtype=float)
42 
43     def __repr__(self) -> str:
44         return "BoundingBox(lower_left={}, upper_right={})".format(
45             tuple(self.lower_left), tuple(self.upper_right))
46 
47     def __getitem__(self, key) -> np.ndarray:
48         return self._bounds[key]
49 
50     def __len__(self):
51         return 2
52 
53     def __setitem__(self, key, val):
54         self._bounds[key] = val
55 
56     def __iand__(self, other: BoundingBox) -> BoundingBox:
57         """Updates the box be the intersection of itself and another box
58 
59         Parameters
60         ----------
61         other : BoundingBox
62             The box used to resize this box
63 
64         Returns
65         -------
66         An updated bounding box
67         """
68         self.lower_left = np.maximum(self.lower_left, other.lower_left)
69         self.upper_right = np.minimum(self.upper_right, other.upper_right)
70         return self
71 
72     def __and__(self, other: BoundingBox) -> BoundingBox:
73         new = BoundingBox(*self)
74         new &= other
75         return new
76 
77     def __ior__(self, other: BoundingBox) -> BoundingBox:
78         """Updates the box be the union of itself and another box
79 
80         Parameters
81         ----------
82         other : BoundingBox
83             The box used to resize this box
84 
85         Returns
86         -------
87         An updated bounding box
88         """
89         self.lower_left = np.minimum(self.lower_left, other.lower_left)
90         self.upper_right = np.maximum(self.upper_right, other.upper_right)
91         return self
92 
93     def __or__(self, other: BoundingBox) -> BoundingBox:
94         new = BoundingBox(*self)
95         new |= other
96         return new
97 
98     def __contains__(self, point):
99         """Check whether or not a point is in the bounding box"""
100         return all(point > self.lower_left) and all(point < self.upper_right)
101 
102     @property
103     def center(self) -> np.ndarray:
104         return (self[0] + self[1]) / 2
105 
106     @property
107     def lower_left(self) -> np.ndarray:
108         return self[0]
109 
110     @lower_left.setter
111     def lower_left(self, llc):
112         check_length('lower_left', llc, 3, 3)
113         self[0] = llc
114 
115     @property
116     def upper_right(self) -> np.ndarray:
117         return self[1]
118 
119     @upper_right.setter
120     def upper_right(self, urc):
121         check_length('upper_right', urc, 3, 3)
122         self[1] = urc
123 
124     @property
125     def volume(self) -> float:
126         return np.abs(np.prod(self[1] - self[0]))
127 
128     @property
129     def extent(self):
130         return {
131             "xy": (
132                 self.lower_left[0],
133                 self.upper_right[0],
134                 self.lower_left[1],
135                 self.upper_right[1],
136             ),
137             "xz": (
138                 self.lower_left[0],
139                 self.upper_right[0],
140                 self.lower_left[2],
141                 self.upper_right[2],
142             ),
143             "yz": (
144                 self.lower_left[1],
145                 self.upper_right[1],
146                 self.lower_left[2],
147                 self.upper_right[2],
148             ),
149         }
150 
151     @property
152     def width(self):
153         return self.upper_right - self.lower_left
154 
155     def expand(self, padding_distance: float, inplace: bool = False) -> BoundingBox:
156         """Returns an expanded bounding box
157 
158         Parameters
159         ----------
160         padding_distance : float
161             The distance to enlarge the bounding box by
162         inplace : bool
163             Whether or not to return a new BoundingBox instance or to modify the
164             current BoundingBox object.
165 
166         Returns
167         -------
168         An expanded bounding box
169         """
170         if inplace:
171             self[0] -= padding_distance
172             self[1] += padding_distance
173             return self
174         else:
175             return BoundingBox(self[0] - padding_distance, self[1] + padding_distance)
176 
177     @classmethod
178     def infinite(cls) -> BoundingBox:
179         """Create an infinite box. Useful as a starting point for determining
180            geometry bounds.
181 
182         Returns
183         -------
184         An infinitely large bounding box.
185         """
186         infs = np.full((3,), np.inf)
187         return cls(-infs, infs)
188
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/openmc/bounding_box.py b/openmc/bounding_box.py
--- a/openmc/bounding_box.py
+++ b/openmc/bounding_box.py
@@ -95,9 +95,23 @@
         new |= other
         return new
 
-    def __contains__(self, point):
-        """Check whether or not a point is in the bounding box"""
-        return all(point > self.lower_left) and all(point < self.upper_right)
+    def __contains__(self, other):
+        """Check whether or not a point or another bounding box is in the bounding box.
+
+        For another bounding box to be in the parent it must lie fully inside of it.
+        """
+        # test for a single point
+        if isinstance(other, (tuple, list, np.ndarray)):
+            point = other
+            check_length("Point", point, 3, 3)
+            return all(point > self.lower_left) and all(point < self.upper_right)
+        elif isinstance(other, BoundingBox):
+            return all([p in self for p in [other.lower_left, other.upper_right]])
+        else:
+            raise TypeError(
+                f"Unable to determine if {other} is in the bounding box."
+                f" Expected a tuple or a bounding box, but {type(other)} given"
+            )
 
     @property
     def center(self) -> np.ndarray:

{"golden_diff": "diff --git a/openmc/bounding_box.py b/openmc/bounding_box.py\n--- a/openmc/bounding_box.py\n+++ b/openmc/bounding_box.py\n@@ -95,9 +95,23 @@\n         new |= other\n         return new\n \n-    def __contains__(self, point):\n-        \"\"\"Check whether or not a point is in the bounding box\"\"\"\n-        return all(point > self.lower_left) and all(point < self.upper_right)\n+    def __contains__(self, other):\n+        \"\"\"Check whether or not a point or another bounding box is in the bounding box.\n+\n+        For another bounding box to be in the parent it must lie fully inside of it.\n+        \"\"\"\n+        # test for a single point\n+        if isinstance(other, (tuple, list, np.ndarray)):\n+            point = other\n+            check_length(\"Point\", point, 3, 3)\n+            return all(point > self.lower_left) and all(point < self.upper_right)\n+        elif isinstance(other, BoundingBox):\n+            return all([p in self for p in [other.lower_left, other.upper_right]])\n+        else:\n+            raise TypeError(\n+                f\"Unable to determine if {other} is in the bounding box.\"\n+                f\" Expected a tuple or a bounding box, but {type(other)} given\"\n+            )\n \n     @property\n     def center(self) -> np.ndarray:\n", "issue": "Implement contains for BoundingBox\n## Description\r\nImplement `__contains__` for `BoundingBox` containing either a point, or another `BoundingBox`. This means that users could then write:\r\n\r\n`if point in box:` or `if little_box in big_box`.\r\n\r\n\r\n## Alternatives\r\nIt is possible for users to accomplish this currently but requires some clever coding to avoid becoming difficult to read:\r\n\r\n``` python\r\ndef in_box(point, box):\r\n   for min_p, p, max_p in zip(box.lower_left, point, box.upper_right):\r\n     if p < min_p or > max_p:\r\n        return False \r\n   return True\r\n```\r\n\r\n\r\n## Compatibility\r\nThis would be an enhancement, and would not alter the behavior of the existing API. \r\n\r\nThere is a risk though that users will misinterpret the results. A point in the bounding box of a volume *may* be in the volume, but not necessarily. A user could misuse this information and create problems for themselves. Also a small volume's bounding box can be completely contained in another volume's bounding box, and be completely outside that other volume. \n", "before_files": [{"content": "from __future__ import annotations\nfrom typing import Iterable\n\nimport numpy as np\n\nfrom .checkvalue import check_length\n\n\nclass BoundingBox:\n    \"\"\"Axis-aligned bounding box.\n\n    .. versionadded:: 0.14.0\n\n    Parameters\n    ----------\n    lower_left : iterable of float\n        The x, y, z coordinates of the lower left corner of the bounding box in [cm]\n    upper_right : iterable of float\n        The x, y, z coordinates of the upper right corner of the bounding box in [cm]\n\n    Attributes\n    ----------\n    center : numpy.ndarray\n        x, y, z coordinates of the center of the bounding box in [cm]\n    lower_left : numpy.ndarray\n        The x, y, z coordinates of the lower left corner of the bounding box in [cm]\n    upper_right : numpy.ndarray\n        The x, y, z coordinates of the upper right corner of the bounding box in [cm]\n    volume : float\n        The volume of the bounding box in [cm^3]\n    extent : dict\n        A dictionary of basis as keys and the extent (left, right, bottom, top)\n        as values. Intended use in Matplotlib plots when setting extent\n    width : iterable of float\n        The width of the x, y and z axis in [cm]\n    \"\"\"\n\n    def __init__(self, lower_left: Iterable[float], upper_right: Iterable[float]):\n        check_length(\"lower_left\", lower_left, 3, 3)\n        check_length(\"upper_right\", upper_right, 3, 3)\n        self._bounds = np.asarray([lower_left, upper_right], dtype=float)\n\n    def __repr__(self) -> str:\n        return \"BoundingBox(lower_left={}, upper_right={})\".format(\n            tuple(self.lower_left), tuple(self.upper_right))\n\n    def __getitem__(self, key) -> np.ndarray:\n        return self._bounds[key]\n\n    def __len__(self):\n        return 2\n\n    def __setitem__(self, key, val):\n        self._bounds[key] = val\n\n    def __iand__(self, other: BoundingBox) -> BoundingBox:\n        \"\"\"Updates the box be the intersection of itself and another box\n\n        Parameters\n        ----------\n        other : BoundingBox\n            The box used to resize this box\n\n        Returns\n        -------\n        An updated bounding box\n        \"\"\"\n        self.lower_left = np.maximum(self.lower_left, other.lower_left)\n        self.upper_right = np.minimum(self.upper_right, other.upper_right)\n        return self\n\n    def __and__(self, other: BoundingBox) -> BoundingBox:\n        new = BoundingBox(*self)\n        new &= other\n        return new\n\n    def __ior__(self, other: BoundingBox) -> BoundingBox:\n        \"\"\"Updates the box be the union of itself and another box\n\n        Parameters\n        ----------\n        other : BoundingBox\n            The box used to resize this box\n\n        Returns\n        -------\n        An updated bounding box\n        \"\"\"\n        self.lower_left = np.minimum(self.lower_left, other.lower_left)\n        self.upper_right = np.maximum(self.upper_right, other.upper_right)\n        return self\n\n    def __or__(self, other: BoundingBox) -> BoundingBox:\n        new = BoundingBox(*self)\n        new |= other\n        return new\n\n    def __contains__(self, point):\n        \"\"\"Check whether or not a point is in the bounding box\"\"\"\n        return all(point > self.lower_left) and all(point < self.upper_right)\n\n    @property\n    def center(self) -> np.ndarray:\n        return (self[0] + self[1]) / 2\n\n    @property\n    def lower_left(self) -> np.ndarray:\n        return self[0]\n\n    @lower_left.setter\n    def lower_left(self, llc):\n        check_length('lower_left', llc, 3, 3)\n        self[0] = llc\n\n    @property\n    def upper_right(self) -> np.ndarray:\n        return self[1]\n\n    @upper_right.setter\n    def upper_right(self, urc):\n        check_length('upper_right', urc, 3, 3)\n        self[1] = urc\n\n    @property\n    def volume(self) -> float:\n        return np.abs(np.prod(self[1] - self[0]))\n\n    @property\n    def extent(self):\n        return {\n            \"xy\": (\n                self.lower_left[0],\n                self.upper_right[0],\n                self.lower_left[1],\n                self.upper_right[1],\n            ),\n            \"xz\": (\n                self.lower_left[0],\n                self.upper_right[0],\n                self.lower_left[2],\n                self.upper_right[2],\n            ),\n            \"yz\": (\n                self.lower_left[1],\n                self.upper_right[1],\n                self.lower_left[2],\n                self.upper_right[2],\n            ),\n        }\n\n    @property\n    def width(self):\n        return self.upper_right - self.lower_left\n\n    def expand(self, padding_distance: float, inplace: bool = False) -> BoundingBox:\n        \"\"\"Returns an expanded bounding box\n\n        Parameters\n        ----------\n        padding_distance : float\n            The distance to enlarge the bounding box by\n        inplace : bool\n            Whether or not to return a new BoundingBox instance or to modify the\n            current BoundingBox object.\n\n        Returns\n        -------\n        An expanded bounding box\n        \"\"\"\n        if inplace:\n            self[0] -= padding_distance\n            self[1] += padding_distance\n            return self\n        else:\n            return BoundingBox(self[0] - padding_distance, self[1] + padding_distance)\n\n    @classmethod\n    def infinite(cls) -> BoundingBox:\n        \"\"\"Create an infinite box. Useful as a starting point for determining\n           geometry bounds.\n\n        Returns\n        -------\n        An infinitely large bounding box.\n        \"\"\"\n        infs = np.full((3,), np.inf)\n        return cls(-infs, infs)\n", "path": "openmc/bounding_box.py"}], "after_files": [{"content": "from __future__ import annotations\nfrom typing import Iterable\n\nimport numpy as np\n\nfrom .checkvalue import check_length\n\n\nclass BoundingBox:\n    \"\"\"Axis-aligned bounding box.\n\n    .. versionadded:: 0.14.0\n\n    Parameters\n    ----------\n    lower_left : iterable of float\n        The x, y, z coordinates of the lower left corner of the bounding box in [cm]\n    upper_right : iterable of float\n        The x, y, z coordinates of the upper right corner of the bounding box in [cm]\n\n    Attributes\n    ----------\n    center : numpy.ndarray\n        x, y, z coordinates of the center of the bounding box in [cm]\n    lower_left : numpy.ndarray\n        The x, y, z coordinates of the lower left corner of the bounding box in [cm]\n    upper_right : numpy.ndarray\n        The x, y, z coordinates of the upper right corner of the bounding box in [cm]\n    volume : float\n        The volume of the bounding box in [cm^3]\n    extent : dict\n        A dictionary of basis as keys and the extent (left, right, bottom, top)\n        as values. Intended use in Matplotlib plots when setting extent\n    width : iterable of float\n        The width of the x, y and z axis in [cm]\n    \"\"\"\n\n    def __init__(self, lower_left: Iterable[float], upper_right: Iterable[float]):\n        check_length(\"lower_left\", lower_left, 3, 3)\n        check_length(\"upper_right\", upper_right, 3, 3)\n        self._bounds = np.asarray([lower_left, upper_right], dtype=float)\n\n    def __repr__(self) -> str:\n        return \"BoundingBox(lower_left={}, upper_right={})\".format(\n            tuple(self.lower_left), tuple(self.upper_right))\n\n    def __getitem__(self, key) -> np.ndarray:\n        return self._bounds[key]\n\n    def __len__(self):\n        return 2\n\n    def __setitem__(self, key, val):\n        self._bounds[key] = val\n\n    def __iand__(self, other: BoundingBox) -> BoundingBox:\n        \"\"\"Updates the box be the intersection of itself and another box\n\n        Parameters\n        ----------\n        other : BoundingBox\n            The box used to resize this box\n\n        Returns\n        -------\n        An updated bounding box\n        \"\"\"\n        self.lower_left = np.maximum(self.lower_left, other.lower_left)\n        self.upper_right = np.minimum(self.upper_right, other.upper_right)\n        return self\n\n    def __and__(self, other: BoundingBox) -> BoundingBox:\n        new = BoundingBox(*self)\n        new &= other\n        return new\n\n    def __ior__(self, other: BoundingBox) -> BoundingBox:\n        \"\"\"Updates the box be the union of itself and another box\n\n        Parameters\n        ----------\n        other : BoundingBox\n            The box used to resize this box\n\n        Returns\n        -------\n        An updated bounding box\n        \"\"\"\n        self.lower_left = np.minimum(self.lower_left, other.lower_left)\n        self.upper_right = np.maximum(self.upper_right, other.upper_right)\n        return self\n\n    def __or__(self, other: BoundingBox) -> BoundingBox:\n        new = BoundingBox(*self)\n        new |= other\n        return new\n\n    def __contains__(self, other):\n        \"\"\"Check whether or not a point or another bounding box is in the bounding box.\n\n        For another bounding box to be in the parent it must lie fully inside of it.\n        \"\"\"\n        # test for a single point\n        if isinstance(other, (tuple, list, np.ndarray)):\n            point = other\n            check_length(\"Point\", point, 3, 3)\n            return all(point > self.lower_left) and all(point < self.upper_right)\n        elif isinstance(other, BoundingBox):\n            return all([p in self for p in [other.lower_left, other.upper_right]])\n        else:\n            raise TypeError(\n                f\"Unable to determine if {other} is in the bounding box.\"\n                f\" Expected a tuple or a bounding box, but {type(other)} given\"\n            )\n\n    @property\n    def center(self) -> np.ndarray:\n        return (self[0] + self[1]) / 2\n\n    @property\n    def lower_left(self) -> np.ndarray:\n        return self[0]\n\n    @lower_left.setter\n    def lower_left(self, llc):\n        check_length('lower_left', llc, 3, 3)\n        self[0] = llc\n\n    @property\n    def upper_right(self) -> np.ndarray:\n        return self[1]\n\n    @upper_right.setter\n    def upper_right(self, urc):\n        check_length('upper_right', urc, 3, 3)\n        self[1] = urc\n\n    @property\n    def volume(self) -> float:\n        return np.abs(np.prod(self[1] - self[0]))\n\n    @property\n    def extent(self):\n        return {\n            \"xy\": (\n                self.lower_left[0],\n                self.upper_right[0],\n                self.lower_left[1],\n                self.upper_right[1],\n            ),\n            \"xz\": (\n                self.lower_left[0],\n                self.upper_right[0],\n                self.lower_left[2],\n                self.upper_right[2],\n            ),\n            \"yz\": (\n                self.lower_left[1],\n                self.upper_right[1],\n                self.lower_left[2],\n                self.upper_right[2],\n            ),\n        }\n\n    @property\n    def width(self):\n        return self.upper_right - self.lower_left\n\n    def expand(self, padding_distance: float, inplace: bool = False) -> BoundingBox:\n        \"\"\"Returns an expanded bounding box\n\n        Parameters\n        ----------\n        padding_distance : float\n            The distance to enlarge the bounding box by\n        inplace : bool\n            Whether or not to return a new BoundingBox instance or to modify the\n            current BoundingBox object.\n\n        Returns\n        -------\n        An expanded bounding box\n        \"\"\"\n        if inplace:\n            self[0] -= padding_distance\n            self[1] += padding_distance\n            return self\n        else:\n            return BoundingBox(self[0] - padding_distance, self[1] + padding_distance)\n\n    @classmethod\n    def infinite(cls) -> BoundingBox:\n        \"\"\"Create an infinite box. Useful as a starting point for determining\n           geometry bounds.\n\n        Returns\n        -------\n        An infinitely large bounding box.\n        \"\"\"\n        infs = np.full((3,), np.inf)\n        return cls(-infs, infs)\n", "path": "openmc/bounding_box.py"}]}