Repository: openmc-dev/openmc

Issue:
Nuclides with partial S(alpha, beta) tables
Some reactor materials have isotopes which appear in a mixture of different molecular structures.  For example, the carbon in TREAT fuel assemblies is usually treated as a mixture of graphite and amorphus carbon.  In these cases, we need to apply S(alpha, beta) tables to only a fraction of the atoms in that material.

I've made a [branch](https://github.com/smharper/openmc/tree/partial_sab) that I believe implements all the necessary code.  I still need to update docs/relaxNG/tests, but that might take a while as I'll be out of town for a bit.  @tjlaboss, can you test this branch and make sure it gives the expected results for your problem?  To use it, set the optional `fraction` argument when calling `material.add_s_alpha_beta`.


Discussion:

Resolving patch:
diff --git a/openmc/material.py b/openmc/material.py
--- a/openmc/material.py
+++ b/openmc/material.py
@@ -618,13 +618,18 @@ def remove_element(self, element):
             if element == elm[0]:
                 self._elements.remove(elm)
 
-    def add_s_alpha_beta(self, name):
+    def add_s_alpha_beta(self, name, fraction=1.0):
         r"""Add an :math:`S(\alpha,\beta)` table to the material
 
         Parameters
         ----------
         name : str
             Name of the :math:`S(\alpha,\beta)` table
+        fraction : float
+            The fraction of relevant nuclei that are affected by the
+            :math:`S(\alpha,\beta)` table.  For example, if the material is a
+            block of carbon that is 60% graphite and 40% amorphous then add a
+            graphite :math:`S(\alpha,\beta)` table with fraction=0.6.
 
         """
 
@@ -638,13 +643,17 @@ def add_s_alpha_beta(self, name):
                         'non-string table name "{}"'.format(self._id, name)
             raise ValueError(msg)
 
+        cv.check_type('S(a,b) fraction', fraction, Real)
+        cv.check_greater_than('S(a,b) fraction', fraction, 0.0, True)
+        cv.check_less_than('S(a,b) fraction', fraction, 1.0, True)
+
         new_name = openmc.data.get_thermal_name(name)
         if new_name != name:
             msg = 'OpenMC S(a,b) tables follow the GND naming convention. ' \
                   'Table "{}" is being renamed as "{}".'.format(name, new_name)
             warnings.warn(msg)
 
-        self._sab.append(new_name)
+        self._sab.append((new_name, fraction))
 
     def make_isotropic_in_lab(self):
         for nuclide, percent, percent_type in self._nuclides:
@@ -972,7 +981,9 @@ def to_xml_element(self, cross_sections=None):
         if len(self._sab) > 0:
             for sab in self._sab:
                 subelement = ET.SubElement(element, "sab")
-                subelement.set("name", sab)
+                subelement.set("name", sab[0])
+                if sab[1] != 1.0:
+                    subelement.set("fraction", str(sab[1]))
 
         return element
 


