Repository: openmc-dev/openmc

Issue:
Dimensional inconsistency for SLBW competitive width terms

The neutron wave number factor seems to be missing in the channel radius ratio (`rhoc`) and scattering radius ratio (`rhochat`) terms for the competitive width branch of `reconstruct_slbw`. See lines 302 and 303 of [openmc/data/reconstruct.pyx](https://github.com/mit-crpg/openmc/blob/1008ab8ea1509e17151a96b535c57b4f64831831/openmc/data/reconstruct.pyx#L302)


Discussion:
Nice find @apmccartney. Do you know of any evaluations that have non-zero competitive widths for SLBW/MLBW?
Not yet, but I'm planning to build up a test suite as part the resonance reconstruction effort. Assuming I can find such an evaluation, it will be available there. I'll ping you if I come across one before then.
Yeah, I hadn't run into one either which is probably part of the reason this slipped through. I'd welcome a PR if you want "credit" for finding the bug. If you have better things to do though, I can put in a fix :)
I'll post a fix sometime this weekend =)
math for wavenumber in reconstruct.pyx seems to be wrong. Code looks ok though

Resolving patch:
diff --git a/openmc/region.py b/openmc/region.py
--- a/openmc/region.py
+++ b/openmc/region.py
@@ -259,13 +259,16 @@ def clone(self, memo=None):
         clone[:] = [n.clone(memo) for n in self]
         return clone
 
-    def translate(self, vector, memo=None):
+    def translate(self, vector, inplace=False, memo=None):
         """Translate region in given direction
 
         Parameters
         ----------
         vector : iterable of float
             Direction in which region should be translated
+        inplace : bool
+            Whether or not to return a region based on new surfaces or one based
+            on the original surfaces that have been modified.
         memo : dict or None
             Dictionary used for memoization. This parameter is used internally
             and should not be specified by the user.
@@ -279,7 +282,7 @@ def translate(self, vector, memo=None):
 
         if memo is None:
             memo = {}
-        return type(self)(n.translate(vector, memo) for n in self)
+        return type(self)(n.translate(vector, inplace, memo) for n in self)
 
     def rotate(self, rotation, pivot=(0., 0., 0.), order='xyz', inplace=False,
                memo=None):
@@ -308,7 +311,7 @@ def rotate(self, rotation, pivot=(0., 0., 0.), order='xyz', inplace=False,
             :math:`\psi` about z. This corresponds to an x-y-z extrinsic
             rotation as well as a z-y'-x'' intrinsic rotation using Tait-Bryan
             angles :math:`(\phi, \theta, \psi)`.
-        inplace : boolean
+        inplace : bool
             Whether or not to return a new instance of Surface or to modify the
             coefficients of this Surface in place. Defaults to False.
         memo : dict or None
@@ -622,10 +625,10 @@ def clone(self, memo=None):
         clone.node = self.node.clone(memo)
         return clone
 
-    def translate(self, vector, memo=None):
+    def translate(self, vector, inplace=False, memo=None):
         if memo is None:
             memo = {}
-        return type(self)(self.node.translate(vector, memo))
+        return type(self)(self.node.translate(vector, inplace, memo))
 
     def rotate(self, rotation, pivot=(0., 0., 0.), order='xyz', inplace=False,
                memo=None):
diff --git a/openmc/surface.py b/openmc/surface.py
--- a/openmc/surface.py
+++ b/openmc/surface.py
@@ -336,9 +336,9 @@ def translate(self, vector, inplace=False):
         ----------
         vector : iterable of float
             Direction in which surface should be translated
-        inplace : boolean
+        inplace : bool
             Whether or not to return a new instance of this Surface or to
-            modify the coefficients of this Surface. Defaults to False
+            modify the coefficients of this Surface.
 
         Returns
         -------
@@ -374,7 +374,7 @@ def rotate(self, rotation, pivot=(0., 0., 0.), order='xyz', inplace=False):
             :math:`\psi` about z. This corresponds to an x-y-z extrinsic
             rotation as well as a z-y'-x'' intrinsic rotation using Tait-Bryan
             angles :math:`(\phi, \theta, \psi)`.
-        inplace : boolean
+        inplace : bool
             Whether or not to return a new instance of Surface or to modify the
             coefficients of this Surface in place. Defaults to False.
 
@@ -568,9 +568,9 @@ def translate(self, vector, inplace=False):
         ----------
         vector : iterable of float
             Direction in which surface should be translated
-        inplace : boolean
+        inplace : bool
             Whether or not to return a new instance of a Plane or to modify the
-            coefficients of this plane. Defaults to False
+            coefficients of this plane.
 
         Returns
         -------
@@ -1012,9 +1012,8 @@ def translate(self, vector, inplace=False):
         ----------
         vector : iterable of float
             Direction in which surface should be translated
-        inplace : boolean
+        inplace : bool
             Whether to return a clone of the Surface or the Surface itself.
-            Defaults to False
 
         Returns
         -------
@@ -2563,7 +2562,7 @@ def clone(self, memo=None):
         clone.surface = self.surface.clone(memo)
         return clone
 
-    def translate(self, vector, memo=None):
+    def translate(self, vector, inplace=False, memo=None):
         """Translate half-space in given direction
 
         Parameters
@@ -2585,7 +2584,7 @@ def translate(self, vector, memo=None):
         # If translated surface not in memo, add it
         key = (self.surface, tuple(vector))
         if key not in memo:
-            memo[key] = self.surface.translate(vector)
+            memo[key] = self.surface.translate(vector, inplace)
 
         # Return translated half-space
         return type(self)(memo[key], self.side)
@@ -2617,7 +2616,7 @@ def rotate(self, rotation, pivot=(0., 0., 0.), order='xyz', inplace=False,
             :math:`\psi` about z. This corresponds to an x-y-z extrinsic
             rotation as well as a z-y'-x'' intrinsic rotation using Tait-Bryan
             angles :math:`(\phi, \theta, \psi)`.
-        inplace : boolean
+        inplace : bool
             Whether or not to return a new instance of Surface or to modify the
             coefficients of this Surface in place. Defaults to False.
         memo : dict or None


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
@@ -208,3 +208,17 @@ def test_from_expression(reset):
     # Opening parenthesis immediately after halfspace
     r = openmc.Region.from_expression('1(2|-3)', surfs)
     assert str(r) == '(1 (2 | -3))'
+
+
+def test_translate_inplace():
+    sph = openmc.Sphere()
+    x = openmc.XPlane()
+    region = -sph & +x
+
+    # Translating a region should produce new surfaces
+    region2 = region.translate((0.5, -6.7, 3.9), inplace=False)
+    assert str(region) != str(region2)
+
+    # 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)