Test patch:
diff --git a/tests/test_salphabeta/geometry.xml b/tests/test_salphabeta/geometry.xml
deleted file mode 100644
--- a/tests/test_salphabeta/geometry.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0"?>
-<geometry>
-
-  <surface id="1" type="x-plane" coeffs="-10" boundary="vacuum"/>
-  <surface id="2" type="x-plane" coeffs="-5" />
-  <surface id="3" type="x-plane" coeffs="0" />
-  <surface id="4" type="x-plane" coeffs="5" />
-  <surface id="5" type="x-plane" coeffs="10" boundary="vacuum"/>
-
-  <cell id="1" material="1" region="1 -2" />
-  <cell id="2" material="2" region="2 -3" />
-  <cell id="3" material="3" region="3 -4" />
-  <cell id="4" material="4" region="4 -5" />
-
-</geometry>
diff --git a/tests/test_salphabeta/inputs_true.dat b/tests/test_salphabeta/inputs_true.dat
new file mode 100644
--- /dev/null
+++ b/tests/test_salphabeta/inputs_true.dat
@@ -0,0 +1,60 @@
+<?xml version='1.0' encoding='utf-8'?>
+<geometry>
+  <cell id="1" material="1" region="1 -2" universe="1" />
+  <cell id="2" material="2" region="2 -3" universe="1" />
+  <cell id="3" material="3" region="3 -4" universe="1" />
+  <cell id="4" material="4" region="4 -5" universe="1" />
+  <surface boundary="vacuum" coeffs="-10" id="1" type="x-plane" />
+  <surface coeffs="-5" id="2" type="x-plane" />
+  <surface coeffs="0" id="3" type="x-plane" />
+  <surface coeffs="5" id="4" type="x-plane" />
+  <surface boundary="vacuum" coeffs="10" id="5" type="x-plane" />
+</geometry>
+<?xml version='1.0' encoding='utf-8'?>
+<materials>
+  <material id="1">
+    <density units="g/cc" value="4.5" />
+    <nuclide ao="1.0" name="U235" />
+    <nuclide ao="1.0" name="H1" />
+    <sab fraction="0.5" name="c_H_in_H2O" />
+  </material>
+  <material id="2">
+    <density units="g/cc" value="4.5" />
+    <nuclide ao="1.0" name="U235" />
+    <nuclide ao="1.0" name="C0" />
+    <sab name="c_Graphite" />
+  </material>
+  <material id="3">
+    <density units="g/cc" value="4.5" />
+    <nuclide ao="1.0" name="U235" />
+    <nuclide ao="1.0" name="Be9" />
+    <nuclide ao="1.0" name="O16" />
+    <sab name="c_Be_in_BeO" />
+    <sab name="c_O_in_BeO" />
+  </material>
+  <material id="4">
+    <density units="g/cm3" value="5.90168" />
+    <nuclide ao="0.3" name="H1" />
+    <nuclide ao="0.15" name="Zr90" />
+    <nuclide ao="0.1" name="Zr91" />
+    <nuclide ao="0.1" name="Zr92" />
+    <nuclide ao="0.05" name="Zr94" />
+    <nuclide ao="0.05" name="Zr96" />
+    <nuclide ao="0.1" name="U235" />
+    <nuclide ao="0.15" name="U238" />
+    <sab name="c_Zr_in_ZrH" />
+    <sab name="c_H_in_ZrH" />
+  </material>
+</materials>
+<?xml version='1.0' encoding='utf-8'?>
+<settings>
+  <run_mode>eigenvalue</run_mode>
+  <particles>1000</particles>
+  <batches>5</batches>
+  <inactive>0</inactive>
+  <source strength="1.0">
+    <space type="box">
+      <parameters>-4 -4 -4 4 4 4</parameters>
+    </space>
+  </source>
+</settings>
diff --git a/tests/test_salphabeta/materials.xml b/tests/test_salphabeta/materials.xml
deleted file mode 100644
--- a/tests/test_salphabeta/materials.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0"?>
-<materials>
-
-  <material id="1">
-    <density value="4.5" units="g/cc" />
-    <nuclide name="U235" ao="1.0" />
-    <nuclide name="H1" ao="1.0" />
-    <sab name="c_H_in_H2O" />
-  </material>
-
-  <material id="2">
-    <density value="4.5" units="g/cc" />
-    <nuclide name="U235" ao="1.0" />
-    <nuclide name="C0" ao="1.0" />
-    <sab name="c_Graphite" />
-  </material>
-
-  <material id="3">
-    <density value="4.5" units="g/cc" />
-    <nuclide name="U235" ao="1.0" />
-    <nuclide name="Be9" ao="1.0" />
-    <nuclide name="O16" ao="1.0" />
-    <sab name="c_Be_in_BeO" />
-    <sab name="c_O_in_BeO" />
-  </material>
-
-  <material id="4">
-    <density value="5.90168" units="g/cm3" />
-    <nuclide name="H1"    ao="0.3" />
-    <nuclide name="Zr90"  ao="0.15" />
-    <nuclide name="Zr91"  ao="0.1" />
-    <nuclide name="Zr92"  ao="0.1" />
-    <nuclide name="Zr94"  ao="0.05" />
-    <nuclide name="Zr96"  ao="0.05" />
-    <nuclide name="U235"  ao="0.1" />
-    <nuclide name="U238"  ao="0.15" />
-    <sab name="c_Zr_in_ZrH" />
-    <sab name="c_H_in_ZrH" />
-  </material>
-
-</materials>
diff --git a/tests/test_salphabeta/results_true.dat b/tests/test_salphabeta/results_true.dat
--- a/tests/test_salphabeta/results_true.dat
+++ b/tests/test_salphabeta/results_true.dat
@@ -1,2 +1,2 @@
 k-combined:
-8.544160E-01 1.274133E-02
+8.447580E-01 1.806149E-02
diff --git a/tests/test_salphabeta/settings.xml b/tests/test_salphabeta/settings.xml
deleted file mode 100644
--- a/tests/test_salphabeta/settings.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0"?>
-<settings>
-
-  <run_mode>eigenvalue</run_mode>
-  <batches>10</batches>
-  <inactive>5</inactive>
-  <particles>1000</particles>
-
-  <source>
-    <space type="box">
-      <parameters>-4 -4 -4  4  4  4</parameters>
-    </space>
-  </source>
-
-</settings>
diff --git a/tests/test_salphabeta/test_salphabeta.py b/tests/test_salphabeta/test_salphabeta.py
--- a/tests/test_salphabeta/test_salphabeta.py
+++ b/tests/test_salphabeta/test_salphabeta.py
@@ -3,9 +3,82 @@
 import os
 import sys
 sys.path.insert(0, os.pardir)
-from testing_harness import TestHarness
+
+from testing_harness import PyAPITestHarness
+import openmc
+import openmc.model
+
+
+def make_model():
+    model = openmc.model.Model()
+
+    # Materials
+    m1 = openmc.Material()
+    m1.set_density('g/cc', 4.5)
+    m1.add_nuclide('U235', 1.0)
+    m1.add_nuclide('H1', 1.0)
+    m1.add_s_alpha_beta('c_H_in_H2O', fraction=0.5)
+
+    m2 = openmc.Material()
+    m2.set_density('g/cc', 4.5)
+    m2.add_nuclide('U235', 1.0)
+    m2.add_nuclide('C0', 1.0)
+    m2.add_s_alpha_beta('c_Graphite')
+
+    m3 = openmc.Material()
+    m3.set_density('g/cc', 4.5)
+    m3.add_nuclide('U235', 1.0)
+    m3.add_nuclide('Be9', 1.0)
+    m3.add_nuclide('O16', 1.0)
+    m3.add_s_alpha_beta('c_Be_in_BeO')
+    m3.add_s_alpha_beta('c_O_in_BeO')
+
+    m4 = openmc.Material()
+    m4.set_density('g/cm3', 5.90168)
+    m4.add_nuclide('H1', 0.3)
+    m4.add_nuclide('Zr90', 0.15)
+    m4.add_nuclide('Zr91', 0.1)
+    m4.add_nuclide('Zr92', 0.1)
+    m4.add_nuclide('Zr94', 0.05)
+    m4.add_nuclide('Zr96', 0.05)
+    m4.add_nuclide('U235', 0.1)
+    m4.add_nuclide('U238', 0.15)
+    m4.add_s_alpha_beta('c_Zr_in_ZrH')
+    m4.add_s_alpha_beta('c_H_in_ZrH')
+
+    model.materials += [m1, m2, m3, m4]
+
+    # Geometry
+    x0 = openmc.XPlane(x0=-10, boundary_type='vacuum')
+    x1 = openmc.XPlane(x0=-5)
+    x2 = openmc.XPlane(x0=0)
+    x3 = openmc.XPlane(x0=5)
+    x4 = openmc.XPlane(x0=10, boundary_type='vacuum')
+
+    root_univ = openmc.Universe()
+
+    surfs = (x0, x1, x2, x3, x4)
+    mats = (m1, m2, m3, m4)
+    cells = []
+    for i in range(4):
+        cell = openmc.Cell()
+        cell.region = +surfs[i] & -surfs[i+1]
+        cell.fill = mats[i]
+        root_univ.add_cell(cell)
+
+    model.geometry.root_universe = root_univ
+
+    # Settings
+    model.settings.batches = 5
+    model.settings.inactive = 0
+    model.settings.particles = 1000
+    model.settings.source = openmc.Source(space=openmc.stats.Box(
+        [-4, -4, -4], [4, 4, 4]))
+
+    return model
 
 
 if __name__ == '__main__':
-    harness = TestHarness('statepoint.10.h5')
+    model = make_model()
+    harness = PyAPITestHarness('statepoint.5.h5', model)
     harness.main()