diff --git a/docs/source/_templates/myclassinherit.rst b/docs/source/_templates/myclassinherit.rst new file mode 100644 index 0000000000..ed93a29669 --- /dev/null +++ b/docs/source/_templates/myclassinherit.rst @@ -0,0 +1,8 @@ +{{ fullname }} +{{ underline }} + +.. currentmodule:: {{ module }} + +.. autoclass:: {{ objname }} + :members: + :inherited-members: diff --git a/docs/source/io_formats/mgxs_library.rst b/docs/source/io_formats/mgxs_library.rst index 35addf15e9..52b67b7f2c 100644 --- a/docs/source/io_formats/mgxs_library.rst +++ b/docs/source/io_formats/mgxs_library.rst @@ -172,7 +172,7 @@ attributes/sub-elements required to describe the meta-data: during the scattering process. Specifically, the options are to either convert the Legendre expansion to a tabular representation or leave it as a set of Legendre coefficients. Converting to a tabular representation will - cost memory but is likely to decrease runtime compared to leaving as a + cost memory but can allow for a decrease in runtime compared to leaving as a set of Legendre coefficients. This element has the following attributes/sub-elements: @@ -181,7 +181,7 @@ attributes/sub-elements required to describe the meta-data: tabular format should be performed or not. A value of "true" means the conversion should be performed, "false" means it should not. - *Default*: "true" + *Default*: "false" :num_points: If the conversion is to take place the number of tabular points is diff --git a/docs/source/methods/geometry.rst b/docs/source/methods/geometry.rst index f642cca108..bcd568ca3a 100644 --- a/docs/source/methods/geometry.rst +++ b/docs/source/methods/geometry.rst @@ -437,6 +437,8 @@ where :math:`(x_0, y_0, z_0)` are the coordinates to the lower-left-bottom corner of the lattice, and :math:`p_0, p_1, p_2` are the pitches along the :math:`x`, :math:`y`, and :math:`z` axes, respectively. +.. _hexagonal_indexing: + Hexagonal Lattice Indexing -------------------------- diff --git a/docs/source/pythonapi/examples/mgxs-part-iii.ipynb b/docs/source/pythonapi/examples/mgxs-part-iii.ipynb index bc2f96414a..5f0acde3f1 100644 --- a/docs/source/pythonapi/examples/mgxs-part-iii.ipynb +++ b/docs/source/pythonapi/examples/mgxs-part-iii.ipynb @@ -260,7 +260,6 @@ "source": [ "# Create fuel assembly Lattice\n", "assembly = openmc.RectLattice(name='1.6% Fuel Assembly')\n", - "assembly.dimension = (17, 17)\n", "assembly.pitch = (1.26, 1.26)\n", "assembly.lower_left = [-1.26 * 17. / 2.0] * 2" ] @@ -939,7 +938,8 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The `NuFissionXS` object supports all of the methods described previously the `openmc.mgxs` tutorials, such as [Pandas](http://pandas.pydata.org/) `DataFrames`:" + "The `NuFissionXS` object supports all of the methods described previously in the `openmc.mgxs` tutorials, such as [Pandas](http://pandas.pydata.org/) `DataFrames`:\n", + "Note that since so few histories were simulated, we should expect a few division-by-error errors as some tallies have not yet scored any results." ] }, { @@ -1596,21 +1596,21 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 2", + "display_name": "Python 3", "language": "python", - "name": "python2" + "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", - "version": 2 + "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython2", - "version": "2.7.11" + "pygments_lexer": "ipython3", + "version": "3.5.1" } }, "nbformat": 4, diff --git a/docs/source/pythonapi/examples/mgxs-part-iv.ipynb b/docs/source/pythonapi/examples/mgxs-part-iv.ipynb index 4b73cf3caa..b845636e21 100644 --- a/docs/source/pythonapi/examples/mgxs-part-iv.ipynb +++ b/docs/source/pythonapi/examples/mgxs-part-iv.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "This Notebook illustrates the use of the openmc.mgxs.Library class specifically for application in OpenMC's multi-group mode. This example notebook follows the same process as was done in MGXS Part III, but instead uses OpenMC as the multi-group solver. This Notebook illustrates the following features:\n", + "This Notebook illustrates the use of the openmc.mgxs.Library class specifically for application in OpenMC's multi-group mode. This example notebook follows the same process as was done in MGXS Part III, but instead uses OpenMC as the multi-group solver. During this process, this notebook will illustrate the following features:\n", "\n", " - Calculation of multi-group cross sections for a fuel assembly\n", " - Automated creation and storage of MGXS with openmc.mgxs.Library\n", @@ -244,7 +244,6 @@ "source": [ "# Create fuel assembly Lattice\n", "assembly = openmc.RectLattice(name='1.6% Fuel Assembly')\n", - "assembly.dimension = (17, 17)\n", "assembly.pitch = (1.26, 1.26)\n", "assembly.lower_left = [-1.26 * 17. / 2.0] * 2" ] @@ -334,7 +333,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "With the geometry and materials finished, we now just need to define simulation parameters. In this case, we will use 10 inactive batches and 40 active batches each with 2500 particles." + "With the geometry and materials finished, we now just need to define simulation parameters. In this case, we will use 10 inactive batches and 40 active batches each with 5000 particles." ] }, { @@ -433,7 +432,7 @@ "outputs": [ { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPoAAAD6AgMAAAD1grKuAAAABGdBTUEAALGPC/xhBQAAACBjSFJN\nAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAADFBMVEX////pgJFyEhJNv8RV\nUZDeAAAAAWJLR0QAiAUdSAAAAAd0SU1FB+AFERUOBQ7RtjIAAAWFSURBVGje7Zs7cttADIZ9CSvX\ncrP0iCxUqbBc8Ag6xR6BhV2EvYvwFD4CCx1ABT1jMdgndpegRQnOrCbjpPlGESISC4A/gd27e8H5\n83CX3b4+iKJrRHkS4vkghMPBonRYWGwtfgD2YN+dRDUOoh6lACw0Noi9w2fESuEoAR/uVuMolX03\n9oXGT7F3eFL2iEfhUX1f4cPdL/ishs+68ai+udE4xPhexbjX2FfjGNoPj/DPNX4Tsd+EODr8FvsV\ndf1Hd9P2VvCi4+s/aXvrf+upAD+1/9GV1mkOH5X9vV6THtfvACslcaUCbESL61drBPtdI8SrFMWr\nELsXCkuFDYW75gbiP7d9Cf7bAYI/aCwUShrBvh30+lWQkzVgZ/HD4OixNCgcQpJ3BxU/Ln91elKo\nM5VEE38QtJ+Yv6cQ9xjKNYayyl8TypP8DfJnQ2H/b/N3ye9P83cT33SQv/sQh9gV7zZ/0dNj5HQa\nC5vVzv9+/WFN2w8KVaZ2BwL1+pv4g0x1QRfjq0dB4Q3kT277oP6VNL6gKxNU9a8zK+WLbi/Wwpdi\nhbboKqyxFOulHMj6v4W/AXbmUeAxrv9J/CqEBXaRKsXaodD4nsYvkT/G6H1D4SR/iPy1Roj9JsQ5\ne18/7EUHv1+Fvx/Xj5V9Ugb5K8TW4TZEEdcvoz/up0VTe9qsVIppKVX6a7D6y9ZvwEKjrtQxPtv6\nfXII9vCxKOGaIeAIfEF8IvAG8ie3vRK9rRQl+PPpSctbhfpTUCpviH+kxsZgpT91+snoX1l49KK3\niUQvICRy5aUw6l8leoVwoo3Uv1rKreF/UFLY6d9QP4L9Wf2r7EP9GOSfcsjZ56f60kz+XmVPXv+R\nuP49ff0T/53Rv6n/7m2lvXT9Wqd/VUz8hvh5M/ED6ILmt4mfHYZSaePnTWpsf/SvqV9O6dLYYClL\nEetnoH/LBLFoBvrX189uTv8++kot5vTvQD4/9jP690g9P/4z/bvo/XVG/xYoZZx+8fr3MxAtsf7t\nUOkG2JqsTtCIpgCt/qX1226KqZS7gfzJbe+c9jLrtIZ8lXD+s4umlW6AKIVrlML2/cXjgPFjlJqI\nRC+Fj0bVJe+vSh56pSdR6YkQ1ygF10Wqf0FeLta/iKn9Mv1L24ti2e+7W4n1b3T/W+L+t9H9T/Sv\nVboUmqJJon1/hZq8LnzRDlDrX1u0xRT1+6vEpomMmyYkqi95vIH8yW1PN+122KkLcNLKi/WTF01z\n/cNASrWE/l3ev6T17zX909z9X27/euK/Rf3zWP+Waf9eEv37KkWJ+rfDl6ZglNDa+cEBhwYDvkoN\nP/rX69814NaI3imq0l7OYDy/qSdDGwr7r+Y3VbzoKZr6XX2lfxfOb87qXzr+b1j/Xlp/nP6dn98M\ncdH7cn7zjPObKsYWS3Eb9w8n85smHtqQuPuZ30T2dlIT6F9xFl+n8xslegL9a4c2KRr9W4rp/GYq\numiM9Nec/j2v/yj9u1h//hv9e93vc++f63/u+rPjL3f+5Lbn1j9m/eXWf+7zh/v8+2b9e/Hzn6s/\nuPqHrb8g71n6L3f+5Lbnvn8w33+4718/+5d47//c/gO7/5E7/nPbc/tv3P4fs//I7X9y+6/fqH+v\n6j9z+9/c/ju3/8+eP+TOn9z23PkXc/7Gnf9x5483q38Xzn+582fu/Js9fy8kb/6fO39y23P3n3S8\n/S/c/Tfc/T83uX/pgv1XE/9duP+Lu/+Mvf8td/znti8kb/8ld/9nx9t/Sjw/Ltr/yt1/+337f6/b\nf0zoB3nJ/ucVc/81d/83e/957vzJbc89/8A8f8E9/5HE78XnT/4H/cs5f8Q9/8Q9f8U+/5U7f3Lb\nc88fdrzzjyvm+cuf/Uu887/c88fs88954/8vO4SjPC+2QRIAAAAldEVYdGRhdGU6Y3JlYXRlADIw\nMTYtMDUtMTdUMjE6MTQ6MDUtMDQ6MDCzw4K8AAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE2LTA1LTE3\nVDIxOjE0OjA1LTA0OjAwwp46AAAAAABJRU5ErkJggg==\n", + "image/png": "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\n", "text/plain": [ "" ] @@ -499,8 +498,8 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Now, we must specify to the Library which types of cross sections to compute. OpenMC's multi-group mode can accept isotropic flux-weighted cross sections or angle-dependent cross sections, as well as supporting anisotropic scattering represented by either Legendre polynomials, histogram, or tabular angular distributions. At this time the MGXS Library class only supports the generation of isotropic flux-weighted cross sections and P0 scattering, so that is what will be used for this example. Therefore, we will create the following multi-group cross sections needed to run an OpenMC simulation to verify the accuracy of our cross sections: \"transport\", \"absorption\", \"nu-fission\", '\"fission\", \"nu-scatter matrix\", \"scatter matrix\", and \"chi\".\n", - "\"scatter matrix\" is needed in addition to \"nu-scatter matrix\" because OpenMC's multi-group mode can treat scattering multiplication (i.e., (n,xn) reactions)) explicitly instead of adjusting the absorption cross section to maintain neutron balance, and using this explicit treatment would require tallying of both types of scattering matrices." + "Now, we must specify to the Library which types of cross sections to compute. OpenMC's multi-group mode can accept isotropic flux-weighted cross sections or angle-dependent cross sections, as well as supporting anisotropic scattering represented by either Legendre polynomials, histogram, or tabular angular distributions. At this time the MGXS Library class only supports the generation of isotropic flux-weighted cross sections and P0 scattering, so that is what will be used for this example. Therefore, we will create the following multi-group cross sections needed to run an OpenMC simulation to verify the accuracy of our cross sections: \"total\", \"absorption\", \"nu-fission\", '\"fission\", \"nu-scatter matrix\", \"multiplicity matrix\", and \"chi\".\n", + "\"multiplicity matrix\" is needed to provide OpenMC's multi-group mode with additional information needed to accurately treat scattering multiplication (i.e., (n,xn) reactions)) explicitly." ] }, { @@ -513,7 +512,7 @@ "source": [ "# Specify multi-group cross section types to compute\n", "mgxs_lib.mgxs_types = ['total', 'absorption', 'nu-fission', 'fission',\n", - " 'nu-scatter matrix', 'scatter matrix', 'chi']" + " 'nu-scatter matrix', 'multiplicity matrix', 'chi']" ] }, { @@ -565,7 +564,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Now we will set the scattering order that we wish to use. For this problem we will use P3 scattering." + "Now we will set the scattering order that we wish to use. For this problem we will use P3 scattering. A warning is expected telling us that the default behavior (a P0 correction on the scattering data) is over-ridden by our choice of using a Legendre expansion to treat anisotropic scattering." ] }, { @@ -681,24 +680,24 @@ "tally.scores = ['fission']\n", "\n", "# Add tally to collection\n", - "tallies_file.append(tally)" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ + "tallies_file.append(tally, merge=True)\n", + "\n", "# Export all tallies to a \"tallies.xml\" file\n", "tallies_file.export_to_xml()" ] }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": true + }, + "source": [ + "Time to run the calculation and get our results!" + ] + }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 26, "metadata": { "collapsed": false }, @@ -723,8 +722,8 @@ " Copyright: 2011-2016 Massachusetts Institute of Technology\n", " License: http://openmc.readthedocs.io/en/latest/license.html\n", " Version: 0.7.1\n", - " Git SHA1: 058ba68895a2f880402fda3d58cfb14b162931d9\n", - " Date/Time: 2016-05-17 21:14:05\n", + " Git SHA1: d20322f22d4850bd640b4accf34e2551550d17fb\n", + " Date/Time: 2016-05-30 14:20:53\n", " OpenMP Threads: 4\n", "\n", " ===========================================================================\n", @@ -811,20 +810,20 @@ "\n", " =======================> TIMING STATISTICS <=======================\n", "\n", - " Total time for initialization = 1.4530E+00 seconds\n", - " Reading cross sections = 1.1470E+00 seconds\n", - " Total time in simulation = 1.8747E+01 seconds\n", - " Time in transport only = 1.8639E+01 seconds\n", - " Time in inactive batches = 2.1690E+00 seconds\n", - " Time in active batches = 1.6578E+01 seconds\n", - " Time synchronizing fission bank = 6.0000E-03 seconds\n", - " Sampling source sites = 4.0000E-03 seconds\n", - " SEND/RECV source sites = 2.0000E-03 seconds\n", + " Total time for initialization = 1.4260E+00 seconds\n", + " Reading cross sections = 1.1340E+00 seconds\n", + " Total time in simulation = 1.6739E+01 seconds\n", + " Time in transport only = 1.6650E+01 seconds\n", + " Time in inactive batches = 2.1540E+00 seconds\n", + " Time in active batches = 1.4585E+01 seconds\n", + " Time synchronizing fission bank = 1.0000E-02 seconds\n", + " Sampling source sites = 9.0000E-03 seconds\n", + " SEND/RECV source sites = 1.0000E-03 seconds\n", " Time accumulating tallies = 0.0000E+00 seconds\n", " Total time for finalization = 0.0000E+00 seconds\n", - " Total time elapsed = 2.0209E+01 seconds\n", - " Calculation Rate (inactive) = 23052.1 neutrons/second\n", - " Calculation Rate (active) = 12064.2 neutrons/second\n", + " Total time elapsed = 1.8174E+01 seconds\n", + " Calculation Rate (inactive) = 23212.6 neutrons/second\n", + " Calculation Rate (active) = 13712.7 neutrons/second\n", "\n", " ============================> RESULTS <============================\n", "\n", @@ -842,7 +841,7 @@ "0" ] }, - "execution_count": 27, + "execution_count": 26, "metadata": {}, "output_type": "execute_result" } @@ -861,7 +860,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 27, "metadata": { "collapsed": false }, @@ -886,7 +885,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 28, "metadata": { "collapsed": false }, @@ -905,7 +904,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 29, "metadata": { "collapsed": false }, @@ -924,7 +923,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 30, "metadata": { "collapsed": false }, @@ -952,12 +951,13 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "We will now use the `Library` to produce a multi-group cross section data set for use by the OpenMC multi-group solver. " + "We will now use the `Library` to produce a multi-group cross section data set for use by the OpenMC multi-group solver. \n", + "Note that since this simulation included so few histories, it is reasonable to expect some divisions by zero errors. This will show up as a runtime warning in the following step." ] }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 31, "metadata": { "collapsed": false }, @@ -966,11 +966,11 @@ "name": "stderr", "output_type": "stream", "text": [ - "/home/nelsonag/git/openmc/openmc/tallies.py:1988: RuntimeWarning: invalid value encountered in true_divide\n", + "/home/nelsonag/git/openmc/openmc/tallies.py:1986: RuntimeWarning: invalid value encountered in true_divide\n", " self_rel_err = data['self']['std. dev.'] / data['self']['mean']\n", - "/home/nelsonag/git/openmc/openmc/tallies.py:1989: RuntimeWarning: invalid value encountered in true_divide\n", + "/home/nelsonag/git/openmc/openmc/tallies.py:1987: RuntimeWarning: invalid value encountered in true_divide\n", " other_rel_err = data['other']['std. dev.'] / data['other']['mean']\n", - "/home/nelsonag/git/openmc/openmc/tallies.py:1990: RuntimeWarning: invalid value encountered in true_divide\n", + "/home/nelsonag/git/openmc/openmc/tallies.py:1988: RuntimeWarning: invalid value encountered in true_divide\n", " new_tally._mean = data['self']['mean'] / data['other']['mean']\n" ] } @@ -997,7 +997,7 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 32, "metadata": { "collapsed": false }, @@ -1047,7 +1047,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 33, "metadata": { "collapsed": true }, @@ -1072,7 +1072,7 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 34, "metadata": { "collapsed": false, "scrolled": true @@ -1098,8 +1098,8 @@ " Copyright: 2011-2016 Massachusetts Institute of Technology\n", " License: http://openmc.readthedocs.io/en/latest/license.html\n", " Version: 0.7.1\n", - " Git SHA1: 058ba68895a2f880402fda3d58cfb14b162931d9\n", - " Date/Time: 2016-05-17 21:14:26\n", + " Git SHA1: d20322f22d4850bd640b4accf34e2551550d17fb\n", + " Date/Time: 2016-05-30 14:21:12\n", " OpenMP Threads: 4\n", "\n", " ===========================================================================\n", @@ -1183,20 +1183,20 @@ "\n", " =======================> TIMING STATISTICS <=======================\n", "\n", - " Total time for initialization = 4.6000E-02 seconds\n", - " Reading cross sections = 8.0000E-03 seconds\n", - " Total time in simulation = 1.4524E+01 seconds\n", - " Time in transport only = 1.4457E+01 seconds\n", - " Time in inactive batches = 1.3350E+00 seconds\n", - " Time in active batches = 1.3189E+01 seconds\n", - " Time synchronizing fission bank = 7.0000E-03 seconds\n", - " Sampling source sites = 5.0000E-03 seconds\n", - " SEND/RECV source sites = 2.0000E-03 seconds\n", + " Total time for initialization = 2.8000E-02 seconds\n", + " Reading cross sections = 4.0000E-03 seconds\n", + " Total time in simulation = 1.2816E+01 seconds\n", + " Time in transport only = 1.2770E+01 seconds\n", + " Time in inactive batches = 1.3130E+00 seconds\n", + " Time in active batches = 1.1503E+01 seconds\n", + " Time synchronizing fission bank = 9.0000E-03 seconds\n", + " Sampling source sites = 3.0000E-03 seconds\n", + " SEND/RECV source sites = 6.0000E-03 seconds\n", " Time accumulating tallies = 0.0000E+00 seconds\n", " Total time for finalization = 0.0000E+00 seconds\n", - " Total time elapsed = 1.4579E+01 seconds\n", - " Calculation Rate (inactive) = 37453.2 neutrons/second\n", - " Calculation Rate (active) = 15164.2 neutrons/second\n", + " Total time elapsed = 1.2854E+01 seconds\n", + " Calculation Rate (inactive) = 38080.7 neutrons/second\n", + " Calculation Rate (active) = 17386.8 neutrons/second\n", "\n", " ============================> RESULTS <============================\n", "\n", @@ -1214,7 +1214,7 @@ "0" ] }, - "execution_count": 35, + "execution_count": 34, "metadata": {}, "output_type": "execute_result" } @@ -1237,7 +1237,7 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 35, "metadata": { "collapsed": false }, @@ -1257,7 +1257,7 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 36, "metadata": { "collapsed": true }, @@ -1275,7 +1275,7 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 37, "metadata": { "collapsed": false }, @@ -1302,7 +1302,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "This shows a nontrivial pcm bias between the two methods. Some degree of mismatch is expected simply to the very few histories being used in these example problems. An additional mismatch is always inherent in the practical application of multi-group theory due to the high degree of approximations inherent in that method." + "This shows a small but nontrivial pcm bias between the two methods. Some degree of mismatch is expected simply to the very few histories being used in these example problems. An additional mismatch is always inherent in the practical application of multi-group theory due to the high degree of approximations inherent in that method." ] }, { @@ -1323,7 +1323,7 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 38, "metadata": { "collapsed": false }, @@ -1349,7 +1349,7 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 39, "metadata": { "collapsed": false }, @@ -1375,7 +1375,7 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 40, "metadata": { "collapsed": false }, @@ -1383,10 +1383,10 @@ { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 41, + "execution_count": 40, "metadata": {}, "output_type": "execute_result" }, @@ -1394,7 +1394,7 @@ "data": { "image/png": "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\n", "text/plain": [ - "" + "" ] }, "metadata": {}, diff --git a/docs/source/pythonapi/examples/pandas-dataframes.ipynb b/docs/source/pythonapi/examples/pandas-dataframes.ipynb index b88cf99498..2c222ad6e0 100644 --- a/docs/source/pythonapi/examples/pandas-dataframes.ipynb +++ b/docs/source/pythonapi/examples/pandas-dataframes.ipynb @@ -199,7 +199,6 @@ "source": [ "# Create fuel assembly Lattice\n", "assembly = openmc.RectLattice(name='1.6% Fuel - 0BA')\n", - "assembly.dimension = (17, 17)\n", "assembly.pitch = (1.26, 1.26)\n", "assembly.lower_left = [-1.26 * 17. / 2.0] * 2\n", "assembly.universes = [[pin_cell_universe] * 17] * 17" @@ -2194,21 +2193,21 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 2", + "display_name": "Python 3", "language": "python", - "name": "python2" + "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", - "version": 2 + "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython2", - "version": "2.7.6" + "pygments_lexer": "ipython3", + "version": "3.5.1" } }, "nbformat": 4, diff --git a/docs/source/pythonapi/index.rst b/docs/source/pythonapi/index.rst index bf35e75874..09b3d5135e 100644 --- a/docs/source/pythonapi/index.rst +++ b/docs/source/pythonapi/index.rst @@ -271,14 +271,17 @@ Multi-group Cross Sections .. autosummary:: :toctree: generated :nosignatures: - :template: myclass.rst + :template: myclassinherit.rst openmc.mgxs.MGXS openmc.mgxs.AbsorptionXS openmc.mgxs.CaptureXS openmc.mgxs.Chi openmc.mgxs.FissionXS + openmc.mgxs.KappaFissionXS + openmc.mgxs.MultiplicityMatrixXS openmc.mgxs.NuFissionXS + openmc.mgxs.NuFissionMatrixXS openmc.mgxs.NuScatterXS openmc.mgxs.NuScatterMatrixXS openmc.mgxs.ScatterXS @@ -296,6 +299,33 @@ Multi-group Cross Section Libraries openmc.mgxs.Library +------------------------------------- +:mod:`openmc.model` -- Model Building +------------------------------------- + +TRISO Fuel Modeling +------------------- + +Classes ++++++++ + +.. autosummary:: + :toctree: generated + :nosignatures: + :template: myclass.rst + + openmc.model.TRISO + +Functions ++++++++++ + +.. autosummary:: + :toctree: generated + :nosignatures: + + openmc.model.create_triso_lattice + + .. _Jupyter: https://jupyter.org/ .. _NumPy: http://www.numpy.org/ .. _Codecademy: https://www.codecademy.com/tracks/python diff --git a/docs/source/usersguide/input.rst b/docs/source/usersguide/input.rst index f6c6869f8e..4c1f238238 100644 --- a/docs/source/usersguide/input.rst +++ b/docs/source/usersguide/input.rst @@ -921,11 +921,19 @@ Each ```` element can have the following attributes or sub-elements: *Default*: None :boundary: - The boundary condition for the surface. This can be "transmission", - "vacuum", or "reflective". + The boundary condition for the surface. This can be "transmission", + "vacuum", "reflective", or "periodic". Periodic boundary conditions can + only be applied to x-, y-, and z-planes. Only axis-aligned periodicity is + supported, i.e., x-planes can only be paired with x-planes. Specify which + planes are periodic and the code will automatically identify which planes + are paired together. *Default*: "transmission" + :periodic_surface_id: + If a periodic boundary condition is applied, this attribute identifies the + ``id`` of the corresponding periodic sufrace. + The following quadratic surfaces can be modeled: :x-plane: @@ -1623,7 +1631,8 @@ The ```` element accepts the following sub-elements: |Score | Description | +======================+===================================================+ |absorption |Total absorption rate. This accounts for all | - | |reactions which do not produce secondary neutrons. | + | |reactions which do not produce secondary neutrons | + | |as well as fission. | +----------------------+---------------------------------------------------+ |elastic |Elastic scattering reaction rate. | +----------------------+---------------------------------------------------+ diff --git a/examples/python/basic/build-xml.py b/examples/python/basic/build-xml.py index ffff037205..81aecc9f99 100644 --- a/examples/python/basic/build-xml.py +++ b/examples/python/basic/build-xml.py @@ -74,8 +74,7 @@ universe1.add_cells([cell2, cell3]) root.add_cells([cell1, cell4]) # Instantiate a Geometry, register the root Universe, and export to XML -geometry = openmc.Geometry() -geometry.root_universe = root +geometry = openmc.Geometry(root) geometry.export_to_xml() diff --git a/examples/python/boxes/build-xml.py b/examples/python/boxes/build-xml.py index 814f60bebd..4be33dcf1f 100644 --- a/examples/python/boxes/build-xml.py +++ b/examples/python/boxes/build-xml.py @@ -97,8 +97,7 @@ root = openmc.Universe(universe_id=0, name='root universe') root.add_cells([inner_box, middle_box, outer_box]) # Instantiate a Geometry, register the root Universe, and export to XML -geometry = openmc.Geometry() -geometry.root_universe = root +geometry = openmc.Geometry(root) geometry.export_to_xml() diff --git a/examples/python/lattice/hexagonal/build-xml.py b/examples/python/lattice/hexagonal/build-xml.py index ef3a128474..05cb2cb010 100644 --- a/examples/python/lattice/hexagonal/build-xml.py +++ b/examples/python/lattice/hexagonal/build-xml.py @@ -105,8 +105,7 @@ lattice.outer = univ2 cell1.fill = lattice # Instantiate a Geometry, register the root Universe, and export to XML -geometry = openmc.Geometry() -geometry.root_universe = root +geometry = openmc.Geometry(root) geometry.export_to_xml() diff --git a/examples/python/lattice/nested/build-xml.py b/examples/python/lattice/nested/build-xml.py index b2d611d345..03cede9dc5 100644 --- a/examples/python/lattice/nested/build-xml.py +++ b/examples/python/lattice/nested/build-xml.py @@ -98,14 +98,12 @@ univ4.add_cell(cell2) # Instantiate nested Lattices lattice1 = openmc.RectLattice(lattice_id=4, name='4x4 assembly') -lattice1.dimension = [2, 2] lattice1.lower_left = [-1., -1.] lattice1.pitch = [1., 1.] lattice1.universes = [[univ1, univ2], [univ2, univ3]] lattice2 = openmc.RectLattice(lattice_id=6, name='4x4 core') -lattice2.dimension = [2, 2] lattice2.lower_left = [-2., -2.] lattice2.pitch = [2., 2.] lattice2.universes = [[univ4, univ4], @@ -116,8 +114,7 @@ cell1.fill = lattice2 cell2.fill = lattice1 # Instantiate a Geometry, register the root Universe, and export to XML -geometry = openmc.Geometry() -geometry.root_universe = root +geometry = openmc.Geometry(root) geometry.export_to_xml() diff --git a/examples/python/lattice/simple/build-xml.py b/examples/python/lattice/simple/build-xml.py index 65c3554798..5a642d3086 100644 --- a/examples/python/lattice/simple/build-xml.py +++ b/examples/python/lattice/simple/build-xml.py @@ -94,7 +94,6 @@ root.add_cell(cell1) # Instantiate a Lattice lattice = openmc.RectLattice(lattice_id=5) -lattice.dimension = [4, 4] lattice.lower_left = [-2., -2.] lattice.pitch = [1., 1.] lattice.universes = [[univ1, univ2, univ1, univ2], @@ -106,8 +105,7 @@ lattice.universes = [[univ1, univ2, univ1, univ2], cell1.fill = lattice # Instantiate a Geometry, register the root Universe, and export to XML -geometry = openmc.Geometry() -geometry.root_universe = root +geometry = openmc.Geometry(root) geometry.export_to_xml() diff --git a/examples/python/pincell/build-xml.py b/examples/python/pincell/build-xml.py index a3be3e97ec..0afb2527f6 100644 --- a/examples/python/pincell/build-xml.py +++ b/examples/python/pincell/build-xml.py @@ -149,8 +149,7 @@ root = openmc.Universe(universe_id=0, name='root universe') root.add_cells([fuel, gap, clad, water]) # Instantiate a Geometry, register the root Universe, and export to XML -geometry = openmc.Geometry() -geometry.root_universe = root +geometry = openmc.Geometry(root) geometry.export_to_xml() diff --git a/examples/python/pincell_multigroup/build-xml.py b/examples/python/pincell_multigroup/build-xml.py index 5ac5b376a3..6dbfa336bb 100644 --- a/examples/python/pincell_multigroup/build-xml.py +++ b/examples/python/pincell_multigroup/build-xml.py @@ -119,8 +119,7 @@ root = openmc.Universe(universe_id=0, name='root universe') root.add_cells([fuel, moderator]) # Instantiate a Geometry, register the root Universe, and export to XML -geometry = openmc.Geometry() -geometry.root_universe = root +geometry = openmc.Geometry(root) geometry.export_to_xml() diff --git a/examples/python/reflective/build-xml.py b/examples/python/reflective/build-xml.py index 4ecd0351fc..949e57c8c7 100644 --- a/examples/python/reflective/build-xml.py +++ b/examples/python/reflective/build-xml.py @@ -64,8 +64,7 @@ root = openmc.Universe(universe_id=0, name='root universe') root.add_cell(cell) # Instantiate a Geometry, register the root Universe, and export to XML -geometry = openmc.Geometry() -geometry.root_universe = root +geometry = openmc.Geometry(root) geometry.export_to_xml() diff --git a/openmc/arithmetic.py b/openmc/arithmetic.py index 5869cad802..f9ef58db89 100644 --- a/openmc/arithmetic.py +++ b/openmc/arithmetic.py @@ -67,24 +67,6 @@ class CrossScore(object): def __ne__(self, other): return not self == other - def __deepcopy__(self, memo): - existing = memo.get(id(self)) - - # If this is the first time we have tried to copy this object, create a copy - if existing is None: - clone = type(self).__new__(type(self)) - clone._left_score = self.left_score - clone._right_score = self.right_score - clone._binary_op = self.binary_op - - memo[id(self)] = clone - - return clone - - # If this object has been copied before, return the first copy made - else: - return existing - def __repr__(self): string = '({0} {1} {2})'.format(self.left_score, self.binary_op, self.right_score) @@ -169,28 +151,9 @@ class CrossNuclide(object): def __ne__(self, other): return not self == other - def __deepcopy__(self, memo): - existing = memo.get(id(self)) - - # If this is the first time we have tried to copy this object, create a copy - if existing is None: - clone = type(self).__new__(type(self)) - clone._left_nuclide = self.left_nuclide - clone._right_nuclide = self.right_nuclide - clone._binary_op = self.binary_op - - memo[id(self)] = clone - - return clone - - # If this object has been copied before, return the first copy made - else: - return existing - def __repr__(self): return self.name - @property def left_nuclide(self): return self._left_nuclide @@ -325,27 +288,6 @@ class CrossFilter(object): string += '{0: <16}{1}{2}\n'.format('\tBins', '=\t', filter_bins) return string - def __deepcopy__(self, memo): - existing = memo.get(id(self)) - - # If this is the first time we have tried to copy this object, create a copy - if existing is None: - clone = type(self).__new__(type(self)) - clone._left_filter = self.left_filter - clone._right_filter = self.right_filter - clone._binary_op = self.binary_op - clone._type = self.type - clone._bins = self._bins - clone._stride = self.stride - - memo[id(self)] = clone - - return clone - - # If this object has been copied before, return the first copy made - else: - return existing - @property def left_filter(self): return self._left_filter @@ -532,23 +474,6 @@ class AggregateScore(object): def __ne__(self, other): return not self == other - def __deepcopy__(self, memo): - existing = memo.get(id(self)) - - # If this is the first time we have tried to copy this object, create a copy - if existing is None: - clone = type(self).__new__(type(self)) - clone._scores = self.scores - clone._aggregate_op = self.aggregate_op - - memo[id(self)] = clone - - return clone - - # If this object has been copied before, return the first copy made - else: - return existing - def __repr__(self): string = ', '.join(map(str, self.scores)) string = '{0}({1})'.format(self.aggregate_op, string) @@ -622,23 +547,6 @@ class AggregateNuclide(object): def __ne__(self, other): return not self == other - def __deepcopy__(self, memo): - existing = memo.get(id(self)) - - # If this is the first time we have tried to copy this object, create a copy - if existing is None: - clone = type(self).__new__(type(self)) - clone._nuclides = self.nuclides - clone._aggregate_op = self._aggregate_op - - memo[id(self)] = clone - - return clone - - # If this object has been copied before, return the first copy made - else: - return existing - def __repr__(self): # Append each nuclide in the aggregate to the string @@ -757,26 +665,6 @@ class AggregateFilter(object): string += '{0: <16}{1}{2}\n'.format('\tBins', '=\t', self.bins) return string - def __deepcopy__(self, memo): - existing = memo.get(id(self)) - - # If this is the first time we have tried to copy this object, create a copy - if existing is None: - clone = type(self).__new__(type(self)) - clone._type = self.type - clone._aggregate_filter = self.aggregate_filter - clone._aggregate_op = self.aggregate_op - clone._bins = self._bins - clone._stride = self.stride - - memo[id(self)] = clone - - return clone - - # If this object has been copied before, return the first copy made - else: - return existing - @property def aggregate_filter(self): return self._aggregate_filter diff --git a/openmc/cell.py b/openmc/cell.py index c6cfc45136..936ffd94fe 100644 --- a/openmc/cell.py +++ b/openmc/cell.py @@ -1,9 +1,12 @@ from collections import OrderedDict, Iterable +from math import cos, sin, pi from numbers import Real, Integral from xml.etree import ElementTree as ET import sys import warnings +import numpy as np + import openmc import openmc.checkvalue as cv from openmc.surface import Halfspace @@ -33,7 +36,7 @@ class Cell(object): automatically be assigned. name : str, optional Name of the cell. If not specified, the name is the empty string. - fill : openmc.Material or openmc.Universe or openmc.Lattice or 'void' or iterable of openmc.Material, optional + fill : openmc.Material or openmc.Universe or openmc.Lattice or None or iterable of openmc.Material, optional Indicates what the region of space is filled with region : openmc.Region, optional Region of space that is assigned to the cell. @@ -44,9 +47,13 @@ class Cell(object): Unique identifier for the cell name : str Name of the cell - fill : openmc.Material or openmc.Universe or openmc.Lattice or 'void' or iterable of openmc.Material - Indicates what the region of space is filled with - region : openmc.Region + fill : openmc.Material or openmc.Universe or openmc.Lattice or None or iterable of openmc.Material + Indicates what the region of space is filled with. If None, the cell is + treated as a void. An iterable of materials is used to fill repeated + instances of a cell with different materials. + fill_type : {'material', 'universe', 'lattice', 'distribmat', 'void'} + Indicates what the cell is filled with. + region : openmc.Region or None Region of space that is assigned to the cell. rotation : Iterable of float If the cell is filled with a universe, this array specifies the angles @@ -63,6 +70,9 @@ class Cell(object): \sin\phi \sin\theta \sin\psi & -\sin\phi \cos\psi + \cos\phi \sin\theta \sin\psi \\ -\sin\theta & \sin\phi \cos\theta & \cos\phi \cos\theta \end{array} \right ] + rotation_matrix : numpy.ndarray + The rotation matrix defined by the angles specified in the + :attr:`Cell.rotation` property. temperature : float or iterable of float Temperature of the cell in Kelvin. Multiple temperatures can be given to give each distributed cell instance a unique temperature. @@ -80,19 +90,20 @@ class Cell(object): # Initialize Cell class attributes self.id = cell_id self.name = name - self._fill = None - self._type = None - self._region = None - self._temperature = None + self.fill = fill + self.region = region self._rotation = None + self._rotation_matrix = None + self._temperature = None self._translation = None self._offsets = None self._distribcell_index = None - if fill is not None: - self.fill = fill - if region is not None: - self.region = region + def __contains__(self, point): + if self.region is None: + return True + else: + return point in self.region def __eq__(self, other): if not isinstance(other, Cell): @@ -122,36 +133,27 @@ class Cell(object): def __repr__(self): string = 'Cell\n' - string += '{0: <16}{1}{2}\n'.format('\tID', '=\t', self._id) - string += '{0: <16}{1}{2}\n'.format('\tName', '=\t', self._name) + string += '{: <16}=\t{}\n'.format('\tID', self.id) + string += '{: <16}=\t{}\n'.format('\tName', self.name) - if isinstance(self._fill, openmc.Material): - string += '{0: <16}{1}{2}\n'.format('\tMaterial', '=\t', - self._fill._id) - elif isinstance(self._fill, Iterable): - string += '{0: <16}{1}'.format('\tMaterial', '=\t') - string += '[' - string += ', '.join(['void' if m == 'void' else str(m.id) - for m in self.fill]) - string += ']\n' - elif isinstance(self._fill, (openmc.Universe, openmc.Lattice)): - string += '{0: <16}{1}{2}\n'.format('\tFill', '=\t', - self._fill._id) + if self.fill_type == 'material': + string += '{: <16}=\tMaterial {}\n'.format('\tFill', self.fill.id) + elif self.fill_type == 'void': + string += '{: <16}=\tNone\n'.format('\tFill') + elif self.fill_type == 'distribmat': + string += '{: <16}=\t{}\n'.format('\tFill', list(map( + lambda m: m if m is None else m.id, self.fill))) else: - string += '{0: <16}{1}{2}\n'.format('\tFill', '=\t', self._fill) + string += '{: <16}=\t{}\n'.format('\tFill', self.fill.id) - string += '{0: <16}{1}{2}\n'.format('\tRegion', '=\t', self._region) - - string += '{0: <16}{1}{2}\n'.format('\tRotation', '=\t', - self._rotation) + string += '{: <16}=\t{}\n'.format('\tRegion', self.region) + string += '{: <16}=\t{}\n'.format('\tRotation', self.rotation) if self.fill_type == 'material': string += '\t{0: <15}=\t{1}\n'.format('Temperature', self.temperature) - string += '{0: <16}{1}{2}\n'.format('\tTranslation', '=\t', - self._translation) - string += '{0: <16}{1}{2}\n'.format('\tOffset', '=\t', self._offsets) - string += '{0: <16}{1}{2}\n'.format('\tDistribcell index', '=\t', - self._distribcell_index) + string += '{: <16}=\t{}\n'.format('\tTranslation', self.translation) + string += '{: <16}=\t{}\n'.format('\tOffset', self.offsets) + string += '{: <16}=\t{}\n'.format('\tDistribcell index', self.distribcell_index) return string @@ -175,8 +177,10 @@ class Cell(object): return 'universe' elif isinstance(self.fill, openmc.Lattice): return 'lattice' + elif isinstance(self.fill, Iterable): + return 'distribmat' else: - return None + return 'void' @property def region(self): @@ -186,6 +190,10 @@ class Cell(object): def rotation(self): return self._rotation + @property + def rotation_matrix(self): + return self._rotation_matrix + @property def temperature(self): return self._temperature @@ -223,33 +231,25 @@ class Cell(object): @fill.setter def fill(self, fill): - if isinstance(fill, basestring): - if fill.strip().lower() == 'void': - self._type = 'void' - else: + if fill is not None: + if isinstance(fill, basestring): + if fill.strip().lower() != 'void': + msg = 'Unable to set Cell ID="{0}" to use a non-Material ' \ + 'or Universe fill "{1}"'.format(self._id, fill) + raise ValueError(msg) + fill = None + + elif isinstance(fill, Iterable): + for i, f in enumerate(fill): + if f is not None: + cv.check_type('cell.fill[i]', f, openmc.Material) + + elif not isinstance(fill, (openmc.Material, openmc.Lattice, + openmc.Universe)): msg = 'Unable to set Cell ID="{0}" to use a non-Material or ' \ - 'Universe fill "{1}"'.format(self._id, fill) + 'Universe fill "{1}"'.format(self._id, fill) raise ValueError(msg) - elif isinstance(fill, openmc.Material): - self._type = 'normal' - - elif isinstance(fill, Iterable): - cv.check_type('cell.fill', fill, Iterable, - (openmc.Material, basestring)) - self._type = 'normal' - - elif isinstance(fill, openmc.Universe): - self._type = 'fill' - - elif isinstance(fill, openmc.Lattice): - self._type = 'lattice' - - else: - msg = 'Unable to set Cell ID="{0}" to use a non-Material or ' \ - 'Universe fill "{1}"'.format(self._id, fill) - raise ValueError(msg) - self._fill = fill @rotation.setter @@ -260,13 +260,23 @@ class Cell(object): cv.check_type('cell rotation', rotation, Iterable, Real) cv.check_length('cell rotation', rotation, 3) - self._rotation = rotation + self._rotation = np.asarray(rotation) + + # Save rotation matrix + phi, theta, psi = self.rotation*(-pi/180.) + c3, s3 = cos(phi), sin(phi) + c2, s2 = cos(theta), sin(theta) + c1, s1 = cos(psi), sin(psi) + self._rotation_matrix = np.array([ + [c1*c2, c1*s2*s3 - c3*s1, s1*s3 + c1*c3*s2], + [c2*s1, c1*c3 + s1*s2*s3, c3*s1*s2 - c1*s3], + [-s2, c2*s3, c2*c3]]) @translation.setter def translation(self, translation): cv.check_type('cell translation', translation, Iterable, Real) cv.check_length('cell translation', translation, 3) - self._translation = translation + self._translation = np.asarray(translation) @temperature.setter def temperature(self, temperature): @@ -286,7 +296,8 @@ class Cell(object): @region.setter def region(self, region): - cv.check_type('cell region', region, Region) + if region is not None: + cv.check_type('cell region', region, Region) self._region = region @distribcell_index.setter @@ -341,11 +352,11 @@ class Cell(object): def get_cell_instance(self, path, distribcell_index): # If the Cell is filled by a Material - if self._type == 'normal' or self._type == 'void': + if self.fill_type in ('material', 'distribmat', 'void'): offset = 0 # If the Cell is filled by a Universe - elif self._type == 'fill': + elif self.fill_type == 'universe': offset = self.offsets[distribcell_index-1] offset += self.fill.get_cell_instance(path, distribcell_index) @@ -368,8 +379,8 @@ class Cell(object): nuclides = OrderedDict() - if self._type != 'void': - nuclides.update(self._fill.get_all_nuclides()) + if self.fill_type != 'void': + nuclides.update(self.fill.get_all_nuclides()) return nuclides @@ -387,8 +398,8 @@ class Cell(object): cells = OrderedDict() - if self._type == 'fill' or self._type == 'lattice': - cells.update(self._fill.get_all_cells()) + if self.fill_type in ('universe', 'lattice'): + cells.update(self.fill.get_all_cells()) return cells @@ -428,11 +439,11 @@ class Cell(object): universes = OrderedDict() - if self._type == 'fill': - universes[self._fill._id] = self._fill - universes.update(self._fill.get_all_universes()) - elif self._type == 'lattice': - universes.update(self._fill.get_all_universes()) + if self.fill_type == 'universe': + universes[self.fill.id] = self.fill + universes.update(self.fill.get_all_universes()) + elif self.fill_type == 'lattice': + universes.update(self.fill.get_all_universes()) return universes @@ -443,24 +454,20 @@ class Cell(object): if len(self._name) > 0: element.set("name", str(self.name)) - if isinstance(self.fill, basestring): + if self.fill_type == 'void': element.set("material", "void") - elif isinstance(self.fill, openmc.Material): + elif self.fill_type == 'material': element.set("material", str(self.fill.id)) - elif isinstance(self.fill, Iterable): - element.set("material", ' '.join([m if m == 'void' else str(m.id) + elif self.fill_type == 'distribmat': + element.set("material", ' '.join(['void' if m is None else str(m.id) for m in self.fill])) - elif isinstance(self.fill, (openmc.Universe, openmc.Lattice)): + elif self.fill_type in ('universe', 'lattice'): element.set("fill", str(self.fill.id)) self.fill.create_xml_subelement(xml_element) - else: - element.set("fill", str(self.fill)) - self.fill.create_xml_subelement(xml_element) - if self.region is not None: # Set the region attribute with the region specification element.set("region", str(self.region)) diff --git a/openmc/checkvalue.py b/openmc/checkvalue.py index 62b843a3a0..cc0e1190df 100644 --- a/openmc/checkvalue.py +++ b/openmc/checkvalue.py @@ -4,33 +4,6 @@ from numbers import Integral, Real import numpy as np -def _isinstance(value, expected_type): - """A Numpy-aware replacement for isinstance - - This function will be obsolete when Numpy v. >= 1.9 is established. - """ - - # Declare numpy numeric types. - np_ints = (np.int_, np.intc, np.intp, np.int8, np.int16, np.int32, np.int64, - np.uint8, np.uint16, np.uint32, np.uint64) - np_floats = (np.float_, np.float16, np.float32, np.float64) - - # Include numpy integers, if necessary. - if type(expected_type) is tuple: - if Integral in expected_type: - expected_type = expected_type + np_ints - elif expected_type is Integral: - expected_type = (Integral, ) + np_ints - - # Include numpy floats, if necessary. - if type(expected_type) is tuple: - if Real in expected_type: - expected_type = expected_type + np_floats - elif expected_type is Real: - expected_type = (Real, ) + np_floats - - # Now, make the instance check. - return isinstance(value, expected_type) def check_type(name, value, expected_type, expected_iter_type=None): """Ensure that an object is of an expected type. Optionally, if the object is @@ -50,7 +23,7 @@ def check_type(name, value, expected_type, expected_iter_type=None): """ - if not _isinstance(value, expected_type): + if not isinstance(value, expected_type): if isinstance(expected_type, Iterable): msg = 'Unable to set "{0}" to "{1}" which is not one of the ' \ 'following types: "{2}"'.format(name, value, ', '.join( @@ -61,8 +34,16 @@ def check_type(name, value, expected_type, expected_iter_type=None): raise TypeError(msg) if expected_iter_type: + if isinstance(value, np.ndarray): + if not issubclass(value.dtype.type, expected_iter_type): + msg = 'Unable to set "{0}" to "{1}" since each item must be ' \ + 'of type "{2}"'.format(name, value, + expected_iter_type.__name__) + else: + return + for item in value: - if not _isinstance(item, expected_iter_type): + if not isinstance(item, expected_iter_type): if isinstance(expected_iter_type, Iterable): msg = 'Unable to set "{0}" to "{1}" since each item must be ' \ 'one of the following types: "{2}"'.format( @@ -118,7 +99,7 @@ def check_iterable_type(name, value, expected_type, min_depth=1, max_depth=1): # If this item is of the expected type, then we've reached the bottom # level of this branch. - if _isinstance(current_item, expected_type): + if isinstance(current_item, expected_type): # Is this deep enough? if len(tree) < min_depth: msg = 'Error setting "{0}": The item at {1} does not meet the '\ diff --git a/openmc/element.py b/openmc/element.py index 66371aba9a..14c98d4952 100644 --- a/openmc/element.py +++ b/openmc/element.py @@ -126,8 +126,8 @@ class Element(object): """ isotopes = [] - for isotope, abundance in natural_abundance.items(): - if isotope.startswith(self.name): + for isotope, abundance in sorted(natural_abundance.items()): + if isotope.startswith(self.name + '-'): nuc = openmc.Nuclide(isotope, self.xs) isotopes.append((nuc, abundance)) return isotopes diff --git a/openmc/filter.py b/openmc/filter.py index 52560a193a..72fb3b14ed 100644 --- a/openmc/filter.py +++ b/openmc/filter.py @@ -104,27 +104,6 @@ class Filter(object): def __hash__(self): return hash(repr(self)) - def __deepcopy__(self, memo): - existing = memo.get(id(self)) - - # If this is the first time we have tried to copy this object, create a copy - if existing is None: - clone = type(self).__new__(type(self)) - clone._type = self.type - clone._bins = copy.deepcopy(self.bins, memo) - clone._num_bins = self.num_bins - clone._mesh = copy.deepcopy(self.mesh, memo) - clone._stride = self.stride - clone._distribcell_paths = copy.deepcopy(self.distribcell_paths) - - memo[id(self)] = clone - - return clone - - # If this object has been copied before, return the first copy made - else: - return existing - def __repr__(self): string = 'Filter\n' string += '{0: <16}{1}{2}\n'.format('\tType', '=\t', self.type) @@ -196,7 +175,7 @@ class Filter(object): elif self.type in ['energy', 'energyout']: for edge in bins: - if not cv._isinstance(edge, Real): + if not isinstance(edge, Real): msg = 'Unable to add bin edge "{0}" to a "{1}" Filter ' \ 'since it is a non-integer or floating point ' \ 'value'.format(edge, self.type) @@ -220,7 +199,7 @@ class Filter(object): msg = 'Unable to add bins "{0}" to a mesh Filter since ' \ 'only a single mesh can be used per tally'.format(bins) raise ValueError(msg) - elif not cv._isinstance(bins[0], Integral): + elif not isinstance(bins[0], Integral): msg = 'Unable to add bin "{0}" to mesh Filter since it ' \ 'is a non-integer'.format(bins[0]) raise ValueError(msg) diff --git a/openmc/geometry.py b/openmc/geometry.py index ed437f6e19..2625ed3fa5 100644 --- a/openmc/geometry.py +++ b/openmc/geometry.py @@ -15,6 +15,11 @@ def reset_auto_ids(): class Geometry(object): """Geometry representing a collection of surfaces, cells, and universes. + Parameters + ---------- + root_universe : openmc.Universe, optional + Root universe which contains all others + Attributes ---------- root_universe : openmc.Universe @@ -22,9 +27,11 @@ class Geometry(object): """ - def __init__(self): + def __init__(self, root_universe=None): self._root_universe = None self._offsets = {} + if root_universe is not None: + self.root_universe = root_universe @property def root_universe(self): @@ -62,6 +69,23 @@ class Geometry(object): tree.write("geometry.xml", xml_declaration=True, encoding='utf-8', method="xml") + def find(self, point): + """Find cells/universes/lattices which contain a given point + + Parameters + ---------- + point : 3-tuple of float + Cartesian coordinates of the point + + Returns + ------- + list + Sequence of universes, cells, and lattices which are traversed to + find the given point + + """ + return self.root_universe.find(point) + def get_cell_instance(self, path): """Return the instance number for the final cell in a geometry path. @@ -120,14 +144,8 @@ class Geometry(object): """ - all_cells = self._root_universe.get_all_cells() - cells = set() - - for cell in all_cells.values(): - if cell._type == 'normal': - cells.add(cell) - - cells = list(cells) + all_cells = self.root_universe.get_all_cells() + cells = list(set(all_cells.values())) cells.sort(key=lambda x: x.id) return cells @@ -142,12 +160,7 @@ class Geometry(object): """ all_universes = self._root_universe.get_all_universes() - universes = set() - - for universe in all_universes.values(): - universes.add(universe) - - universes = list(universes) + universes = list(set(all_universes.values())) universes.sort(key=lambda x: x.id) return universes @@ -180,15 +193,17 @@ class Geometry(object): """ material_cells = self.get_all_material_cells() - materials = set() + materials = [] for cell in material_cells: - if isinstance(cell.fill, Iterable): - for m in cell.fill: materials.add(m) - else: - materials.add(cell.fill) + if cell.fill_type == 'distribmat': + for m in cell.fill: + if m is not None and m not in materials: + materials.append(m) + elif cell.fill_type == 'material': + if cell.fill not in materials: + materials.append(cell.fill) - materials = list(materials) materials.sort(key=lambda x: x.id) return materials @@ -203,13 +218,13 @@ class Geometry(object): """ all_cells = self.get_all_cells() - material_cells = set() + material_cells = [] for cell in all_cells: - if cell._type == 'normal': - material_cells.add(cell) + if cell.fill_type in ('material', 'distribmat'): + if cell not in material_cells: + material_cells.append(cell) - material_cells = list(material_cells) material_cells.sort(key=lambda x: x.id) return material_cells @@ -224,15 +239,15 @@ class Geometry(object): """ all_universes = self.get_all_universes() - material_universes = set() + material_universes = [] for universe in all_universes: cells = universe.cells for cell in cells: - if cell._type == 'normal': - material_universes.add(universe) + if cell.fill_type in ('material', 'distribmat', 'void'): + if universe not in material_universes: + material_universes.append(universe) - material_universes = list(material_universes) material_universes.sort(key=lambda x: x.id) return material_universes @@ -247,13 +262,13 @@ class Geometry(object): """ cells = self.get_all_cells() - lattices = set() + lattices = [] for cell in cells: - if isinstance(cell.fill, openmc.Lattice): - lattices.add(cell.fill) + if cell.fill_type == 'lattice': + if cell.fill not in lattices: + lattices.append(cell.fill) - lattices = list(lattices) lattices.sort(key=lambda x: x.id) return lattices diff --git a/openmc/lattice.py b/openmc/lattice.py index af6c14a6a9..7690104c71 100644 --- a/openmc/lattice.py +++ b/openmc/lattice.py @@ -1,8 +1,12 @@ +from __future__ import division + import abc from collections import OrderedDict, Iterable +from math import sqrt, floor from numbers import Real, Integral from xml.etree import ElementTree as ET import sys +import warnings import numpy as np @@ -113,12 +117,6 @@ class Lattice(object): cv.check_type('outer universe', outer, openmc.Universe) self._outer = outer - @universes.setter - def universes(self, universes): - cv.check_iterable_type('lattice universes', universes, openmc.Universe, - min_depth=2, max_depth=3) - self._universes = np.asarray(universes) - def get_unique_universes(self): """Determine all unique universes in the lattice @@ -239,6 +237,19 @@ class Lattice(object): class RectLattice(Lattice): """A lattice consisting of rectangular prisms. + To completely define a rectangular lattice, the + :attr:`RectLattice.lower_left` :attr:`RectLattice.pitch`, + :attr:`RectLattice.outer`, and :attr:`RectLattice.universes` properties need + to be set. + + Most methods for this class use a natural indexing scheme wherein elements + are assigned an index corresponding to their position relative to the + (x,y,z) axes in a Cartesian coordinate system, i.e., an index of (0,0,0) in + the lattice gives the element whose x, y, and z coordinates are the + smallest. However, note that when universes are assigned to lattice elements + using the :attr:`RectLattice.universes` property, the array indices do not + correspond to natural indices. + Parameters ---------- lattice_id : int, optional @@ -253,12 +264,6 @@ class RectLattice(Lattice): Unique identifier for the lattice name : str Name of the lattice - dimension : Iterable of int - An array of two or three integers representing the number of lattice - cells in the x- and y- (and z-) directions, respectively. - lower_left : Iterable of float - The coordinates of the lower-left corner of the lattice. If the lattice - is two-dimensional, only the x- and y-coordinates are specified. pitch : Iterable of float Pitch of the lattice in the x, y, and (if applicable) z directions in cm. @@ -266,7 +271,25 @@ class RectLattice(Lattice): A universe to fill all space outside the lattice universes : Iterable of Iterable of openmc.Universe A two- or three-dimensional list/array of universes filling each element - of the lattice + of the lattice. The first dimension corresponds to the z-direction (if + applicable), the second dimension corresponds to the y-direction, and + the third dimension corresponds to the x-direction. Note that for the + y-direction, a higher index corresponds to a lower physical + y-value. Each z-slice in the array can be thought of as a top-down view + of the lattice. + lower_left : Iterable of float + The Cartesian coordinates of the lower-left corner of the lattice. If + the lattice is two-dimensional, only the x- and y-coordinates are + specified. + indices : list of tuple + A list of all possible (z,y,x) or (y,x) lattice element indices. These + indices correspond to indices in the :attr:`RectLattice.universes` + property. + ndim : int + The number of dimensions of the lattice + shape : Iterable of int + An array of two or three integers representing the number of lattice + cells in the x- and y- (and z-) directions, respectively. """ @@ -274,7 +297,6 @@ class RectLattice(Lattice): super(RectLattice, self).__init__(lattice_id, name) # Initialize Lattice class attributes - self._dimension = None self._lower_left = None self._offsets = None @@ -283,7 +305,7 @@ class RectLattice(Lattice): return False elif not super(RectLattice, self).__eq__(other): return False - elif self.dimension != other.dimension: + elif self.shape != other.shape: return False elif self.lower_left != other.lower_left: return False @@ -300,8 +322,8 @@ class RectLattice(Lattice): string = 'RectLattice\n' string += '{0: <16}{1}{2}\n'.format('\tID', '=\t', self._id) string += '{0: <16}{1}{2}\n'.format('\tName', '=\t', self._name) - string += '{0: <16}{1}{2}\n'.format('\tDimension', '=\t', - self._dimension) + string += '{0: <16}{1}{2}\n'.format('\tShape', '=\t', + self.shape) string += '{0: <16}{1}{2}\n'.format('\tLower Left', '=\t', self._lower_left) string += '{0: <16}{1}{2}\n'.format('\tPitch', '=\t', self._pitch) @@ -320,7 +342,7 @@ class RectLattice(Lattice): string += '{0} '.format(universe._id) # Add a newline character every time we reach end of row of cells - if (i+1) % self._dimension[-1] == 0: + if (i+1) % self.shape[0] == 0: string += '\n' string = string.rstrip('\n') @@ -333,7 +355,7 @@ class RectLattice(Lattice): string += '{0} '.format(offset) # Add a newline character when we reach end of row of cells - if (i+1) % self._dimension[-1] == 0: + if (i+1) % self.shape[0] == 0: string += '\n' string = string.rstrip('\n') @@ -341,24 +363,29 @@ class RectLattice(Lattice): return string @property - def dimension(self): - return self._dimension + def indices(self): + if self.ndim == 2: + return list(np.broadcast(*np.ogrid[ + :self.shape[1], :self.shape[0]])) + else: + return list(np.broadcast(*np.ogrid[ + :self.shape[2], :self.shape[1], :self.shape[0]])) @property def lower_left(self): return self._lower_left + @property + def ndim(self): + return len(self.pitch) + @property def offsets(self): return self._offsets - @dimension.setter - def dimension(self, dimension): - cv.check_type('lattice dimension', dimension, Iterable, Integral) - cv.check_length('lattice dimension', dimension, 2, 3) - for dim in dimension: - cv.check_greater_than('lattice dimension', dim, 0) - self._dimension = dimension + @property + def shape(self): + return self._universes.shape[::-1] @lower_left.setter def lower_left(self, lower_left): @@ -379,8 +406,13 @@ class RectLattice(Lattice): cv.check_greater_than('lattice pitch', dim, 0.0) self._pitch = pitch - def get_cell_instance(self, path, distribcell_index): + @Lattice.universes.setter + def universes(self, universes): + cv.check_iterable_type('lattice universes', universes, openmc.Universe, + min_depth=2, max_depth=3) + self._universes = np.asarray(universes) + def get_cell_instance(self, path, distribcell_index): # Extract the lattice element from the path next_index = path.index('-') lat_id_indices = path[:next_index] @@ -395,7 +427,7 @@ class RectLattice(Lattice): lat_z = int(i.split(',')[2]) - 1 # For 2D Lattices - if len(self._dimension) == 2: + if self.ndim == 2: offset = self._offsets[lat_z, lat_y, lat_x, distribcell_index-1] offset += self._universes[lat_x][lat_y].get_cell_instance(path, distribcell_index) @@ -408,6 +440,128 @@ class RectLattice(Lattice): return offset + def find_element(self, point): + """Determine index of lattice element and local coordinates for a point + + Parameters + ---------- + point : Iterable of float + Cartesian coordinates of point + + Returns + ------- + 2- or 3-tuple of int + A tuple of the corresponding (x,y,z) lattice element indices + 3-tuple of float + Carestian coordinates of the point in the corresponding lattice + element coordinate system + + """ + ix = floor((point[0] - self.lower_left[0])/self.pitch[0]) + iy = floor((point[1] - self.lower_left[1])/self.pitch[1]) + if self.ndim == 2: + idx = (ix, iy) + else: + iz = floor((point[2] - self.lower_left[2])/self.pitch[2]) + idx = (ix, iy, iz) + return idx, self.get_local_coordinates(point, idx) + + def get_local_coordinates(self, point, idx): + """Determine local coordinates of a point within a lattice element + + Parameters + ---------- + point : Iterable of float + Cartesian coordinates of point + idx : Iterable of int + (x,y,z) indices of lattice element. If the lattice is 2D, the z + index can be omitted. + + Returns + ------- + 3-tuple of float + Cartesian coordinates of point in the lattice element coordinate + system + + """ + x = point[0] - (self.lower_left[0] + (idx[0] + 0.5)*self.pitch[0]) + y = point[1] - (self.lower_left[1] + (idx[1] + 0.5)*self.pitch[1]) + if self.ndim == 2: + z = point[2] + else: + z = point[2] - (self.lower_left[2] + (idx[2] + 0.5)*self.pitch[2]) + return (x, y, z) + + def get_universe_index(self, idx): + """Return index in the universes array corresponding to a lattice element index + + Parameters + ---------- + idx : Iterable of int + Lattice element indices in the :math:`(x,y,z)` coordinate system + + Returns + ------- + 2- or 3-tuple of int + Indices used when setting the :attr:`RectLattice.universes` property + + """ + max_y = self.shape[1] - 1 + if self.ndim == 2: + x, y = idx + return (max_y - y, x) + else: + x, y, z = idx + return (z, max_y - y, x) + + def is_valid_index(self, idx): + """Determine whether lattice element index is within defined range + + Parameters + ---------- + idx : Iterable of int + Lattice element indices in the :math:`(x,y,z)` coordinate system + + Returns + ------- + bool + Whether index is valid + + """ + if self.ndim == 2: + return (0 <= idx[0] < self.shape[0] and + 0 <= idx[1] < self.shape[1]) + else: + return (0 <= idx[0] < self.shape[0] and + 0 <= idx[1] < self.shape[1] and + 0 <= idx[2] < self.shape[2]) + + def find(self, point): + """Find cells/universes/lattices which contain a given point + + Parameters + ---------- + point : 3-tuple of float + Cartesian coordinatesof the point + + Returns + ------- + list + Sequence of universes, cells, and lattices which are traversed to + find the given point + + """ + idx, p = self.find_element(point) + if self.is_valid_index(idx): + idx_u = self.get_universe_index(idx) + u = self.universes[idx_u] + else: + if self.outer is not None: + u = self.outer + else: + return [] + return [(self, idx)] + u.find(p) + def create_xml_subelement(self, xml_element): # Determine if XML element already contains subelement for this Lattice @@ -436,7 +590,7 @@ class RectLattice(Lattice): # Export Lattice cell dimensions dimension = ET.SubElement(lattice_subelement, "dimension") - dimension.text = ' '.join(map(str, self._dimension)) + dimension.text = ' '.join(map(str, self.shape)) # Export Lattice lower left lower_left = ET.SubElement(lattice_subelement, "lower_left") @@ -446,10 +600,10 @@ class RectLattice(Lattice): universe_ids = '\n' # 3D Lattices - if len(self._dimension) == 3: - for z in range(self._dimension[2]): - for y in range(self._dimension[1]): - for x in range(self._dimension[0]): + if self.ndim == 3: + for z in range(self.shape[2]): + for y in range(self.shape[1]): + for x in range(self.shape[0]): universe = self._universes[z][y][x] # Append Universe ID to the Lattice XML subelement @@ -466,8 +620,8 @@ class RectLattice(Lattice): # 2D Lattices else: - for y in range(self._dimension[1]): - for x in range(self._dimension[0]): + for y in range(self.shape[1]): + for x in range(self.shape[0]): universe = self._universes[y][x] # Append Universe ID to Lattice XML subelement @@ -490,7 +644,18 @@ class RectLattice(Lattice): class HexLattice(Lattice): - """A lattice consisting of hexagonal prisms. + r"""A lattice consisting of hexagonal prisms. + + To completely define a hexagonal lattice, the :attr:`HexLattice.center`, + :attr:`HexLattice.pitch`, :attr:`HexLattice.universes`, and + :attr:`HexLattice.outer` properties need to be set. + + Most methods for this class use a natural indexing scheme wherein elements + are assigned an index corresponding to their position relative to skewed + :math:`(x,\alpha,z)` axes as described fully in + :ref:`hexagonal_indexing`. However, note that when universes are assigned to + lattice elements using the :attr:`HexLattice.universes` property, the array + indices do not correspond to natural indices. Parameters ---------- @@ -506,26 +671,31 @@ class HexLattice(Lattice): Unique identifier for the lattice name : str Name of the lattice - num_rings : int - Number of radial ring positions in the xy-plane - num_axial : int - Number of positions along the z-axis. - center : Iterable of float - Coordinates of the center of the lattice. If the lattice does not have - axial sections then only the x- and y-coordinates are specified pitch : Iterable of float Pitch of the lattice in cm. The first item in the iterable specifies the pitch in the radial direction and, if the lattice is 3D, the second item in the iterable specifies the pitch in the axial direction. outer : openmc.Universe A universe to fill all space outside the lattice - universes : Iterable of Iterable of openmc.Universe + universes : Nested Iterable of openmc.Universe A two- or three-dimensional list/array of universes filling each element of the lattice. Each sub-list corresponds to one ring of universes and should be ordered from outermost ring to innermost ring. The universes within each sub-list are ordered from the "top" and proceed in a clockwise fashion. The :meth:`HexLattice.show_indices` method can be used to help figure out indices for this property. + center : Iterable of float + Coordinates of the center of the lattice. If the lattice does not have + axial sections then only the x- and y-coordinates are specified + indices : list of tuple + A list of all possible (z,r,i) or (r,i) lattice element indices that are + possible, where z is the axial index, r is in the ring index (starting + from the outermost ring), and i is the index with a ring starting from + the top and proceeding clockwise. + num_rings : int + Number of radial ring positions in the xy-plane + num_axial : int + Number of positions along the z-axis. """ @@ -597,17 +767,15 @@ class HexLattice(Lattice): def center(self): return self._center - @num_rings.setter - def num_rings(self, num_rings): - cv.check_type('number of rings', num_rings, Integral) - cv.check_greater_than('number of rings', num_rings, 0) - self._num_rings = num_rings - - @num_axial.setter - def num_axial(self, num_axial): - cv.check_type('number of axial', num_axial, Integral) - cv.check_greater_than('number of axial', num_axial, 0) - self._num_axial = num_axial + @property + def indices(self): + if self.num_axial is None: + return [(r, i) for r in range(self.num_rings) + for i in range(max(6*(self.num_rings - 1 - r), 1))] + else: + return [(z, r, i) for z in range(self.num_axial) + for r in range(self.num_rings) + for i in range(max(6*(self.num_rings - 1 - r), 1))] @center.setter def center(self, center): @@ -625,8 +793,9 @@ class HexLattice(Lattice): @Lattice.universes.setter def universes(self, universes): - # Call Lattice.universes parent class setter property - Lattice.universes.fset(self, universes) + cv.check_iterable_type('lattice universes', universes, openmc.Universe, + min_depth=2, max_depth=3) + self._universes = universes # NOTE: This routine assumes that the user creates a "ragged" list of # lists, where each sub-list corresponds to one ring of Universes. @@ -649,14 +818,14 @@ class HexLattice(Lattice): # Set the number of axial positions. if n_dims == 3: - self.num_axial = len(self._universes) + self._num_axial = len(self._universes) else: self._num_axial = None # Set the number of rings and make sure this number is consistent for # all axial positions. if n_dims == 3: - self.num_rings = len(self._universes[0]) + self._num_rings = len(self._universes[0]) for rings in self._universes: if len(rings) != self._num_rings: msg = 'HexLattice ID={0:d} has an inconsistent number of ' \ @@ -664,7 +833,7 @@ class HexLattice(Lattice): raise ValueError(msg) else: - self.num_rings = len(self._universes) + self._num_rings = len(self._universes) # Make sure there are the correct number of elements in each ring. if n_dims == 3: @@ -705,6 +874,170 @@ class HexLattice(Lattice): 6*(self._num_rings - 1 - r)) raise ValueError(msg) + def find_element(self, point): + r"""Determine index of lattice element and local coordinates for a point + + Parameters + ---------- + point : Iterable of float + Cartesian coordinates of point + + Returns + ------- + 3-tuple of int + Indices of corresponding lattice element in :math:`(x,\alpha,z)` + bases + numpy.ndarray + Carestian coordinates of the point in the corresponding lattice + element coordinate system + + """ + # Convert coordinates to skewed bases + x = point[0] - self.center[0] + y = point[1] - self.center[1] + if self._num_axial is None: + iz = 1 + else: + z = point[2] - self.center[2] + iz = floor(z/self.pitch[1] + 0.5*self.num_axial) + alpha = y - x/sqrt(3.) + ix = floor(x/(sqrt(0.75) * self.pitch[0])) + ia = floor(alpha/self.pitch[0]) + + # Check four lattice elements to see which one is closest based on local + # coordinates + d_min = np.inf + for idx in [(ix, ia, iz), (ix + 1, ia, iz), (ix, ia + 1, iz), + (ix + 1, ia + 1, iz)]: + p = self.get_local_coordinates(point, idx) + d = p[0]**2 + p[1]**2 + if d < d_min: + d_min = d + idx_min = idx + p_min = p + + return idx_min, p_min + + def get_local_coordinates(self, point, idx): + r"""Determine local coordinates of a point within a lattice element + + Parameters + ---------- + point : Iterable of float + Cartesian coordinates of point + idx : Iterable of int + Indices of lattice element in :math:`(x,\alpha,z)` bases + + Returns + ------- + 3-tuple of float + Cartesian coordinates of point in the lattice element coordinate + system + + """ + x = point[0] - (self.center[0] + sqrt(0.75)*self.pitch[0]*idx[0]) + y = point[1] - (self.center[1] + (0.5*idx[0] + idx[1])*self.pitch[0]) + if self._num_axial is None: + z = point[2] + else: + z = point[2] - (self.center[2] + (idx[2] + 0.5 - 0.5*self.num_axial)* + self.pitch[1]) + return (x, y, z) + + def get_universe_index(self, idx): + r"""Return index in the universes array corresponding to a lattice element index + + Parameters + ---------- + idx : Iterable of int + Lattice element indices in the :math:`(x,\alpha,z)` coordinate + system + + Returns + ------- + 2- or 3-tuple of int + Indices used when setting the :attr:`HexLattice.universes` property + + """ + + # First we determine which ring the index corresponds to. + x = idx[0] + a = idx[1] + z = -a - x + g = max(abs(x), abs(a), abs(z)) + + # Next we use a clever method to figure out where along the ring we are. + i_ring = self._num_rings - 1 - g + if x >= 0: + if a >= 0: + i_within = x + else: + i_within = 2*g + z + else: + if a <= 0: + i_within = 3*g - x + else: + i_within = 5*g - z + + if self.num_axial is None: + return (i_ring, i_within) + else: + return (idx[2], i_ring, i_within) + + def is_valid_index(self, idx): + r"""Determine whether lattice element index is within defined range + + Parameters + ---------- + idx : Iterable of int + Lattice element indices in the :math:`(x,\alpha,z)` coordinate + system + + Returns + ------- + bool + Whether index is valid + + """ + x = idx[0] + y = idx[1] + z = 0 - y - x + g = max(abs(x), abs(y), abs(z)) + if self.num_axial is None: + return g < self.num_rings + else: + return g < self.num_rings and 0 <= idx[2] < self.num_axial + + def find(self, point): + """Find cells/universes/lattices which contain a given point + + Parameters + ---------- + point : 3-tuple of float + Cartesian coordinatesof the point + + Returns + ------- + list + Sequence of universes, cells, and lattices which are traversed to + find the given point + + """ + idx, p = self.find_element(point) + if self.is_valid_index(idx): + idx_u = self.get_universe_index(idx) + if self.num_axial is None: + u = self.universes[idx_u[0]][idx_u[1]] + else: + u = self.universes[idx_u[0]][idx_u[1]][idx_u[2]] + else: + if self.outer is not None: + u = self.outer + else: + return [] + + return [(self, idx)] + u.find(p) + def create_xml_subelement(self, xml_element): # Determine if XML element already contains subelement for this Lattice path = './hex_lattice[@id=\'{0}\']'.format(self._id) @@ -736,8 +1069,8 @@ class HexLattice(Lattice): lattice_subelement.set("n_axial", str(self._num_axial)) # Export Lattice cell center - dimension = ET.SubElement(lattice_subelement, "center") - dimension.text = ' '.join(map(str, self._center)) + center = ET.SubElement(lattice_subelement, "center") + center.text = ' '.join(map(str, self._center)) # Export the Lattice nested Universe IDs. diff --git a/openmc/material.py b/openmc/material.py index 6b2b07f2d4..66c2320ea4 100644 --- a/openmc/material.py +++ b/openmc/material.py @@ -50,14 +50,14 @@ class Material(object): Units used for `density`. Can be one of 'g/cm3', 'g/cc', 'kg/cm3', 'atom/b-cm', 'atom/cm3', 'sum', or 'macro'. The 'macro' unit only applies in the case of a multi-group calculation. - elements : collections.OrderedDict - Dictionary whose keys are element names and values are 3-tuples - consisting of an :class:`openmc.Element` instance, the percent density, - and the percent type (atom or weight fraction). - nuclides : collections.OrderedDict - Dictionary whose keys are nuclide names and values are 3-tuples - consisting of an :class:`openmc.Nuclide` instance, the percent density, - and the percent type (atom or weight fraction). + elements : list of tuple + List in which each item is a 3-tuple consisting of an + :class:`openmc.Element` instance, the percent density, and the percent + type ('ao' or 'wo'). + nuclides : list of tuple + List in which each item is a 3-tuple consisting of an + :class:`openmc.Nuclide` instance, the percent density, and the percent + type ('ao' or 'wo'). """ @@ -68,19 +68,15 @@ class Material(object): self._density = None self._density_units = '' - # An ordered dictionary of Nuclides (order affects OpenMC results) - # Keys - Nuclide names - # Values - tuple (nuclide, percent, percent type) - self._nuclides = OrderedDict() + # A list of tuples (nuclide, percent, percent type) + self._nuclides = [] # The single instance of Macroscopic data present in this material # (only one is allowed, hence this is different than _nuclides, etc) self._macroscopic = None - # An ordered dictionary of Elements (order affects OpenMC results) - # Keys - Element names - # Values - tuple (element, percent, percent type) - self._elements = OrderedDict() + # A list of tuples (element, percent, percent type) + self._elements = [] # If specified, a list of tuples of (table name, xs identifier) self._sab = [] @@ -134,10 +130,8 @@ class Material(object): string += '{0: <16}\n'.format('\tNuclides') - for nuclide in self._nuclides: - percent = self._nuclides[nuclide][1] - percent_type = self._nuclides[nuclide][2] - string += '{0: <16}'.format('\t{0}'.format(nuclide)) + for nuclide, percent, percent_type in self._nuclides: + string += '{0: <16}'.format('\t{0.name}.{0.xs}'.format(nuclide)) string += '=\t{0: <12} [{1}]\n'.format(percent, percent_type) if self._macroscopic is not None: @@ -146,39 +140,12 @@ class Material(object): string += '{0: <16}\n'.format('\tElements') - for element in self._elements: - percent = self._elements[element][1] - percent_type = self._elements[element][2] - string += '{0: <16}'.format('\t{0}'.format(element)) + for element, percent, percent_type in self._elements: + string += '{0: <16}'.format('\t{0.name}.{0.xs}'.format(element)) string += '=\t{0: <12} [{1}]\n'.format(percent, percent_type) return string - def __deepcopy__(self, memo): - existing = memo.get(id(self)) - - if existing is None: - # If this is the first time we have tried to copy this object, create a copy - clone = type(self).__new__(type(self)) - clone._id = self._id - clone._name = self._name - clone._density = self._density - clone._density_units = self._density_units - clone._nuclides = deepcopy(self._nuclides, memo) - clone._macroscopic = self._macroscopic - clone._elements = deepcopy(self._elements, memo) - clone._sab = deepcopy(self._sab, memo) - clone._convert_to_distrib_comps = self._convert_to_distrib_comps - clone._distrib_otf_file = self._distrib_otf_file - - memo[id(self)] = clone - - return clone - - else: - # If this object has been copied before, return the first copy made - return existing - @property def id(self): return self._id @@ -326,7 +293,7 @@ class Material(object): else: nuclide = openmc.Nuclide(nuclide) - self._nuclides[nuclide._name] = (nuclide, percent, percent_type) + self._nuclides.append((nuclide, percent, percent_type)) def remove_nuclide(self, nuclide): """Remove a nuclide from the material @@ -344,8 +311,9 @@ class Material(object): raise ValueError(msg) # If the Material contains the Nuclide, delete it - if nuclide._name in self._nuclides: - del self._nuclides[nuclide._name] + for nuc in self._nuclides: + if nuclide == nuc: + self._nuclides.remove(nuc) def add_macroscopic(self, macroscopic): """Add a macroscopic to the material. This will also set the @@ -464,10 +432,9 @@ class Material(object): raise NotImplementedError('Expanding natural element based on ' 'weight percent is not yet supported.') for isotope, abundance in element.expand(): - self._nuclides[isotope.name] = ( - isotope, percent*abundance, percent_type) + self._nuclides.append((isotope, percent*abundance, percent_type)) else: - self._elements[element.name] = (element, percent, percent_type) + self._elements.append((element, percent, percent_type)) def remove_element(self, element): """Remove a natural element from the material @@ -479,9 +446,15 @@ class Material(object): """ - # If the Material contains the Element, delete it - if element._name in self._elements: - del self._elements[element._name] + if not isinstance(nuclide, openmc.Element): + msg = 'Unable to remove "{0}" in Material ID="{1}" ' \ + 'since it is not an Element'.format(self.id, element) + raise ValueError(msg) + + # If the Material contains the Nuclide, delete it + for elm in self._elements: + if element == elm: + self._nuclides.remove(elm) def add_s_alpha_beta(self, name, xs): r"""Add an :math:`S(\alpha,\beta)` table to the material @@ -513,10 +486,10 @@ class Material(object): self._sab.append((name, xs)) def make_isotropic_in_lab(self): - for nuclide_name in self._nuclides: - self._nuclides[nuclide_name][0].scattering = 'iso-in-lab' - for element_name in self._elements: - self._elements[element_name][0].scattering = 'iso-in-lab' + for nuclide, percent, percent_type in self._nuclides: + nuclide.scattering = 'iso-in-lab' + for element, percent, percent_type in self._elements: + element.scattering = 'iso-in-lab' def get_all_nuclides(self): """Returns all nuclides in the material @@ -531,15 +504,10 @@ class Material(object): nuclides = OrderedDict() - for nuclide_name, nuclide_tuple in self._nuclides.items(): - nuclide = nuclide_tuple[0] - density = nuclide_tuple[1] - nuclides[nuclide._name] = (nuclide, density) - - for element_name, element_tuple in self._elements.items(): - element = element_tuple[0] - density = element_tuple[1] + for nuclide, density, density_type in self._nuclides: + nuclides[nuclide.name] = (nuclide, density) + for element, density, density_type in self._elements: # Expand natural element into isotopes for isotope, abundance in element.expand(): nuclides[isotope.name] = (isotope, density*abundance) @@ -548,7 +516,7 @@ class Material(object): def _get_nuclide_xml(self, nuclide, distrib=False): xml_element = ET.Element("nuclide") - xml_element.set("name", nuclide[0]._name) + xml_element.set("name", nuclide[0].name) if not distrib: if nuclide[2] == 'ao': @@ -575,7 +543,7 @@ class Material(object): def _get_element_xml(self, element, distrib=False): xml_element = ET.Element("element") - xml_element.set("name", str(element[0]._name)) + xml_element.set("name", str(element[0].name)) if not distrib: if element[2] == 'ao': @@ -594,7 +562,7 @@ class Material(object): def _get_nuclides_xml(self, nuclides, distrib=False): xml_elements = [] - for nuclide in nuclides.values(): + for nuclide in nuclides: xml_elements.append(self._get_nuclide_xml(nuclide, distrib)) return xml_elements @@ -602,7 +570,7 @@ class Material(object): def _get_elements_xml(self, elements, distrib=False): xml_elements = [] - for element in elements.values(): + for element in elements: xml_elements.append(self._get_element_xml(element, distrib)) return xml_elements @@ -650,7 +618,7 @@ class Material(object): subelement = ET.SubElement(element, "compositions") comps = [] - allnucs = self._nuclides.values() + self._elements.values() + allnucs = self._nuclides + self._elements dist_per_type = allnucs[0][2] for nuc, per, typ in allnucs: if not typ == dist_per_type: @@ -845,4 +813,4 @@ class Materials(cv.CheckedList): # Write the XML Tree to the materials.xml file tree = ET.ElementTree(self._materials_file) tree.write("materials.xml", xml_declaration=True, - encoding='utf-8', method="xml") + encoding='utf-8', method="xml") diff --git a/openmc/mesh.py b/openmc/mesh.py index 8bad6c5374..0c88d4a680 100644 --- a/openmc/mesh.py +++ b/openmc/mesh.py @@ -1,5 +1,4 @@ from collections import Iterable -import copy from numbers import Real, Integral from xml.etree import ElementTree as ET import sys @@ -83,28 +82,6 @@ class Mesh(object): else: return True - def __deepcopy__(self, memo): - existing = memo.get(id(self)) - - # If this is the first time we have tried to copy this object, create a copy - if existing is None: - clone = type(self).__new__(type(self)) - clone._id = self._id - clone._name = self._name - clone._type = self._type - clone._dimension = copy.deepcopy(self._dimension, memo) - clone._lower_left = copy.deepcopy(self._lower_left, memo) - clone._upper_right = copy.deepcopy(self._upper_right, memo) - clone._width = copy.deepcopy(self._width, memo) - - memo[id(self)] = clone - - return clone - - # If this object has been copied before, return the first copy made - else: - return existing - @property def id(self): return self._id diff --git a/openmc/mgxs/library.py b/openmc/mgxs/library.py index 20302b6246..e0e14b862e 100644 --- a/openmc/mgxs/library.py +++ b/openmc/mgxs/library.py @@ -34,7 +34,7 @@ class Library(object): Parameters ---------- openmc_geometry : openmc.Geometry - An geometry which has been initialized with a root universe + A geometry which has been initialized with a root universe by_nuclide : bool If true, computes cross sections for each nuclide in each domain mgxs_types : Iterable of str @@ -460,7 +460,7 @@ class Library(object): ---------- domain : Material or Cell or Universe or Integral The material, cell, or universe object of interest (or its ID) - mgxs_type : {'total', 'transport', 'nu-transport', 'absorption', 'capture', 'fission', 'nu-fission', 'kappa-fission', 'scatter', 'nu-scatter', 'scatter matrix', 'nu-scatter matrix', 'chi'} + mgxs_type : {'total', 'transport', 'nu-transport', 'absorption', 'capture', 'fission', 'nu-fission', 'kappa-fission', 'scatter', 'nu-scatter', 'scatter matrix', 'nu-scatter matrix', 'multiplicity matrix', 'nu-fission matrix', chi'} The type of multi-group cross section object to return Returns @@ -484,7 +484,7 @@ class Library(object): cv.check_type('domain', domain, (openmc.Universe, Integral)) # Check that requested domain is included in library - if cv._isinstance(domain, Integral): + if isinstance(domain, Integral): domain_id = domain for domain in self.domains: if domain_id == domain.id: @@ -853,16 +853,29 @@ class Library(object): mymgxs = self.get_mgxs(domain, 'kappa-fission') xsdata.set_kappa_fission_mgxs(mymgxs, xs_type=xs_type, nuclide=[nuclide]) - if 'chi' in self.mgxs_types: - mymgxs = self.get_mgxs(domain, 'chi') - xsdata.set_chi_mgxs(mymgxs, xs_type=xs_type, nuclide=[nuclide]) - if 'nu-fission' in self.mgxs_types: - mymgxs = self.get_mgxs(domain, 'nu-fission') + # For chi and nu-fission we can either have only a nu-fission matrix + # provided, or vectors of chi and nu-fission provided + if 'nu-fission matrix' in self.mgxs_types: + mymgxs = self.get_mgxs(domain, 'nu-fission matrix') xsdata.set_nu_fission_mgxs(mymgxs, xs_type=xs_type, nuclide=[nuclide]) - # multiplicity requires scatter and nu-scatter - if ((('scatter matrix' in self.mgxs_types) and - ('nu-scatter matrix' in self.mgxs_types))): + else: + if 'chi' in self.mgxs_types: + mymgxs = self.get_mgxs(domain, 'chi') + xsdata.set_chi_mgxs(mymgxs, xs_type=xs_type, nuclide=[nuclide]) + if 'nu-fission' in self.mgxs_types: + mymgxs = self.get_mgxs(domain, 'nu-fission') + xsdata.set_nu_fission_mgxs(mymgxs, xs_type=xs_type, + nuclide=[nuclide]) + # If multiplicity matrix is available, prefer that + if 'multiplicity matrix' in self.mgxs_types: + mymgxs = self.get_mgxs(domain, 'multiplicity matrix') + xsdata.set_multiplicity_mgxs(mymgxs, xs_type=xs_type, + nuclide=[nuclide]) + using_multiplicity = True + # multiplicity wil fall back to using scatter and nu-scatter + elif ((('scatter matrix' in self.mgxs_types) and + ('nu-scatter matrix' in self.mgxs_types))): scatt_mgxs = self.get_mgxs(domain, 'scatter matrix') nuscatt_mgxs = self.get_mgxs(domain, 'nu-scatter matrix') xsdata.set_multiplicity_mgxs(nuscatt_mgxs, scatt_mgxs, @@ -926,6 +939,7 @@ class Library(object): See also -------- Library.dump_to_file() + Library.create_mg_mode() """ @@ -975,14 +989,14 @@ class Library(object): return mgxs_file - def create_mg_library_and_materials(self, xsdata_names=None, xs_ids=None, - material_ids=None): + def create_mg_mode(self, xsdata_names=None, xs_ids=None): """Creates an openmc.MGXSLibrary object to contain the MGXS data for the Multi-Group mode of OpenMC as well as the associated openmc.Materials - objects. This method cannot be used for Library objects with - `Library.by_nuclide == True` since the materials to output would be - problem dependent and thus any Materials object produced by this method - would not be useful. + and openmc.Geometry objects. The created Geometry is the same as that + used to generate the MGXS data, with the only differences being + modifications to point to newly-created Materials which point to the + multi-group data. This method only creates a macroscopic + MGXS Library even if nuclidic tallies are specified in the Library. Parameters ---------- @@ -993,18 +1007,16 @@ class Library(object): Cross section set identifier (i.e., '71c') for all data sets (if only str) or for each individual one (if iterable of str). Defaults to '1m'. - material_ids : None or Iterable of Integral - An optional list of material IDs to pass to the materials in - materials_file. Defaults to `None` implying the materials will be - given an ID number which matches the index of the domain in - `self.domains` Returns ------- mgxs_file : openmc.MGXSLibrary Multi-Group Cross Section File that is ready to be printed to the file of choice by the user. - materials_file : openmc.Materials + materials : openmc.Materials + Materials file ready to be printed with all the macroscopic data + present within this Library. + geometry : openmc.Geometry Materials file ready to be printed with all the macroscopic data present within this Library. @@ -1036,42 +1048,51 @@ class Library(object): cv.check_iterable_type('xs_ids', xs_ids, basestring) else: xs_ids = ['1m' for i in range(len(self.domains))] - if material_ids is not None: - cv.check_iterable_type('material_ids', material_ids, Integral) xs_type = 'macro' - # Initialize files + # Initialize MGXS File mgxs_file = openmc.MGXSLibrary(self.energy_groups) - materials = [] - macroscopics = [] - nuclide = 'total' + # Create a copy of the Geometry to differentiate for these Macroscopics + geometry = copy.deepcopy(self.openmc_geometry) + materials = openmc.Materials() + + # Get all Cells from the Geometry for differentiation + all_cells = geometry.get_all_material_cells() + # Create the xsdata object and add it to the mgxs_file for i, domain in enumerate(self.domains): + # Build & add metadata to XSdata object if xsdata_names is None: xsdata_name = 'set' + str(i + 1) else: xsdata_name = xsdata_names[i] - xsdata = self.get_xsdata(domain, xsdata_name, nuclide=nuclide, + # Create XSdata and Macroscopic for this domain + xsdata = self.get_xsdata(domain, xsdata_name, nuclide='total', xs_type=xs_type, xs_id=xs_ids[i]) - mgxs_file.add_xsdata(xsdata) + macroscopic = openmc.Macroscopic(name=xsdata_name, xs=xs_ids[i]) - macroscopics.append(openmc.Macroscopic(name=xsdata_name, - xs=xs_ids[i])) - if material_ids is not None: - mat_id = material_ids[i] - else: - mat_id = i - materials.append(openmc.Material(name=xsdata_name + '.' + - xs_ids[i], material_id=mat_id)) - materials[-1].add_macroscopic(macroscopics[-1]) + # Create Material and add to collection + material = openmc.Material(name=xsdata_name + '.' + xs_ids[i]) + material.add_macroscopic(macroscopic) + materials.append(material) - materials_file = openmc.Materials(materials) + # Differentiate Geometry with new Material + if self.domain_type == 'material': + # Fill all appropriate Cells with new Material + for cell in all_cells: + if cell.fill.id == domain.id: + cell.fill = material - return (mgxs_file, materials_file) + elif self.domain_type == 'cell': + for cell in all_cells: + if cell.id == domain.id: + cell.fill = material + + return mgxs_file, materials, geometry def check_library_for_openmc_mgxs(self): """This routine will check the MGXS Types within a Library @@ -1092,8 +1113,9 @@ class Library(object): needed to support tallies the user may wish to request. - A nu-scatter matrix is required. + - Having a multiplicity matrix is preferred. - Having both nu-scatter (of any order) and scatter - (at least isotropic) matrices is preferred + (at least isotropic) matrices is the second choice. - If only nu-scatter, need total (not transport), to be used in adjusting absorption (i.e., reduced_abs = tot - nuscatt) @@ -1101,6 +1123,7 @@ class Library(object): See also -------- Library.create_mg_library() + Library.create_mg_mode() """ @@ -1116,9 +1139,10 @@ class Library(object): msg = '"nu-scatter matrix" MGXS type is required but not provided.' warn(msg) else: - # Ok, now see the status of scatter - if 'scatter matrix' not in self.mgxs_types: - # We dont have both nu-scatter and scatter, therefore + # Ok, now see the status of scatter and/or multiplicity + if ((('scatter matrix' not in self.mgxs_types) and + ('multiplicity matrix' not in self.mgxs_types))): + # We dont have data needed for multiplicity matrix, therefore # we need total, and not transport. if 'total' not in self.mgxs_types: error_flag = True diff --git a/openmc/mgxs/mgxs.py b/openmc/mgxs/mgxs.py index 5be84bb2ce..088db649fa 100644 --- a/openmc/mgxs/mgxs.py +++ b/openmc/mgxs/mgxs.py @@ -32,6 +32,8 @@ MGXS_TYPES = ['total', 'nu-scatter', 'scatter matrix', 'nu-scatter matrix', + 'multiplicity matrix', + 'nu-fission matrix', 'chi'] @@ -44,9 +46,9 @@ DOMAIN_TYPES = ['cell', # Supported domain classes # TODO: Implement Mesh domains -_DOMAINS = [openmc.Cell, - openmc.Universe, - openmc.Material] +_DOMAINS = (openmc.Cell, + openmc.Universe, + openmc.Material) class MGXS(object): @@ -55,7 +57,7 @@ class MGXS(object): This class can be used for both OpenMC input generation and tally data post-processing to compute spatially-homogenized and energy-integrated - multi-group cross sections for deterministic neutronics calculations. + multi-group cross sections for multi-group neutronics calculations. NOTE: Users should instantiate the subclasses of this abstract class. @@ -362,7 +364,7 @@ class MGXS(object): @domain.setter def domain(self, domain): - cv.check_type('domain', domain, tuple(_DOMAINS)) + cv.check_type('domain', domain, _DOMAINS) self._domain = domain # Assign a domain type @@ -425,7 +427,7 @@ class MGXS(object): Parameters ---------- - mgxs_type : {'total', 'transport', 'nu-transport', 'absorption', 'capture', 'fission', 'nu-fission', 'kappa-fission', 'scatter', 'nu-scatter', 'scatter matrix', 'nu-scatter matrix', 'chi'} + mgxs_type : {'total', 'transport', 'nu-transport', 'absorption', 'capture', 'fission', 'nu-fission', 'kappa-fission', 'scatter', 'nu-scatter', 'scatter matrix', 'nu-scatter matrix', 'multiplicity matrix', 'nu-fission matrix', chi'} The type of multi-group cross section object to return domain : openmc.Material or openmc.Cell or openmc.Universe The domain for spatial homogenization @@ -474,6 +476,10 @@ class MGXS(object): mgxs = ScatterMatrixXS(domain, domain_type, energy_groups) elif mgxs_type == 'nu-scatter matrix': mgxs = NuScatterMatrixXS(domain, domain_type, energy_groups) + elif mgxs_type == 'multiplicity matrix': + mgxs = MultiplicityMatrixXS(domain, domain_type, energy_groups) + elif mgxs_type == 'nu-fission matrix': + mgxs = NuFissionMatrixXS(domain, domain_type, energy_groups) elif mgxs_type == 'chi': mgxs = Chi(domain, domain_type, energy_groups) @@ -685,11 +691,11 @@ class MGXS(object): # Find, slice and store Tallies from StatePoint # The tally slicing is needed if tally merging was used for tally_type, tally in self.tallies.items(): - sp_tally = statepoint.get_tally(tally.scores, tally.filters, - tally.nuclides, - estimator=tally.estimator) - sp_tally = sp_tally.get_slice(tally.scores, filters, - filter_bins, tally.nuclides) + sp_tally = statepoint.get_tally( + tally.scores, tally.filters, tally.nuclides, + estimator=tally.estimator, exact_filters=True) + sp_tally = sp_tally.get_slice( + tally.scores, filters, filter_bins, tally.nuclides) sp_tally.sparse = self.sparse self.tallies[tally_type] = sp_tally @@ -721,9 +727,8 @@ class MGXS(object): Return the cross section indexed according to increasing or decreasing energy groups (decreasing or increasing energies). Defaults to 'increasing'. - value : str - A string for the type of value to return - 'mean', 'std_dev' or - 'rel_err' are accepted. Defaults to 'mean'. + value : {'mean', 'std_dev', 'rel_err'} + A string for the type of value to return. Defaults to 'mean'. Returns ------- @@ -957,8 +962,9 @@ class MGXS(object): Returns ------- openmc.mgxs.MGXS - A new tally which encapsulates the subset of data requested for the - nuclide(s) and/or energy group(s) requested in the parameters. + A new MGXS object which encapsulates the subset of data requested + for the nuclide(s) and/or energy group(s) requested in the + parameters. """ @@ -1521,12 +1527,18 @@ class MGXS(object): return df -class TotalXS(MGXS): - """A total multi-group cross section. +class MatrixMGXS(MGXS): + """An abstract multi-group cross section for some energy group structure + within some spatial domain. This class is specifically intended for + cross sections which depend on both the incoming and outgoing energy groups + and are therefore represented by matrices. Examples of this include the + scattering and nu-fission matrices. This class can be used for both OpenMC input generation and tally data post-processing to compute spatially-homogenized and energy-integrated - multi-group cross sections for deterministic neutronics calculations. + multi-group cross sections for multi-group neutronics calculations. + + NOTE: Users should instantiate the subclasses of this abstract class. Parameters ---------- @@ -1602,19 +1614,350 @@ class TotalXS(MGXS): """ - def __init__(self, domain=None, domain_type=None, - groups=None, by_nuclide=False, name=''): - super(TotalXS, self).__init__(domain, domain_type, - groups, by_nuclide, name) - self._rxn_type = 'total' + # This is an abstract class which cannot be instantiated + __metaclass__ = abc.ABCMeta + + @property + def filters(self): + # Create the non-domain specific Filters for the Tallies + group_edges = self.energy_groups.group_edges + energy = openmc.Filter('energy', group_edges) + energyout = openmc.Filter('energyout', group_edges) + + return [[energy], [energy, energyout]] + + @property + def estimator(self): + return 'analog' + + def get_xs(self, in_groups='all', out_groups='all', + subdomains='all', nuclides='all', + xs_type='macro', order_groups='increasing', + row_column='inout', value='mean', **kwargs): + """Returns an array of multi-group cross sections. + + This method constructs a 2D NumPy array for the requested multi-group + matrix data for one or more energy groups and subdomains. + + Parameters + ---------- + in_groups : Iterable of Integral or 'all' + Incoming energy groups of interest. Defaults to 'all'. + out_groups : Iterable of Integral or 'all' + Outgoing energy groups of interest. Defaults to 'all'. + subdomains : Iterable of Integral or 'all' + Subdomain IDs of interest. Defaults to 'all'. + nuclides : Iterable of str or 'all' or 'sum' + A list of nuclide name strings (e.g., ['U-235', 'U-238']). The + special string 'all' will return the cross sections for all + nuclides in the spatial domain. The special string 'sum' will + return the cross section summed over all nuclides. Defaults to + 'all'. + xs_type: {'macro', 'micro'} + Return the macro or micro cross section in units of cm^-1 or barns. + Defaults to 'macro'. + order_groups: {'increasing', 'decreasing'} + Return the cross section indexed according to increasing or + decreasing energy groups (decreasing or increasing energies). + Defaults to 'increasing'. + row_column: {'inout', 'outin'} + Return the cross section indexed first by incoming group and + second by outgoing group ('inout'), or vice versa ('outin'). + Defaults to 'inout'. + value : {'mean', 'std_dev', 'rel_err'} + A string for the type of value to return. Defaults to 'mean'. + + Returns + ------- + ndarray + A NumPy array of the multi-group cross section indexed in the order + each group and subdomain is listed in the parameters. + + Raises + ------ + ValueError + When this method is called before the multi-group cross section is + computed from tally data. + + """ + + cv.check_value('value', value, ['mean', 'std_dev', 'rel_err']) + cv.check_value('xs_type', xs_type, ['macro', 'micro']) + + filters = [] + filter_bins = [] + + # Construct a collection of the domain filter bins + if not isinstance(subdomains, basestring): + cv.check_iterable_type('subdomains', subdomains, Integral, + max_depth=2) + for subdomain in subdomains: + filters.append(self.domain_type) + filter_bins.append((subdomain,)) + + # Construct list of energy group bounds tuples for all requested groups + if not isinstance(in_groups, basestring): + cv.check_iterable_type('groups', in_groups, Integral) + for group in in_groups: + filters.append('energy') + filter_bins.append(( + self.energy_groups.get_group_bounds(group),)) + + # Construct list of energy group bounds tuples for all requested groups + if not isinstance(out_groups, basestring): + cv.check_iterable_type('groups', out_groups, Integral) + for group in out_groups: + filters.append('energyout') + filter_bins.append(( + self.energy_groups.get_group_bounds(group),)) + + # Construct a collection of the nuclides to retrieve from the xs tally + if self.by_nuclide: + if nuclides == 'all' or nuclides == 'sum' or nuclides == ['sum']: + query_nuclides = self.get_all_nuclides() + else: + query_nuclides = nuclides + else: + query_nuclides = ['total'] + + # Use tally summation if user requested the sum for all nuclides + if nuclides == 'sum' or nuclides == ['sum']: + xs_tally = self.xs_tally.summation(nuclides=query_nuclides) + xs = xs_tally.get_values(filters=filters, filter_bins=filter_bins, + value=value) + else: + xs = self.xs_tally.get_values(filters=filters, + filter_bins=filter_bins, + nuclides=query_nuclides, value=value) + + xs = np.nan_to_num(xs) + + # Divide by atom number densities for microscopic cross sections + if xs_type == 'micro': + if self.by_nuclide: + densities = self.get_nuclide_densities(nuclides) + else: + densities = self.get_nuclide_densities('sum') + if value == 'mean' or value == 'std_dev': + xs /= densities[np.newaxis, :, np.newaxis] + + # Reverse data if user requested increasing energy groups since + # tally data is stored in order of increasing energies + if order_groups == 'increasing': + if in_groups == 'all': + num_in_groups = self.num_groups + else: + num_in_groups = len(in_groups) + if out_groups == 'all': + num_out_groups = self.num_groups + else: + num_out_groups = len(out_groups) + + # Reshape tally data array with separate axes for domain and energy + num_subdomains = int(xs.shape[0] / + (num_in_groups * num_out_groups)) + new_shape = (num_subdomains, num_in_groups, num_out_groups) + new_shape += xs.shape[1:] + xs = np.reshape(xs, new_shape) + + # Transpose the matrix if requested by user + if row_column == 'outin': + xs = np.swapaxes(xs, 1, 2) + + # Reverse energies to align with increasing energy groups + xs = xs[:, ::-1, ::-1, :] + + # Eliminate trivial dimensions + xs = np.squeeze(xs) + xs = np.atleast_2d(xs) + + return xs + + def get_slice(self, nuclides=[], in_groups=[], out_groups=[]): + """Build a sliced MatrixMGXS object for the specified nuclides and + energy groups. + + This method constructs a new MGXS to encapsulate a subset of the data + represented by this MGXS. The subset of data to include in the tally + slice is determined by the nuclides and energy groups specified in + the input parameters. + + Parameters + ---------- + nuclides : list of str + A list of nuclide name strings + (e.g., ['U-235', 'U-238']; default is []) + in_groups : list of int + A list of incoming energy group indices starting at 1 for the high + energies (e.g., [1, 2, 3]; default is []) + out_groups : list of int + A list of outgoing energy group indices starting at 1 for the high + energies (e.g., [1, 2, 3]; default is []) + + Returns + ------- + openmc.mgxs.MatrixMGXS + A new MatrixMGXS object which encapsulates the subset of data + requested for the nuclide(s) and/or energy group(s) requested in + the parameters. + + """ + + # Call super class method and null out derived tallies + slice_xs = super(MatrixMGXS, self).get_slice(nuclides, in_groups) + slice_xs._rxn_rate_tally = None + slice_xs._xs_tally = None + + # Slice outgoing energy groups if needed + if len(out_groups) != 0: + filter_bins = [] + for group in out_groups: + group_bounds = self.energy_groups.get_group_bounds(group) + filter_bins.append(group_bounds) + filter_bins = [tuple(filter_bins)] + + # Slice each of the tallies across energyout groups + for tally_type, tally in slice_xs.tallies.items(): + if tally.contains_filter('energyout'): + tally_slice = tally.get_slice(filters=['energyout'], + filter_bins=filter_bins) + slice_xs.tallies[tally_type] = tally_slice + + slice_xs.sparse = self.sparse + return slice_xs + + def print_xs(self, subdomains='all', nuclides='all', xs_type='macro'): + """Prints a string representation for the multi-group cross section. + + Parameters + ---------- + subdomains : Iterable of Integral or 'all' + The subdomain IDs of the cross sections to include in the report. + Defaults to 'all'. + nuclides : Iterable of str or 'all' or 'sum' + The nuclides of the cross-sections to include in the report. This + may be a list of nuclide name strings (e.g., ['U-235', 'U-238']). + The special string 'all' will report the cross sections for all + nuclides in the spatial domain. The special string 'sum' will + report the cross sections summed over all nuclides. Defaults to + 'all'. + xs_type: {'macro', 'micro'} + Return the macro or micro cross section in units of cm^-1 or barns. + Defaults to 'macro'. + + """ + + # Construct a collection of the subdomains to report + if not isinstance(subdomains, basestring): + cv.check_iterable_type('subdomains', subdomains, Integral) + elif self.domain_type == 'distribcell': + subdomains = np.arange(self.num_subdomains, dtype=np.int) + else: + subdomains = [self.domain.id] + + # Construct a collection of the nuclides to report + if self.by_nuclide: + if nuclides == 'all': + nuclides = self.get_all_nuclides() + if nuclides == 'sum': + nuclides = ['sum'] + else: + cv.check_iterable_type('nuclides', nuclides, basestring) + else: + nuclides = ['sum'] + + cv.check_value('xs_type', xs_type, ['macro', 'micro']) + + # Build header for string with type and domain info + string = 'Multi-Group XS\n' + string += '{0: <16}=\t{1}\n'.format('\tReaction Type', self.rxn_type) + string += '{0: <16}=\t{1}\n'.format('\tDomain Type', self.domain_type) + string += '{0: <16}=\t{1}\n'.format('\tDomain ID', self.domain.id) + + # If cross section data has not been computed, only print string header + if self.tallies is None: + print(string) + return + + string += '{0: <16}\n'.format('\tEnergy Groups:') + template = '{0: <12}Group {1} [{2: <10} - {3: <10}MeV]\n' + + # Loop over energy groups ranges + for group in range(1, self.num_groups + 1): + bounds = self.energy_groups.get_group_bounds(group) + string += template.format('', group, bounds[0], bounds[1]) + + # Loop over all subdomains + for subdomain in subdomains: + + if self.domain_type == 'distribcell': + string += \ + '{0: <16}=\t{1}\n'.format('\tSubdomain', subdomain) + + # Loop over all Nuclides + for nuclide in nuclides: + + # Build header for nuclide type + if xs_type != 'sum': + string += '{0: <16}=\t{1}\n'.format('\tNuclide', nuclide) + + # Build header for cross section type + if xs_type == 'macro': + string += '{0: <16}\n'.format('\tCross Sections [cm^-1]:') + else: + string += '{0: <16}\n'.format('\tCross Sections [barns]:') + + template = '{0: <12}Group {1} -> Group {2}:\t\t' + + # Loop over incoming/outgoing energy groups ranges + for in_group in range(1, self.num_groups + 1): + for out_group in range(1, self.num_groups + 1): + string += template.format('', in_group, out_group) + average = \ + self.get_xs([in_group], [out_group], + [subdomain], [nuclide], + xs_type=xs_type, value='mean') + rel_err = \ + self.get_xs([in_group], [out_group], + [subdomain], [nuclide], + xs_type=xs_type, value='rel_err') + average = average.flatten()[0] + rel_err = rel_err.flatten()[0] * 100. + string += '{:1.2e} +/- {:1.2e}%'.format(average, + rel_err) + string += '\n' + string += '\n' + string += '\n' + string += '\n' + + print(string) -class TransportXS(MGXS): - """A transport-corrected total multi-group cross section. +class TotalXS(MGXS): + r"""A total multi-group cross section. This class can be used for both OpenMC input generation and tally data post-processing to compute spatially-homogenized and energy-integrated - multi-group cross sections for deterministic neutronics calculations. + multi-group total cross sections for multi-group neutronics calculations. At + a minimum, one needs to set the :attr:`TotalXS.energy_groups` and + :attr:`TotalXS.domain` properties. Tallies for the flux and appropriate + reaction rates over the specified domain are generated automatically via the + :attr:`TotalXS.tallies` property, which can then be appended to a + :class:`openmc.Tallies` instance. + + For post-processing, the :meth:`MGXS.load_from_statepoint` will pull in the + necessary data to compute multi-group cross sections from a + :class:`openmc.StatePoint` instance. The derived multi-group cross section + can then be obtained from the :attr:`TotalXS.xs_tally` property. + + For a spatial domain :math:`V` and energy group :math:`[E_g,E_{g-1}]`, the + total cross section is calculated as: + + .. math:: + + \frac{\int_{r \in V} dr \int_{4\pi} d\Omega \int_{E_g}^{E_{g-1}} dE \; + \sigma_t (r, E) \psi (r, E, \Omega)}{\int_{r \in V} dr \int_{4\pi} + d\Omega \int_{E_g}^{E_{g-1}} dE \; \psi (r, E, \Omega)}. Parameters ---------- @@ -1622,7 +1965,7 @@ class TransportXS(MGXS): The domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe'} The domain type for spatial homogenization - energy_groups : openmc.mgxs.EnergyGroups + groups : openmc.mgxs.EnergyGroups The energy group structure for energy condensation by_nuclide : bool If true, computes cross sections for each nuclide in domain @@ -1657,7 +2000,127 @@ class TransportXS(MGXS): estimator : {'tracklength', 'analog'} The tally estimator used to compute the multi-group cross section tallies : collections.OrderedDict - OpenMC tallies needed to compute the multi-group cross section + OpenMC tallies needed to compute the multi-group cross section. The keys + are strings listed in the :attr:`TotalXS.tally_keys` property and values + are instances of :class:`openmc.Tally`. + rxn_rate_tally : openmc.Tally + Derived tally for the reaction rate tally used in the numerator to + compute the multi-group cross section. This attribute is None + unless the multi-group cross section has been computed. + xs_tally : openmc.Tally + Derived tally for the multi-group cross section. This attribute + is None unless the multi-group cross section has been computed. + num_subdomains : int + The number of subdomains is unity for 'material', 'cell' and 'universe' + domain types. When the This is equal to the number of cell instances + for 'distribcell' domain types (it is equal to unity prior to loading + tally data from a statepoint file). + num_nuclides : int + The number of nuclides for which the multi-group cross section is + being tracked. This is unity if the by_nuclide attribute is False. + nuclides : Iterable of str or 'sum' + The optional user-specified nuclides for which to compute cross + sections (e.g., 'U-238', 'O-16'). If by_nuclide is True but nuclides + are not specified by the user, all nuclides in the spatial domain + are included. This attribute is 'sum' if by_nuclide is false. + sparse : bool + Whether or not the MGXS' tallies use SciPy's LIL sparse matrix format + for compressed data storage + loaded_sp : bool + Whether or not a statepoint file has been loaded with tally data + derived : bool + Whether or not the MGXS is merged from one or more other MGXS + hdf5_key : str + The key used to index multi-group cross sections in an HDF5 data store + + """ + + def __init__(self, domain=None, domain_type=None, + groups=None, by_nuclide=False, name=''): + super(TotalXS, self).__init__(domain, domain_type, + groups, by_nuclide, name) + self._rxn_type = 'total' + + +class TransportXS(MGXS): + r"""A transport-corrected total multi-group cross section. + + This class can be used for both OpenMC input generation and tally data + post-processing to compute spatially-homogenized and energy-integrated + multi-group cross sections for multi-group neutronics calculations. At a + minimum, one needs to set the :attr:`TransportXS.energy_groups` and + :attr:`TransportXS.domain` properties. Tallies for the flux and appropriate + reaction rates over the specified domain are generated automatically via the + :attr:`TransportXS.tallies` property, which can then be appended to a + :class:`openmc.Tallies` instance. + + For post-processing, the :meth:`MGXS.load_from_statepoint` will pull in the + necessary data to compute multi-group cross sections from a + :class:`openmc.StatePoint` instance. The derived multi-group cross section + can then be obtained from the :attr:`TransportXS.xs_tally` property. + + For a spatial domain :math:`V` and energy group :math:`[E_g,E_{g-1}]`, the + transport-corrected total cross section is calculated as: + + .. math:: + + \langle \sigma_t \phi \rangle &= \int_{r \in V} dr \int_{4\pi} + d\Omega \int_{E_g}^{E_{g-1}} dE \sigma_t (r, E) \psi + (r, E, \Omega) \\ + \langle \sigma_{s1} \phi \rangle &= \int_{r \in V} dr + \int_{4\pi} d\Omega \int_{E_g}^{E_{g-1}} dE \int_{4\pi} + d\Omega' \int_0^\infty dE' \int_{-1}^1 d\mu \; \mu \sigma_s + (r, E' \rightarrow E, \Omega' \cdot \Omega) + \phi (r, E', \Omega) \\ + \langle \phi \rangle &= \int_{r \in V} dr \int_{4\pi} d\Omega + \int_{E_g}^{E_{g-1}} dE \; \psi (r, E, \Omega) \\ + \sigma_{tr} &= \frac{\langle \sigma_t \phi \rangle - \langle \sigma_{s1} + \phi \rangle}{\langle \phi \rangle} + + Parameters + ---------- + domain : openmc.Material or openmc.Cell or openmc.Universe + The domain for spatial homogenization + domain_type : {'material', 'cell', 'distribcell', 'universe'} + The domain type for spatial homogenization + groups : openmc.mgxs.EnergyGroups + The energy group structure for energy condensation + by_nuclide : bool + If true, computes cross sections for each nuclide in domain + name : str, optional + Name of the multi-group cross section. Used as a label to identify + tallies in OpenMC 'tallies.xml' file. + + Attributes + ---------- + name : str, optional + Name of the multi-group cross section + rxn_type : str + Reaction type (e.g., 'total', 'nu-fission', etc.) + by_nuclide : bool + If true, computes cross sections for each nuclide in domain + domain : Material or Cell or Universe + Domain for spatial homogenization + domain_type : {'material', 'cell', 'distribcell', 'universe'} + Domain type for spatial homogenization + energy_groups : openmc.mgxs.EnergyGroups + Energy group structure for energy condensation + tally_trigger : openmc.Trigger + An (optional) tally precision trigger given to each tally used to + compute the cross section + scores : list of str + The scores in each tally used to compute the multi-group cross section + filters : list of openmc.Filter + The filters in each tally used to compute the multi-group cross section + tally_keys : list of str + The keys into the tallies dictionary for each tally used to compute + the multi-group cross section + estimator : {'tracklength', 'analog'} + The tally estimator used to compute the multi-group cross section + tallies : collections.OrderedDict + OpenMC tallies needed to compute the multi-group cross section. The keys + are strings listed in the :attr:`TransportXS.tally_keys` property and + values are instances of :class:`openmc.Tally`. rxn_rate_tally : openmc.Tally Derived tally for the reaction rate tally used in the numerator to compute the multi-group cross section. This attribute is None @@ -1723,12 +2186,26 @@ class TransportXS(MGXS): class NuTransportXS(TransportXS): - """A transport-corrected total multi-group cross section which + r"""A transport-corrected total multi-group cross section which accounts for neutron multiplicity in scattering reactions. This class can be used for both OpenMC input generation and tally data post-processing to compute spatially-homogenized and energy-integrated - multi-group cross sections for deterministic neutronics calculations. + multi-group cross sections for multi-group neutronics calculations. At a + minimum, one needs to set the :attr:`NuTransportXS.energy_groups` and + :attr:`NuTransportXS.domain` properties. Tallies for the flux and + appropriate reaction rates over the specified domain are generated + automatically via the :attr:`NuTransportXS.tallies` property, which can then + be appended to a :class:`openmc.Tallies` instance. + + For post-processing, the :meth:`MGXS.load_from_statepoint` will pull in the + necessary data to compute multi-group cross sections from a + :class:`openmc.StatePoint` instance. The derived multi-group cross section + can then be obtained from the :attr:`NuTransportXS.xs_tally` property. + + The calculation of the transport-corrected cross section is the same as that + for :class:`TransportXS` except that the scattering multiplicity is + accounted for. Parameters ---------- @@ -1736,7 +2213,7 @@ class NuTransportXS(TransportXS): The domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe'} The domain type for spatial homogenization - energy_groups : openmc.mgxs.EnergyGroups + groups : openmc.mgxs.EnergyGroups The energy group structure for energy condensation by_nuclide : bool If true, computes cross sections for each nuclide in domain @@ -1771,7 +2248,9 @@ class NuTransportXS(TransportXS): estimator : {'tracklength', 'analog'} The tally estimator used to compute the multi-group cross section tallies : collections.OrderedDict - OpenMC tallies needed to compute the multi-group cross section + OpenMC tallies needed to compute the multi-group cross section. The keys + are strings listed in the :attr:`NuTransportXS.tally_keys` property and + values are instances of :class:`openmc.Tally`. rxn_rate_tally : openmc.Tally Derived tally for the reaction rate tally used in the numerator to compute the multi-group cross section. This attribute is None @@ -1820,11 +2299,34 @@ class NuTransportXS(TransportXS): class AbsorptionXS(MGXS): - """An absorption multi-group cross section. + r"""An absorption multi-group cross section. + + Absorption is defined as all reactions that do not produce secondary + neutrons (disappearance) plus fission reactions. This class can be used for both OpenMC input generation and tally data post-processing to compute spatially-homogenized and energy-integrated - multi-group cross sections for deterministic neutronics calculations. + multi-group absorption cross sections for multi-group neutronics + calculations. At a minimum, one needs to set the + :attr:`AbsorptionXS.energy_groups` and :attr:`AbsorptionXS.domain` + properties. Tallies for the flux and appropriate reaction rates over the + specified domain are generated automatically via the + :attr:`AbsorptionXS.tallies` property, which can then be appended to a + :class:`openmc.Tallies` instance. + + For post-processing, the :meth:`MGXS.load_from_statepoint` will pull in the + necessary data to compute multi-group cross sections from a + :class:`openmc.StatePoint` instance. The derived multi-group cross section + can then be obtained from the :attr:`AbsorptionXS.xs_tally` property. + + For a spatial domain :math:`V` and energy group :math:`[E_g,E_{g-1}]`, the + absorption cross section is calculated as: + + .. math:: + + \frac{\int_{r \in V} dr \int_{4\pi} d\Omega \int_{E_g}^{E_{g-1}} dE \; + \sigma_a (r, E) \psi (r, E, \Omega)}{\int_{r \in V} dr \int_{4\pi} + d\Omega \int_{E_g}^{E_{g-1}} dE \; \psi (r, E, \Omega)}. Parameters ---------- @@ -1832,7 +2334,7 @@ class AbsorptionXS(MGXS): The domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe'} The domain type for spatial homogenization - energy_groups : openmc.mgxs.EnergyGroups + groups : openmc.mgxs.EnergyGroups The energy group structure for energy condensation by_nuclide : bool If true, computes cross sections for each nuclide in domain @@ -1867,7 +2369,9 @@ class AbsorptionXS(MGXS): estimator : {'tracklength', 'analog'} The tally estimator used to compute the multi-group cross section tallies : collections.OrderedDict - OpenMC tallies needed to compute the multi-group cross section + OpenMC tallies needed to compute the multi-group cross section. The keys + are strings listed in the :attr:`AbsorptionXS.tally_keys` property and + values are instances of :class:`openmc.Tally`. rxn_rate_tally : openmc.Tally Derived tally for the reaction rate tally used in the numerator to compute the multi-group cross section. This attribute is None @@ -1908,16 +2412,37 @@ class AbsorptionXS(MGXS): class CaptureXS(MGXS): - """A capture multi-group cross section. + r"""A capture multi-group cross section. The neutron capture reaction rate is defined as the difference between OpenMC's 'absorption' and 'fission' reaction rate score types. This includes not only radiative capture, but all forms of neutron disappearance aside - from fission (e.g., MT > 100). + from fission (i.e., MT > 100). This class can be used for both OpenMC input generation and tally data post-processing to compute spatially-homogenized and energy-integrated - multi-group cross sections for deterministic neutronics calculations. + multi-group capture cross sections for multi-group neutronics + calculations. At a minimum, one needs to set the + :attr:`CaptureXS.energy_groups` and :attr:`CaptureXS.domain` + properties. Tallies for the flux and appropriate reaction rates over the + specified domain are generated automatically via the + :attr:`CaptureXS.tallies` property, which can then be appended to a + :class:`openmc.Tallies` instance. + + For post-processing, the :meth:`MGXS.load_from_statepoint` will pull in the + necessary data to compute multi-group cross sections from a + :class:`openmc.StatePoint` instance. The derived multi-group cross section + can then be obtained from the :attr:`CaptureXS.xs_tally` property. + + For a spatial domain :math:`V` and energy group :math:`[E_g,E_{g-1}]`, the + capture cross section is calculated as: + + .. math:: + + \frac{\int_{r \in V} dr \int_{4\pi} d\Omega \int_{E_g}^{E_{g-1}} dE \; + \left [ \sigma_a (r, E) \psi (r, E, \Omega) - \sigma_f (r, E) \psi (r, E, + \Omega) \right ]}{\int_{r \in V} dr \int_{4\pi} d\Omega + \int_{E_g}^{E_{g-1}} dE \; \psi (r, E, \Omega)}. Parameters ---------- @@ -1925,7 +2450,7 @@ class CaptureXS(MGXS): The domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe'} The domain type for spatial homogenization - energy_groups : openmc.mgxs.EnergyGroups + groups : openmc.mgxs.EnergyGroups The energy group structure for energy condensation by_nuclide : bool If true, computes cross sections for each nuclide in domain @@ -1960,7 +2485,9 @@ class CaptureXS(MGXS): estimator : {'tracklength', 'analog'} The tally estimator used to compute the multi-group cross section tallies : collections.OrderedDict - OpenMC tallies needed to compute the multi-group cross section + OpenMC tallies needed to compute the multi-group cross section. The keys + are strings listed in the :attr:`CaptureXS.tally_keys` property and + values are instances of :class:`openmc.Tally`. rxn_rate_tally : openmc.Tally Derived tally for the reaction rate tally used in the numerator to compute the multi-group cross section. This attribute is None @@ -2013,11 +2540,31 @@ class CaptureXS(MGXS): class FissionXS(MGXS): - """A fission multi-group cross section. + r"""A fission multi-group cross section. This class can be used for both OpenMC input generation and tally data post-processing to compute spatially-homogenized and energy-integrated - multi-group cross sections for deterministic neutronics calculations. + multi-group fission cross sections for multi-group neutronics + calculations. At a minimum, one needs to set the + :attr:`FissionXS.energy_groups` and :attr:`FissionXS.domain` + properties. Tallies for the flux and appropriate reaction rates over the + specified domain are generated automatically via the + :attr:`FissionXS.tallies` property, which can then be appended to a + :class:`openmc.Tallies` instance. + + For post-processing, the :meth:`MGXS.load_from_statepoint` will pull in the + necessary data to compute multi-group cross sections from a + :class:`openmc.StatePoint` instance. The derived multi-group cross section + can then be obtained from the :attr:`FissionXS.xs_tally` property. + + For a spatial domain :math:`V` and energy group :math:`[E_g,E_{g-1}]`, the + fission cross section is calculated as: + + .. math:: + + \frac{\int_{r \in V} dr \int_{4\pi} d\Omega \int_{E_g}^{E_{g-1}} dE \; + \sigma_f (r, E) \psi (r, E, \Omega)}{\int_{r \in V} dr \int_{4\pi} + d\Omega \int_{E_g}^{E_{g-1}} dE \; \psi (r, E, \Omega)}. Parameters ---------- @@ -2025,7 +2572,7 @@ class FissionXS(MGXS): The domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe'} The domain type for spatial homogenization - energy_groups : openmc.mgxs.EnergyGroups + groups : openmc.mgxs.EnergyGroups The energy group structure for energy condensation by_nuclide : bool If true, computes cross sections for each nuclide in domain @@ -2060,7 +2607,9 @@ class FissionXS(MGXS): estimator : {'tracklength', 'analog'} The tally estimator used to compute the multi-group cross section tallies : collections.OrderedDict - OpenMC tallies needed to compute the multi-group cross section + OpenMC tallies needed to compute the multi-group cross section. The keys + are strings listed in the :attr:`FissionXS.tally_keys` property and + values are instances of :class:`openmc.Tally`. rxn_rate_tally : openmc.Tally Derived tally for the reaction rate tally used in the numerator to compute the multi-group cross section. This attribute is None @@ -2101,11 +2650,32 @@ class FissionXS(MGXS): class NuFissionXS(MGXS): - """A fission production multi-group cross section. + r"""A fission neutron production multi-group cross section. This class can be used for both OpenMC input generation and tally data post-processing to compute spatially-homogenized and energy-integrated - multi-group cross sections for deterministic neutronics calculations. + multi-group fission neutron production cross sections for multi-group + neutronics calculations. At a minimum, one needs to set the + :attr:`NuFissionXS.energy_groups` and :attr:`NuFissionXS.domain` + properties. Tallies for the flux and appropriate reaction rates over the + specified domain are generated automatically via the + :attr:`NuFissionXS.tallies` property, which can then be appended to a + :class:`openmc.Tallies` instance. + + For post-processing, the :meth:`MGXS.load_from_statepoint` will pull in the + necessary data to compute multi-group cross sections from a + :class:`openmc.StatePoint` instance. The derived multi-group cross section + can then be obtained from the :attr:`NuFissionXS.xs_tally` property. + + For a spatial domain :math:`V` and energy group :math:`[E_g,E_{g-1}]`, the + fission neutron production cross section is calculated as: + + .. math:: + + \frac{\int_{r \in V} dr \int_{4\pi} d\Omega \int_{E_g}^{E_{g-1}} dE \; + \nu\sigma_f (r, E) \psi (r, E, \Omega)}{\int_{r \in V} dr \int_{4\pi} + d\Omega \int_{E_g}^{E_{g-1}} dE \; \psi (r, E, \Omega)}. + Parameters ---------- @@ -2113,7 +2683,7 @@ class NuFissionXS(MGXS): The domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe'} The domain type for spatial homogenization - energy_groups : openmc.mgxs.EnergyGroups + groups : openmc.mgxs.EnergyGroups The energy group structure for energy condensation by_nuclide : bool If true, computes cross sections for each nuclide in domain @@ -2148,7 +2718,9 @@ class NuFissionXS(MGXS): estimator : {'tracklength', 'analog'} The tally estimator used to compute the multi-group cross section tallies : collections.OrderedDict - OpenMC tallies needed to compute the multi-group cross section + OpenMC tallies needed to compute the multi-group cross section. The keys + are strings listed in the :attr:`NuFissionXS.tally_keys` property and + values are instances of :class:`openmc.Tally`. rxn_rate_tally : openmc.Tally Derived tally for the reaction rate tally used in the numerator to compute the multi-group cross section. This attribute is None @@ -2189,11 +2761,37 @@ class NuFissionXS(MGXS): class KappaFissionXS(MGXS): - """A recoverable fission energy production rate multi-group cross section. + r"""A recoverable fission energy production rate multi-group cross section. + + The recoverable energy per fission, :math:`\kappa`, is defined as the + fission product kinetic energy, prompt and delayed neutron kinetic energies, + prompt and delayed :math:`\gamma`-ray total energies, and the total energy + released by the delayed :math:`\beta` particles. The neutrino energy does + not contribute to this response. The prompt and delayed :math:`\gamma`-rays + are assumed to deposit their energy locally. This class can be used for both OpenMC input generation and tally data post-processing to compute spatially-homogenized and energy-integrated - multi-group cross sections for deterministic neutronics calculations. + multi-group cross sections for multi-group neutronics calculations. At a + minimum, one needs to set the :attr:`KappaFissionXS.energy_groups` and + :attr:`KappaFissionXS.domain` properties. Tallies for the flux and appropriate + reaction rates over the specified domain are generated automatically via the + :attr:`KappaFissionXS.tallies` property, which can then be appended to a + :class:`openmc.Tallies` instance. + + For post-processing, the :meth:`MGXS.load_from_statepoint` will pull in the + necessary data to compute multi-group cross sections from a + :class:`openmc.StatePoint` instance. The derived multi-group cross section + can then be obtained from the :attr:`KappaFissionXS.xs_tally` property. + + For a spatial domain :math:`V` and energy group :math:`[E_g,E_{g-1}]`, the + recoverable fission energy production rate cross section is calculated as: + + .. math:: + + \frac{\int_{r \in V} dr \int_{4\pi} d\Omega \int_{E_g}^{E_{g-1}} dE \; + \kappa\sigma_f (r, E) \psi (r, E, \Omega)}{\int_{r \in V} dr \int_{4\pi} + d\Omega \int_{E_g}^{E_{g-1}} dE \; \psi (r, E, \Omega)}. Parameters ---------- @@ -2201,7 +2799,7 @@ class KappaFissionXS(MGXS): The domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe'} The domain type for spatial homogenization - energy_groups : openmc.mgxs.EnergyGroups + groups : openmc.mgxs.EnergyGroups The energy group structure for energy condensation by_nuclide : bool If true, computes cross sections for each nuclide in domain @@ -2236,7 +2834,9 @@ class KappaFissionXS(MGXS): estimator : {'tracklength', 'analog'} The tally estimator used to compute the multi-group cross section tallies : collections.OrderedDict - OpenMC tallies needed to compute the multi-group cross section + OpenMC tallies needed to compute the multi-group cross section. The keys + are strings listed in the :attr:`KappaFissionXS.tally_keys` property and + values are instances of :class:`openmc.Tally`. rxn_rate_tally : openmc.Tally Derived tally for the reaction rate tally used in the numerator to compute the multi-group cross section. This attribute is None @@ -2277,11 +2877,34 @@ class KappaFissionXS(MGXS): class ScatterXS(MGXS): - """A scatter multi-group cross section. + r"""A scattering multi-group cross section. + + The scattering cross section is defined as the difference between the total + and absorption cross sections. This class can be used for both OpenMC input generation and tally data post-processing to compute spatially-homogenized and energy-integrated - multi-group cross sections for deterministic neutronics calculations. + multi-group cross sections for multi-group neutronics calculations. At a + minimum, one needs to set the :attr:`ScatterXS.energy_groups` and + :attr:`ScatterXS.domain` properties. Tallies for the flux and + appropriate reaction rates over the specified domain are generated + automatically via the :attr:`ScatterXS.tallies` property, which can + then be appended to a :class:`openmc.Tallies` instance. + + For post-processing, the :meth:`MGXS.load_from_statepoint` will pull in the + necessary data to compute multi-group cross sections from a + :class:`openmc.StatePoint` instance. The derived multi-group cross section + can then be obtained from the :attr:`ScatterXS.xs_tally` property. + + For a spatial domain :math:`V` and energy group :math:`[E_g,E_{g-1}]`, the + scattering cross section is calculated as: + + .. math:: + + \frac{\int_{r \in V} dr \int_{4\pi} d\Omega \int_{E_g}^{E_{g-1}} dE \; + \left [ \sigma_t (r, E) \psi (r, E, \Omega) - \sigma_a (r, E) \psi (r, E, + \Omega) \right ]}{\int_{r \in V} dr \int_{4\pi} d\Omega + \int_{E_g}^{E_{g-1}} dE \; \psi (r, E, \Omega)}. Parameters ---------- @@ -2289,7 +2912,7 @@ class ScatterXS(MGXS): The domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe'} The domain type for spatial homogenization - energy_groups : openmc.mgxs.EnergyGroups + groups : openmc.mgxs.EnergyGroups The energy group structure for energy condensation by_nuclide : bool If true, computes cross sections for each nuclide in domain @@ -2324,7 +2947,9 @@ class ScatterXS(MGXS): estimator : {'tracklength', 'analog'} The tally estimator used to compute the multi-group cross section tallies : collections.OrderedDict - OpenMC tallies needed to compute the multi-group cross section + OpenMC tallies needed to compute the multi-group cross section. The keys + are strings listed in the :attr:`ScatterXS.tally_keys` property and + values are instances of :class:`openmc.Tally`. rxn_rate_tally : openmc.Tally Derived tally for the reaction rate tally used in the numerator to compute the multi-group cross section. This attribute is None @@ -2365,11 +2990,36 @@ class ScatterXS(MGXS): class NuScatterXS(MGXS): - """A nu-scatter multi-group cross section. + r"""A scattering neutron production multi-group cross section. + + The neutron production from scattering is defined as the average number of + neutrons produced from all neutron-producing reactions except for fission. This class can be used for both OpenMC input generation and tally data post-processing to compute spatially-homogenized and energy-integrated - multi-group cross sections for deterministic neutronics calculations. + multi-group cross sections for multi-group neutronics calculations. At a + minimum, one needs to set the :attr:`NuScatterXS.energy_groups` and + :attr:`NuScatterXS.domain` properties. Tallies for the flux and appropriate + reaction rates over the specified domain are generated automatically via the + :attr:`NuScatterXS.tallies` property, which can then be appended to a + :class:`openmc.Tallies` instance. + + For post-processing, the :meth:`MGXS.load_from_statepoint` will pull in the + necessary data to compute multi-group cross sections from a + :class:`openmc.StatePoint` instance. The derived multi-group cross section + can then be obtained from the :attr:`NuScatterXS.xs_tally` property. + + For a spatial domain :math:`V` and energy group :math:`[E_g,E_{g-1}]`, the + scattering neutron production cross section is calculated as: + + .. math:: + + \frac{\int_{r \in V} dr \int_{4\pi} d\Omega \int_{E_g}^{E_{g-1}} dE \; + \sum_i \upsilon_i \sigma_i (r, E) \psi (r, E, \Omega)}{\int_{r \in V} dr + \int_{4\pi} d\Omega \int_{E_g}^{E_{g-1}} dE \; \psi (r, E, \Omega)}. + + where :math:`\upsilon_i` is the multiplicity of the :math:`i`-th scattering + reaction. Parameters ---------- @@ -2377,7 +3027,7 @@ class NuScatterXS(MGXS): The domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe'} The domain type for spatial homogenization - energy_groups : openmc.mgxs.EnergyGroups + groups : openmc.mgxs.EnergyGroups The energy group structure for energy condensation by_nuclide : bool If true, computes cross sections for each nuclide in domain @@ -2412,7 +3062,9 @@ class NuScatterXS(MGXS): estimator : {'tracklength', 'analog'} The tally estimator used to compute the multi-group cross section tallies : collections.OrderedDict - OpenMC tallies needed to compute the multi-group cross section + OpenMC tallies needed to compute the multi-group cross section. The keys + are strings listed in the :attr:`NuScatterXS.tally_keys` property and + values are instances of :class:`openmc.Tally`. rxn_rate_tally : openmc.Tally Derived tally for the reaction rate tally used in the numerator to compute the multi-group cross section. This attribute is None @@ -2451,14 +3103,53 @@ class NuScatterXS(MGXS): groups, by_nuclide, name) self._rxn_type = 'nu-scatter' + @property + def estimator(self): + return 'analog' -class ScatterMatrixXS(MGXS): - """A scattering matrix multi-group cross section for one or more Legendre + +class ScatterMatrixXS(MatrixMGXS): + r"""A scattering matrix multi-group cross section for one or more Legendre moments. This class can be used for both OpenMC input generation and tally data post-processing to compute spatially-homogenized and energy-integrated - multi-group cross sections for deterministic neutronics calculations. + multi-group cross sections for multi-group neutronics calculations. At a + minimum, one needs to set the :attr:`ScatterMatrixXS.energy_groups` and + :attr:`ScatterMatrixXS.domain` properties. Tallies for the flux and + appropriate reaction rates over the specified domain are generated + automatically via the :attr:`ScatterMatrixXS.tallies` property, which can + then be appended to a :class:`openmc.Tallies` instance. + + For post-processing, the :meth:`MGXS.load_from_statepoint` will pull in the + necessary data to compute multi-group cross sections from a + :class:`openmc.StatePoint` instance. The derived multi-group cross section + can then be obtained from the :attr:`ScatterMatrixXS.xs_tally` property. + + For a spatial domain :math:`V`, incoming energy group + :math:`[E_{g'},E_{g'-1}]`, and outgoing energy group :math:`[E_g,E_{g-1}]`, + the scattering moments are calculated as: + + .. math:: + + \langle \sigma_{s,\ell,g'\rightarrow g} \phi \rangle &= \int_{r \in V} dr + \int_{4\pi} d\Omega' \int_{E_{g'}}^{E_{g'-1}} dE' \int_{4\pi} d\Omega + \int_{E_g}^{E_{g-1}} dE \; P_\ell (\Omega \cdot \Omega') \sigma_s (r, E' + \rightarrow E, \Omega' \cdot \Omega) \psi(r, E', \Omega')\\ + \langle \phi \rangle &= \int_{r \in V} dr \int_{4\pi} d\Omega + \int_{E_g}^{E_{g-1}} dE \; \psi (r, E, \Omega) \\ + \sigma_{s,\ell,g'\rightarrow g} &= \frac{\langle + \sigma_{s,\ell,g'\rightarrow g} \phi \rangle}{\langle \phi \rangle} + + If the order is zero and a :math:`P_0` transport-correction is applied + (default), the scattering matrix elements are: + + .. math:: + + \sigma_{s,g'\rightarrow g} = \frac{\langle \sigma_{s,0,g'\rightarrow g} + \phi \rangle - \delta_{gg'} \sum_{g''} \langle \sigma_{s,1,g''\rightarrow + g} \phi \rangle}{\langle \phi \rangle} + Parameters ---------- @@ -2466,7 +3157,7 @@ class ScatterMatrixXS(MGXS): The domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe'} The domain type for spatial homogenization - energy_groups : openmc.mgxs.EnergyGroups + groups : openmc.mgxs.EnergyGroups The energy group structure for energy condensation by_nuclide : bool If true, computes cross sections for each nuclide in domain @@ -2479,7 +3170,7 @@ class ScatterMatrixXS(MGXS): correction : 'P0' or None Apply the P0 correction to scattering matrices if set to 'P0' legendre_order : int - The highest legendre moment in the scattering matrix (default is 0) + The highest Legendre moment in the scattering matrix (default is 0) name : str, optional Name of the multi-group cross section rxn_type : str @@ -2505,7 +3196,9 @@ class ScatterMatrixXS(MGXS): estimator : {'tracklength', 'analog'} The tally estimator used to compute the multi-group cross section tallies : collections.OrderedDict - OpenMC tallies needed to compute the multi-group cross section + OpenMC tallies needed to compute the multi-group cross section. The keys + are strings listed in the :attr:`ScatterMatrixXS.tally_keys` property + and values are instances of :class:`openmc.Tally`. rxn_rate_tally : openmc.Tally Derived tally for the reaction rate tally used in the numerator to compute the multi-group cross section. This attribute is None @@ -2586,10 +3279,6 @@ class ScatterMatrixXS(MGXS): return filters - @property - def estimator(self): - return 'analog' - @property def rxn_rate_tally(self): @@ -2706,9 +3395,10 @@ class ScatterMatrixXS(MGXS): Returns ------- - openmc.mgxs.MGXS - A new tally which encapsulates the subset of data requested for the - nuclide(s) and/or energy group(s) requested in the parameters. + openmc.mgxs.MatrixMGXS + A new MatrixMGXS which encapsulates the subset of data requested + for the nuclide(s) and/or energy group(s) requested in the + parameters. """ @@ -2790,9 +3480,8 @@ class ScatterMatrixXS(MGXS): Return the cross section indexed first by incoming group and second by outgoing group ('inout'), or vice versa ('outin'). Defaults to 'inout'. - value : str - A string for the type of value to return - 'mean', 'std_dev', or - 'rel_err' are accepted. Defaults to the empty string. + value : {'mean', 'std_dev', 'rel_err'} + A string for the type of value to return. Defaults to 'mean'. Returns ------- @@ -3020,7 +3709,7 @@ class ScatterMatrixXS(MGXS): cv.check_value('xs_type', xs_type, ['macro', 'micro']) if self.correction != 'P0': - rxn_type= '{0} (P{1})'.format(self.rxn_type, moment) + rxn_type = '{0} (P{1})'.format(self.rxn_type, moment) else: rxn_type = self.rxn_type @@ -3039,7 +3728,7 @@ class ScatterMatrixXS(MGXS): template = '{0: <12}Group {1} [{2: <10} - {3: <10}MeV]\n' # Loop over energy groups ranges - for group in range(1, self.num_groups+1): + for group in range(1, self.num_groups + 1): bounds = self.energy_groups.get_group_bounds(group) string += template.format('', group, bounds[0], bounds[1]) @@ -3066,8 +3755,8 @@ class ScatterMatrixXS(MGXS): template = '{0: <12}Group {1} -> Group {2}:\t\t' # Loop over incoming/outgoing energy groups ranges - for in_group in range(1, self.num_groups+1): - for out_group in range(1, self.num_groups+1): + for in_group in range(1, self.num_groups + 1): + for out_group in range(1, self.num_groups + 1): string += template.format('', in_group, out_group) average = \ self.get_xs([in_group], [out_group], @@ -3079,7 +3768,8 @@ class ScatterMatrixXS(MGXS): xs_type=xs_type, value='rel_err') average = average.flatten()[0] rel_err = rel_err.flatten()[0] * 100. - string += '{:1.2e} +/- {:1.2e}%'.format(average, rel_err) + string += '{:1.2e} +/- {:1.2e}%'.format(average, + rel_err) string += '\n' string += '\n' string += '\n' @@ -3094,7 +3784,21 @@ class NuScatterMatrixXS(ScatterMatrixXS): This class can be used for both OpenMC input generation and tally data post-processing to compute spatially-homogenized and energy-integrated - multi-group cross sections for deterministic neutronics calculations. + multi-group cross sections for multi-group neutronics calculations. At a + minimum, one needs to set the :attr:`NuScatterMatrixXS.energy_groups` and + :attr:`NuScatterMatrixXS.domain` properties. Tallies for the flux and + appropriate reaction rates over the specified domain are generated + automatically via the :attr:`NuScatterMatrixXS.tallies` property, which can + then be appended to a :class:`openmc.Tallies` instance. + + For post-processing, the :meth:`MGXS.load_from_statepoint` will pull in the + necessary data to compute multi-group cross sections from a + :class:`openmc.StatePoint` instance. The derived multi-group cross section + can then be obtained from the :attr:`NuScatterMatrixXS.xs_tally` property. + + The calculation of the scattering-production matrix is the same as that for + :class:`ScatterMatrixXS` except that the scattering multiplicity is + accounted for. Parameters ---------- @@ -3102,7 +3806,7 @@ class NuScatterMatrixXS(ScatterMatrixXS): The domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe'} The domain type for spatial homogenization - energy_groups : openmc.mgxs.EnergyGroups + groups : openmc.mgxs.EnergyGroups The energy group structure for energy condensation by_nuclide : bool If true, computes cross sections for each nuclide in domain @@ -3141,7 +3845,9 @@ class NuScatterMatrixXS(ScatterMatrixXS): estimator : {'tracklength', 'analog'} The tally estimator used to compute the multi-group cross section tallies : collections.OrderedDict - OpenMC tallies needed to compute the multi-group cross section + OpenMC tallies needed to compute the multi-group cross section. The keys + are strings listed in the :attr:`NuScatterMatrixXS.tally_keys` property + and values are instances of :class:`openmc.Tally`. rxn_rate_tally : openmc.Tally Derived tally for the reaction rate tally used in the numerator to compute the multi-group cross section. This attribute is None @@ -3182,12 +3888,43 @@ class NuScatterMatrixXS(ScatterMatrixXS): self._hdf5_key = 'nu-scatter matrix' -class Chi(MGXS): - """The fission spectrum. +class MultiplicityMatrixXS(MatrixMGXS): + r"""The scattering multiplicity matrix. This class can be used for both OpenMC input generation and tally data post-processing to compute spatially-homogenized and energy-integrated - multi-group cross sections for deterministic neutronics calculations. + multi-group cross sections for multi-group neutronics calculations. At a + minimum, one needs to set the :attr:`MultiplicityMatrixXS.energy_groups` and + :attr:`MultiplicityMatrixXS.domain` properties. Tallies for the flux and + appropriate reaction rates over the specified domain are generated + automatically via the :attr:`MultiplicityMatrixXS.tallies` property, which + can then be appended to a :class:`openmc.Tallies` instance. + + For post-processing, the :meth:`MGXS.load_from_statepoint` will pull in the + necessary data to compute multi-group cross sections from a + :class:`openmc.StatePoint` instance. The derived multi-group cross section + can then be obtained from the :attr:`MultiplicityMatrixXS.xs_tally` + property. + + For a spatial domain :math:`V`, incoming energy group + :math:`[E_{g'},E_{g'-1}]`, and outgoing energy group :math:`[E_g,E_{g-1}]`, + the multiplicity is calculated as: + + .. math:: + + \langle \upsilon \sigma_{s,g'\rightarrow g} \phi \rangle &= \int_{r \in + D} dr \int_{4\pi} d\Omega' \int_{E_{g'}}^{E_{g'-1}} dE' \int_{4\pi} + d\Omega \int_{E_g}^{E_{g-1}} dE \; \sum_i \upsilon_i \sigma_i (r, E' \rightarrow + E, \Omega' \cdot \Omega) \psi(r, E', \Omega') \\ + \langle \sigma_{s,g'\rightarrow g} \phi \rangle &= \int_{r \in + D} dr \int_{4\pi} d\Omega' \int_{E_{g'}}^{E_{g'-1}} dE' \int_{4\pi} + d\Omega \int_{E_g}^{E_{g-1}} dE \; \sum_i \upsilon_i \sigma_i (r, E' \rightarrow + E, \Omega' \cdot \Omega) \psi(r, E', \Omega') \\ + \upsilon_{g'\rightarrow g} &= \frac{\langle \upsilon + \sigma_{s,g'\rightarrow g} \rangle}{\langle \sigma_{s,g'\rightarrow g} + \rangle} + + where :math:`\upsilon_i` is the multiplicity for the :math:`i`-th reaction. Parameters ---------- @@ -3195,7 +3932,7 @@ class Chi(MGXS): The domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe'} The domain type for spatial homogenization - energy_groups : openmc.mgxs.EnergyGroups + groups : openmc.mgxs.EnergyGroups The energy group structure for energy condensation by_nuclide : bool If true, computes cross sections for each nuclide in domain @@ -3230,7 +3967,271 @@ class Chi(MGXS): estimator : {'tracklength', 'analog'} The tally estimator used to compute the multi-group cross section tallies : collections.OrderedDict - OpenMC tallies needed to compute the multi-group cross section + OpenMC tallies needed to compute the multi-group cross section. The keys + are strings listed in the :attr:`MultiplicityMatrixXS.tally_keys` + property and values are instances of :class:`openmc.Tally`. + rxn_rate_tally : openmc.Tally + Derived tally for the reaction rate tally used in the numerator to + compute the multi-group cross section. This attribute is None + unless the multi-group cross section has been computed. + xs_tally : openmc.Tally + Derived tally for the multi-group cross section. This attribute + is None unless the multi-group cross section has been computed. + num_subdomains : int + The number of subdomains is unity for 'material', 'cell' and 'universe' + domain types. When the This is equal to the number of cell instances + for 'distribcell' domain types (it is equal to unity prior to loading + tally data from a statepoint file). + num_nuclides : int + The number of nuclides for which the multi-group cross section is + being tracked. This is unity if the by_nuclide attribute is False. + nuclides : Iterable of str or 'sum' + The optional user-specified nuclides for which to compute cross + sections (e.g., 'U-238', 'O-16'). If by_nuclide is True but nuclides + are not specified by the user, all nuclides in the spatial domain + are included. This attribute is 'sum' if by_nuclide is false. + sparse : bool + Whether or not the MGXS' tallies use SciPy's LIL sparse matrix format + for compressed data storage + loaded_sp : bool + Whether or not a statepoint file has been loaded with tally data + derived : bool + Whether or not the MGXS is merged from one or more other MGXS + hdf5_key : str + The key used to index multi-group cross sections in an HDF5 data store + + """ + + def __init__(self, domain=None, domain_type=None, + groups=None, by_nuclide=False, name=''): + super(MultiplicityMatrixXS, self).__init__(domain, domain_type, groups, + by_nuclide, name) + self._rxn_type = 'multiplicity matrix' + + @property + def scores(self): + scores = ['nu-scatter', 'scatter'] + return scores + + @property + def filters(self): + # Create the non-domain specific Filters for the Tallies + group_edges = self.energy_groups.group_edges + energy = openmc.Filter('energy', group_edges) + energyout = openmc.Filter('energyout', group_edges) + + return [[energy, energyout], [energy, energyout]] + + @property + def rxn_rate_tally(self): + if self._rxn_rate_tally is None: + self._rxn_rate_tally = self.tallies['nu-scatter'] + self._rxn_rate_tally.sparse = self.sparse + return self._rxn_rate_tally + + @property + def xs_tally(self): + + if self._xs_tally is None: + scatter = self.tallies['scatter'] + + # Compute the multiplicity + self._xs_tally = self.rxn_rate_tally / scatter + super(MultiplicityMatrixXS, self)._compute_xs() + + return self._xs_tally + + +class NuFissionMatrixXS(MatrixMGXS): + r"""A fission production matrix multi-group cross section. + + This class can be used for both OpenMC input generation and tally data + post-processing to compute spatially-homogenized and energy-integrated + multi-group cross sections for multi-group neutronics calculations. At a + minimum, one needs to set the :attr:`NuFissionMatrixXS.energy_groups` and + :attr:`NuFissionMatrixXS.domain` properties. Tallies for the flux and + appropriate reaction rates over the specified domain are generated + automatically via the :attr:`NuFissionMatrixXS.tallies` property, which can + then be appended to a :class:`openmc.Tallies` instance. + + For post-processing, the :meth:`MGXS.load_from_statepoint` will pull in the + necessary data to compute multi-group cross sections from a + :class:`openmc.StatePoint` instance. The derived multi-group cross section + can then be obtained from the :attr:`NuFissionMatrixXS.xs_tally` property. + + For a spatial domain :math:`V`, incoming energy group + :math:`[E_{g'},E_{g'-1}]`, and outgoing energy group :math:`[E_g,E_{g-1}]`, + the fission production is calculated as: + + .. math:: + + \langle \nu\sigma_{f,g'\rightarrow g} \phi \rangle &= \int_{r \in V} dr + \int_{4\pi} d\Omega' \int_{E_{g'}}^{E_{g'-1}} dE' \int_{E_g}^{E_{g-1}} dE + \; \chi(E) \nu\sigma_f (r, E') \psi(r, E', \Omega')\\ + \langle \phi \rangle &= \int_{r \in V} dr \int_{4\pi} d\Omega + \int_{E_g}^{E_{g-1}} dE \; \psi (r, E, \Omega) \\ + \nu\sigma_{f,g'\rightarrow g} &= \frac{\langle \nu\sigma_{f,g'\rightarrow + g} \phi \rangle}{\langle \phi \rangle} + + Parameters + ---------- + domain : openmc.Material or openmc.Cell or openmc.Universe + The domain for spatial homogenization + domain_type : {'material', 'cell', 'distribcell', 'universe'} + The domain type for spatial homogenization + groups : openmc.mgxs.EnergyGroups + The energy group structure for energy condensation + by_nuclide : bool + If true, computes cross sections for each nuclide in domain + name : str, optional + Name of the multi-group cross section. Used as a label to identify + tallies in OpenMC 'tallies.xml' file. + + Attributes + ---------- + name : str, optional + Name of the multi-group cross section + rxn_type : str + Reaction type (e.g., 'total', 'nu-fission', etc.) + by_nuclide : bool + If true, computes cross sections for each nuclide in domain + domain : Material or Cell or Universe + Domain for spatial homogenization + domain_type : {'material', 'cell', 'distribcell', 'universe'} + Domain type for spatial homogenization + energy_groups : openmc.mgxs.EnergyGroups + Energy group structure for energy condensation + tally_trigger : openmc.Trigger + An (optional) tally precision trigger given to each tally used to + compute the cross section + scores : list of str + The scores in each tally used to compute the multi-group cross section + filters : list of openmc.Filter + The filters in each tally used to compute the multi-group cross section + tally_keys : list of str + The keys into the tallies dictionary for each tally used to compute + the multi-group cross section + estimator : {'tracklength', 'analog'} + The tally estimator used to compute the multi-group cross section + tallies : collections.OrderedDict + OpenMC tallies needed to compute the multi-group cross section. The keys + are strings listed in the :attr:`NuFissionMatrixXS.tally_keys` + property and values are instances of :class:`openmc.Tally`. + rxn_rate_tally : openmc.Tally + Derived tally for the reaction rate tally used in the numerator to + compute the multi-group cross section. This attribute is None + unless the multi-group cross section has been computed. + xs_tally : openmc.Tally + Derived tally for the multi-group cross section. This attribute + is None unless the multi-group cross section has been computed. + num_subdomains : int + The number of subdomains is unity for 'material', 'cell' and 'universe' + domain types. When the This is equal to the number of cell instances + for 'distribcell' domain types (it is equal to unity prior to loading + tally data from a statepoint file). + num_nuclides : int + The number of nuclides for which the multi-group cross section is + being tracked. This is unity if the by_nuclide attribute is False. + nuclides : Iterable of str or 'sum' + The optional user-specified nuclides for which to compute cross + sections (e.g., 'U-238', 'O-16'). If by_nuclide is True but nuclides + are not specified by the user, all nuclides in the spatial domain + are included. This attribute is 'sum' if by_nuclide is false. + sparse : bool + Whether or not the MGXS' tallies use SciPy's LIL sparse matrix format + for compressed data storage + loaded_sp : bool + Whether or not a statepoint file has been loaded with tally data + derived : bool + Whether or not the MGXS is merged from one or more other MGXS + hdf5_key : str + The key used to index multi-group cross sections in an HDF5 data store + + """ + + def __init__(self, domain=None, domain_type=None, + groups=None, by_nuclide=False, name=''): + super(NuFissionMatrixXS, self).__init__(domain, domain_type, + groups, by_nuclide, name) + self._rxn_type = 'nu-fission' + self._hdf5_key = 'nu-fission matrix' + + +class Chi(MGXS): + r"""The fission spectrum. + + This class can be used for both OpenMC input generation and tally data + post-processing to compute spatially-homogenized and energy-integrated + multi-group cross sections for multi-group neutronics calculations. At a + minimum, one needs to set the :attr:`Chi.energy_groups` and + :attr:`Chi.domain` properties. Tallies for the flux and appropriate reaction + rates over the specified domain are generated automatically via the + :attr:`Chi.tallies` property, which can then be appended to a + :class:`openmc.Tallies` instance. + + For post-processing, the :meth:`MGXS.load_from_statepoint` will pull in the + necessary data to compute multi-group cross sections from a + :class:`openmc.StatePoint` instance. The derived multi-group cross section + can then be obtained from the :attr:`Chi.xs_tally` property. + + For a spatial domain :math:`V` and energy group :math:`[E_g,E_{g-1}]`, the + fission spectrum is calculated as: + + .. math:: + + \langle \nu\sigma_{f,\rightarrow g} \phi \rangle &= \int_{r \in V} dr + \int_{4\pi} d\Omega' \int_0^\infty dE' \int_{E_g}^{E_{g-1}} dE \; \chi(E) + \nu\sigma_f (r, E') \psi(r, E', \Omega')\\ + \langle \nu\sigma_f \phi \rangle &= \int_{r \in V} dr \int_{4\pi} + d\Omega' \int_0^\infty dE' \int_0^\infty dE \; \chi(E) \nu\sigma_f (r, + E') \psi(r, E', \Omega') \\ + \chi_g &= \frac{\langle \nu\sigma_{f,\rightarrow g} \phi \rangle}{\langle + \nu\sigma_f \phi \rangle} + + Parameters + ---------- + domain : openmc.Material or openmc.Cell or openmc.Universe + The domain for spatial homogenization + domain_type : {'material', 'cell', 'distribcell', 'universe'} + The domain type for spatial homogenization + groups : openmc.mgxs.EnergyGroups + The energy group structure for energy condensation + by_nuclide : bool + If true, computes cross sections for each nuclide in domain + name : str, optional + Name of the multi-group cross section. Used as a label to identify + tallies in OpenMC 'tallies.xml' file. + + Attributes + ---------- + name : str, optional + Name of the multi-group cross section + rxn_type : str + Reaction type (e.g., 'total', 'nu-fission', etc.) + by_nuclide : bool + If true, computes cross sections for each nuclide in domain + domain : Material or Cell or Universe + Domain for spatial homogenization + domain_type : {'material', 'cell', 'distribcell', 'universe'} + Domain type for spatial homogenization + energy_groups : openmc.mgxs.EnergyGroups + Energy group structure for energy condensation + tally_trigger : openmc.Trigger + An (optional) tally precision trigger given to each tally used to + compute the cross section + scores : list of str + The scores in each tally used to compute the multi-group cross section + filters : list of openmc.Filter + The filters in each tally used to compute the multi-group cross section + tally_keys : list of str + The keys into the tallies dictionary for each tally used to compute + the multi-group cross section + estimator : {'tracklength', 'analog'} + The tally estimator used to compute the multi-group cross section + tallies : collections.OrderedDict + OpenMC tallies needed to compute the multi-group cross section. The keys + are strings listed in the :attr:`Chi.tally_keys` property and values are + instances of :class:`openmc.Tally`. rxn_rate_tally : openmc.Tally Derived tally for the reaction rate tally used in the numerator to compute the multi-group cross section. This attribute is None @@ -3333,9 +4334,10 @@ class Chi(MGXS): Returns ------- - MGXS - A new tally which encapsulates the subset of data requested for the - nuclide(s) and/or energy group(s) requested in the parameters. + openmc.mgxs.MGXS + A new MGXS which encapsulates the subset of data requested + for the nuclide(s) and/or energy group(s) requested in the + parameters. """ @@ -3447,9 +4449,8 @@ class Chi(MGXS): Return the cross section indexed according to increasing or decreasing energy groups (decreasing or increasing energies). Defaults to 'increasing'. - value : str - A string for the type of value to return - 'mean', 'std_dev', or - 'rel_err' are accepted. Defaults to 'mean'. + value : {'mean', 'std_dev', 'rel_err'} + A string for the type of value to return. Defaults to 'mean'. Returns ------- diff --git a/openmc/mgxs_library.py b/openmc/mgxs_library.py index 88ae05808a..35b48d8730 100644 --- a/openmc/mgxs_library.py +++ b/openmc/mgxs_library.py @@ -131,6 +131,8 @@ class XSdata(object): num_polar : int Number of equal width angular bins that the polar angular domain is subdivided into. This only applies when ``representation`` is "angle". + use_chi : bool + Whether or not a chi vector or nu-fission matrix was used. vector_shape : iterable of int Dimensionality of vector multi-group cross sections (e.g., the total cross section). The return result depends on the value of @@ -292,6 +294,10 @@ class XSdata(object): def num_azimuthal(self): return self._num_azimuthal + @property + def use_chi(self): + return self._use_chi + @property def total(self): return self._total @@ -461,6 +467,11 @@ class XSdata(object): check_greater_than('num_azimuthal', num_azimuthal, 0) self._num_azimuthal = num_azimuthal + @use_chi.setter + def use_chi(self, use_chi): + check_type('use_chi', use_chi, bool) + self._use_chi = use_chi + @total.setter def total(self, total): check_type('total', total, Iterable, expected_iter_type=Real) @@ -512,9 +523,9 @@ class XSdata(object): @chi.setter def chi(self, chi): - if self._use_chi is not None: - if not self._use_chi: - msg = 'Providing chi when nu_fission already provided as a' \ + if self.use_chi is not None: + if not self.use_chi: + msg = 'Providing "chi" when "nu-fission" already provided as a' \ 'matrix' raise ValueError(msg) @@ -529,8 +540,8 @@ class XSdata(object): self._chi = npchi - if self._use_chi is not None: - self._use_chi = True + if self.use_chi is not None: + self.use_chi = True @scatter.setter def scatter(self, scatter): @@ -574,8 +585,8 @@ class XSdata(object): check_iterable_type('nu_fission', npnu_fission, Real, max_depth=len(npnu_fission.shape)) - if self._use_chi is not None: - if self._use_chi: + if self.use_chi is not None: + if self.use_chi: check_value('nu_fission shape', npnu_fission.shape, [self.vector_shape]) else: @@ -587,9 +598,9 @@ class XSdata(object): # Find out if we have a nu-fission matrix or vector # and set a flag to allow other methods to check this later. if npnu_fission.shape == self.vector_shape: - self._use_chi = True + self.use_chi = True else: - self._use_chi = False + self.use_chi = False self._nu_fission = npnu_fission if np.sum(self._nu_fission) > 0.0: @@ -728,10 +739,8 @@ class XSdata(object): """ - # The NuFissionXS class does not have the capability to produce - # a fission matrix and therefore if this path is pursued, we know - # chi must be used. - check_type('nu_fission', nu_fission, openmc.mgxs.NuFissionXS) + check_type('nu_fission', nu_fission, (openmc.mgxs.NuFissionXS, + openmc.mgxs.NuFissionMatrixXS)) check_value('energy_groups', nu_fission.energy_groups, [self.energy_groups]) check_value('domain_type', nu_fission.domain_type, @@ -744,7 +753,10 @@ class XSdata(object): msg = 'Angular-Dependent MGXS have not yet been implemented' raise ValueError(msg) - self._use_chi = True + if isinstance(nu_fission, openmc.mgxs.NuFissionMatrixXS): + self.use_chi = False + else: + self.use_chi = True if np.sum(self._nu_fission) > 0.0: self._fissionable = True @@ -809,8 +821,8 @@ class XSdata(object): """ - if self._use_chi is not None: - if not self._use_chi: + if self.use_chi is not None: + if not self.use_chi: msg = 'Providing chi when nu_fission already provided as a ' \ 'matrix!' raise ValueError(msg) @@ -827,8 +839,8 @@ class XSdata(object): msg = 'Angular-Dependent MGXS have not yet been implemented' raise ValueError(msg) - if self._use_chi is not None: - self._use_chi = True + if self.use_chi is not None: + self.use_chi = True def set_scatter_mgxs(self, scatter, nuclide='total', xs_type='macro'): """This method allows for an openmc.mgxs.ScatterMatrixXS @@ -891,9 +903,10 @@ class XSdata(object): msg = 'Angular-Dependent MGXS have not yet been implemented' raise ValueError(msg) - def set_multiplicity_mgxs(self, nuscatter, scatter, nuclide='total', + def set_multiplicity_mgxs(self, nuscatter, scatter=None, nuclide='total', xs_type='macro'): - """This method allows for an openmc.mgxs.NuScatterMatrixXS and + """This method allows for either the direct use of only an + openmc.mgxs.MultiplicityMatrixXS OR an openmc.mgxs.NuScatterMatrixXS and openmc.mgxs.ScatterMatrixXS to be used to set the scattering multiplicity for this XSdata object. Multiplicity, in OpenMC parlance, is a factor used to account for the production @@ -903,9 +916,10 @@ class XSdata(object): Parameters ---------- - nuscatter: openmc.mgxs.NuScatterMatrixXS - MGXS Object containing the nu-scattering matrix cross section - for the domain of interest. + nuscatter: {openmc.mgxs.NuScatterMatrixXS, + openmc.mgxs.MultiplicityMatrixXS} + MGXS Object containing the matrix cross section for the domain + of interest. scatter: openmc.mgxs.ScatterMatrixXS MGXS Object containing the scattering matrix cross section for the domain of interest. @@ -923,23 +937,33 @@ class XSdata(object): """ - check_type('nuscatter', nuscatter, openmc.mgxs.NuScatterMatrixXS) - check_type('scatter', scatter, openmc.mgxs.ScatterMatrixXS) + check_type('nuscatter', nuscatter, (openmc.mgxs.NuScatterMatrixXS, + openmc.mgxs.MultiplicityMatrixXS)) check_value('energy_groups', nuscatter.energy_groups, [self.energy_groups]) - check_value('energy_groups', scatter.energy_groups, - [self.energy_groups]) check_value('domain_type', nuscatter.domain_type, ['universe', 'cell', 'material']) - check_value('domain_type', scatter.domain_type, - ['universe', 'cell', 'material']) + if scatter is not None: + check_type('scatter', scatter, openmc.mgxs.ScatterMatrixXS) + if isinstance(nuscatter, openmc.mgxs.MultiplicityMatrixXS): + msg = 'Either an MultiplicityMatrixXS object must be passed ' \ + 'for "nuscatter" or the "scatter" argument must be ' \ + 'provided.' + raise ValueError(msg) + check_value('energy_groups', scatter.energy_groups, + [self.energy_groups]) + check_value('domain_type', scatter.domain_type, + ['universe', 'cell', 'material']) if self.representation is 'isotropic': nuscatt = nuscatter.get_xs(nuclides=nuclide, xs_type=xs_type, moment=0) - scatt = scatter.get_xs(nuclides=nuclide, - xs_type=xs_type, moment=0) - self._multiplicity = np.divide(nuscatt, scatt) + if isinstance(nuscatter, openmc.mgxs.MultiplicityMatrixXS): + self._multiplicity = nuscatt + else: + scatt = scatter.get_xs(nuclides=nuclide, + xs_type=xs_type, moment=0) + self._multiplicity = np.divide(nuscatt, scatt) elif self.representation is 'angle': msg = 'Angular-Dependent MGXS have not yet been implemented' raise ValueError(msg) diff --git a/openmc/model/__init__.py b/openmc/model/__init__.py new file mode 100644 index 0000000000..ffb1f42820 --- /dev/null +++ b/openmc/model/__init__.py @@ -0,0 +1 @@ +from .triso import * diff --git a/openmc/model/triso.py b/openmc/model/triso.py new file mode 100644 index 0000000000..89e0d8aa76 --- /dev/null +++ b/openmc/model/triso.py @@ -0,0 +1,155 @@ +import copy +from collections import Iterable +from numbers import Real +import warnings + +import numpy as np + +import openmc +import openmc.checkvalue as cv + +class TRISO(openmc.Cell): + """Tristructural-isotopic (TRISO) micro fuel particle + + Parameters + ---------- + outer_radius : float + Outer radius of TRISO particle + fill : openmc.Universe + Universe which contains all layers of the TRISO particle + center : Iterable of float + Cartesian coordinates of the center of the TRISO particle in cm + + Attributes + ---------- + id : int + Unique identifier for the TRISO cell + name : str + Name of the TRISO cell + center : numpy.ndarray + Cartesian coordinates of the center of the TRISO particle in cm + fill : openmc.Universe + Universe that contains the TRISO layers + region : openmc.Region + Region of space within the TRISO particle + + """ + + def __init__(self, outer_radius, fill, center=(0., 0., 0.)): + self._surface = openmc.Sphere(R=outer_radius) + super(TRISO, self).__init__(fill=fill, region=-self._surface) + self.center = np.asarray(center) + + @property + def center(self): + return self._center + + @center.setter + def center(self, center): + cv.check_type('TRISO center', center, Iterable, Real) + self._surface.x0 = center[0] + self._surface.y0 = center[1] + self._surface.z0 = center[2] + self.translation = center + self._center = center + + def classify(self, lattice): + """Determine lattice element indices which might contain the TRISO particle. + + Parameters + ---------- + lattice : openmc.RectLattice + Lattice to check + + Returns + ------- + list of tuple + (z,y,x) lattice element indices which might contain the TRISO + particle. + + """ + + ll, ur = self.region.bounding_box + if lattice.ndim == 2: + (i_min, j_min), p = lattice.find_element(ll) + (i_max, j_max), p = lattice.find_element(ur) + return list(np.broadcast(*np.ogrid[ + j_min:j_max+1, i_min:i_max+1])) + else: + (i_min, j_min, k_min), p = lattice.find_element(ll) + (i_max, j_max, k_max), p = lattice.find_element(ur) + return list(np.broadcast(*np.ogrid[ + k_min:k_max+1, j_min:j_max+1, i_min:i_max+1])) + + +def create_triso_lattice(trisos, lower_left, pitch, shape, background): + """Create a lattice containing TRISO particles for optimized tracking. + + Parameters + ---------- + trisos : list of openmc.model.TRISO + List of TRISO particles to put in lattice + lower_left : Iterable of float + Lower-left Cartesian coordinates of the lattice + pitch : Iterable of float + Pitch of the lattice elements in the x-, y-, and z-directions + shape : Iterable of float + Number of lattice elements in the x-, y-, and z-directions + background : openmc.Material + A background material that is used anywhere within the lattice but + outside a TRISO particle + + Returns + ------- + lattice : openmc.RectLattice + A lattice containing the TRISO particles + + """ + + lattice = openmc.RectLattice() + lattice.lower_left = lower_left + lattice.pitch = pitch + + indices = list(np.broadcast(*np.ogrid[:shape[2], :shape[1], :shape[0]])) + triso_locations = {idx: [] for idx in indices} + for t in trisos: + for idx in t.classify(lattice): + if idx in sorted(triso_locations): + # Create copy of TRISO particle with materials preserved and + # different cell/surface IDs + t_copy = copy.deepcopy(t) + t_copy.id = None + t_copy.fill = t.fill + t_copy._surface.id = None + triso_locations[idx].append(t_copy) + else: + warnings.warn('TRISO particle is partially or completely ' + 'outside of the lattice.') + + # Create universes + universes = np.empty(shape[::-1], dtype=openmc.Universe) + for idx, triso_list in sorted(triso_locations.items()): + if len(triso_list) > 0: + outside_trisos = openmc.Intersection(*[~t.region for t in triso_list]) + background_cell = openmc.Cell(fill=background, region=outside_trisos) + else: + background_cell = openmc.Cell(fill=background) + + u = openmc.Universe() + u.add_cell(background_cell) + for t in triso_list: + u.add_cell(t) + iz, iy, ix = idx + t.center = lattice.get_local_coordinates(t.center, (ix, iy, iz)) + + if len(shape) == 2: + universes[-1 - idx[0], idx[1]] = u + else: + universes[idx[0], -1 - idx[1], idx[2]] = u + lattice.universes = universes + + # Set outer universe + background_cell = openmc.Cell(fill=background) + lattice.outer = openmc.Universe(cells=[background_cell]) + + return lattice diff --git a/openmc/opencg_compatible.py b/openmc/opencg_compatible.py index 562fe9cadf..b112ed327f 100644 --- a/openmc/opencg_compatible.py +++ b/openmc/opencg_compatible.py @@ -1,4 +1,5 @@ import copy +import operator import numpy as np @@ -9,8 +10,6 @@ except ImportError: raise ImportError(msg) import openmc -from openmc.region import Intersection -from openmc.surface import Halfspace import openmc.checkvalue as cv @@ -467,13 +466,13 @@ def get_opencg_cell(openmc_cell): # half-spaces, i.e., no complex cells. region = openmc_cell.region if region is not None: - if isinstance(region, Halfspace): + if isinstance(region, openmc.Halfspace): surface = region.surface halfspace = -1 if region.side == '-' else 1 opencg_cell.add_surface(get_opencg_surface(surface), halfspace) - elif isinstance(region, Intersection): + elif isinstance(region, openmc.Intersection): for node in region.nodes: - if not isinstance(node, Halfspace): + if not isinstance(node, openmc.Halfspace): raise NotImplementedError("Complex cells not yet " "supported in OpenCG.") surface = node.surface @@ -697,12 +696,13 @@ def get_openmc_cell(opencg_cell): translation = np.asarray(opencg_cell.translation, dtype=np.float64) openmc_cell.translation = translation - surfaces = opencg_cell.surfaces - - for surface_id in surfaces: - surface = surfaces[surface_id][0] - halfspace = surfaces[surface_id][1] - openmc_cell.add_surface(get_openmc_surface(surface), halfspace) + surfaces = [] + operators = [] + for surface, halfspace in opencg_cell.surfaces.values(): + surfaces.append(get_openmc_surface(surface)) + operators.append(operator.neg if halfspace == -1 else operator.pos) + openmc_cell.region = openmc.Intersection( + *[op(s) for op, s in zip(operators, surfaces)]) # Add the OpenMC Cell to the global collection of all OpenMC Cells OPENMC_CELLS[cell_id] = openmc_cell @@ -861,8 +861,8 @@ def get_opencg_lattice(openmc_lattice): universes = new_universes # Initialize an empty array for the OpenCG nested Universes in this Lattice - universe_array = np.ndarray(tuple(np.array(dimension)[::-1]), - dtype=opencg.Universe) + universe_array = np.empty(tuple(np.array(dimension)[::-1]), + dtype=opencg.Universe) # Create OpenCG Universes for each unique nested Universe in this Lattice unique_universes = openmc_lattice.get_unique_universes() @@ -929,8 +929,8 @@ def get_openmc_lattice(opencg_lattice): outer = opencg_lattice.outside # Initialize an empty array for the OpenMC nested Universes in this Lattice - universe_array = np.ndarray(tuple(np.array(dimension)[::-1]), - dtype=openmc.Universe) + universe_array = np.empty(tuple(np.array(dimension)[::-1]), + dtype=openmc.Universe) # Create OpenMC Universes for each unique nested Universe in this Lattice unique_universes = opencg_lattice.get_unique_universes() @@ -953,7 +953,6 @@ def get_openmc_lattice(opencg_lattice): np.array(dimension, dtype=np.float64))) / -2.0 openmc_lattice = openmc.RectLattice(lattice_id=lattice_id) - openmc_lattice.dimension = dimension openmc_lattice.pitch = width openmc_lattice.universes = universe_array openmc_lattice.lower_left = lower_left diff --git a/openmc/particle_restart.py b/openmc/particle_restart.py index 72bf3ac3de..7a27db2f60 100644 --- a/openmc/particle_restart.py +++ b/openmc/particle_restart.py @@ -37,6 +37,11 @@ class Particle(object): def __init__(self, filename): import h5py + if h5py.__version__ == '2.6.0': + raise ImportError("h5py 2.6.0 has a known bug which makes it " + "incompatible with OpenMC's HDF5 files. " + "Please switch to a different version.") + self._f = h5py.File(filename, 'r') # Ensure filetype and revision are correct diff --git a/openmc/region.py b/openmc/region.py index a2edbeedd6..9e10112710 100644 --- a/openmc/region.py +++ b/openmc/region.py @@ -28,6 +28,10 @@ class Region(object): def __invert__(self): return Complement(self) + @abstractmethod + def __contains__(self, point): + return False + @abstractmethod def __str__(self): return '' @@ -219,7 +223,7 @@ class Intersection(Region): Attributes ---------- - nodes : tuple of openmc.Region + nodes : list of openmc.Region Regions to take the intersection of bounding_box : tuple of numpy.array Lower-left and upper-right coordinates of an axis-aligned bounding box @@ -229,6 +233,26 @@ class Intersection(Region): def __init__(self, *nodes): self.nodes = list(nodes) + def __iter__(self): + for n in self.nodes: + yield n + + def __contains__(self, point): + """Check whether a point is contained in the region. + + Parameters + ---------- + point : 3-tuple of float + Cartesian coordinates, :math:`(x',y',z')`, of the point + + Returns + ------- + bool + Whether the point is in the region + + """ + return all(point in n for n in self.nodes) + def __str__(self): return '(' + ' '.join(map(str, self.nodes)) + ')' @@ -281,6 +305,26 @@ class Union(Region): def __init__(self, *nodes): self.nodes = list(nodes) + def __iter__(self): + for n in self.nodes: + yield n + + def __contains__(self, point): + """Check whether a point is contained in the region. + + Parameters + ---------- + point : 3-tuple of float + Cartesian coordinates, :math:`(x',y',z')`, of the point + + Returns + ------- + bool + Whether the point is in the region + + """ + return any(point in n for n in self.nodes) + def __str__(self): return '(' + ' | '.join(map(str, self.nodes)) + ')' @@ -336,6 +380,22 @@ class Complement(Region): def __init__(self, node): self.node = node + def __contains__(self, point): + """Check whether a point is contained in the region. + + Parameters + ---------- + point : 3-tuple of float + Cartesian coordinates, :math:`(x',y',z')`, of the point + + Returns + ------- + bool + Whether the point is in the region + + """ + return point not in self.node + def __str__(self): return '~' + str(self.node) diff --git a/openmc/statepoint.py b/openmc/statepoint.py index d5dd7bc1e7..0baa631581 100644 --- a/openmc/statepoint.py +++ b/openmc/statepoint.py @@ -106,6 +106,11 @@ class StatePoint(object): def __init__(self, filename, autolink=True): import h5py + if h5py.__version__ == '2.6.0': + raise ImportError("h5py 2.6.0 has a known bug which makes it " + "incompatible with OpenMC's HDF5 files. " + "Please switch to a different version.") + self._f = h5py.File(filename, 'r') # Ensure filetype and revision are correct @@ -497,13 +502,18 @@ class StatePoint(object): self.tallies[tally_id].sparse = self.sparse def get_tally(self, scores=[], filters=[], nuclides=[], - name=None, id=None, estimator=None): + name=None, id=None, estimator=None, exact_filters=False, + exact_nuclides=False, exact_scores=False): """Finds and returns a Tally object with certain properties. This routine searches the list of Tallies and returns the first Tally found which satisfies all of the input parameters. - NOTE: The input parameters do not need to match the complete Tally - specification and may only represent a subset of the Tally's properties. + + NOTE: If any of the "exact" parameters are False (default), the input + parameters do not need to match the complete Tally specification and + may only represent a subset of the Tally's properties. If an "exact" + parameter is True then number of scores, filters, or nuclides in the + parameters must precisely match those of any matching Tally. Parameters ---------- @@ -519,6 +529,18 @@ class StatePoint(object): The id specified for the Tally (default is None). estimator: str, optional The type of estimator ('tracklength', 'analog'; default is None). + exact_filters : bool + If True, the number of filters in the parameters must be identical + to those in the matching Tally. If False (default), the filters in + the parameters may be a subset of those in the matching Tally. + exact_nuclides : bool + If True, the number of nuclides in the parameters must be identical + to those in the matching Tally. If False (default), the nuclides in + the parameters may be a subset of those in the matching Tally. + exact_scores : bool + If True, the number of scores in the parameters must be identical + to those in the matching Tally. If False (default), the scores + in the parameters may be a subset of those in the matching Tally. Returns ------- @@ -547,7 +569,15 @@ class StatePoint(object): continue # Determine if Tally has queried estimator - if estimator and not estimator == test_tally.estimator: + if estimator and estimator != test_tally.estimator: + continue + + # The number of filters, nuclides and scores must exactly match + if exact_scores and len(scores) != test_tally.num_scores: + continue + if exact_nuclides and len(nuclides) != test_tally.num_nuclides: + continue + if exact_filters and len(filters) != test_tally.num_filters: continue # Determine if Tally has the queried score(s) diff --git a/openmc/stats/univariate.py b/openmc/stats/univariate.py index 0deeb600c4..af9b9b3013 100644 --- a/openmc/stats/univariate.py +++ b/openmc/stats/univariate.py @@ -66,14 +66,14 @@ class Discrete(Univariate): @x.setter def x(self, x): - if cv._isinstance(x, Real): + if isinstance(x, Real): x = [x] cv.check_type('discrete values', x, Iterable, Real) self._x = x @p.setter def p(self, p): - if cv._isinstance(p, Real): + if isinstance(p, Real): p = [p] cv.check_type('discrete probabilities', p, Iterable, Real) for pk in p: diff --git a/openmc/summary.py b/openmc/summary.py index 04c37f82dc..c5dbcadda0 100644 --- a/openmc/summary.py +++ b/openmc/summary.py @@ -26,6 +26,10 @@ class Summary(object): # Python API so we'll only try to import h5py if the user actually inits # a Summary object. import h5py + if h5py.__version__ == '2.6.0': + raise ImportError("h5py 2.6.0 has a known bug which makes it " + "incompatible with OpenMC's HDF5 files. " + "Please switch to a different version.") openmc.reset_auto_ids() @@ -362,7 +366,6 @@ class Summary(object): # Create the Lattice lattice = openmc.RectLattice(lattice_id=lattice_id, name=name) - lattice.dimension = tuple(dimension) lattice.lower_left = lower_left lattice.pitch = pitch @@ -372,7 +375,7 @@ class Summary(object): # Build array of Universe pointers for the Lattice universes = \ - np.ndarray(tuple(universe_ids.shape), dtype=openmc.Universe) + np.empty(tuple(universe_ids.shape), dtype=openmc.Universe) for z in range(universe_ids.shape[0]): for y in range(universe_ids.shape[1]): @@ -407,8 +410,6 @@ class Summary(object): # Create the Lattice lattice = openmc.HexLattice(lattice_id=lattice_id, name=name) - lattice.num_rings = n_rings - lattice.num_axial = n_axial lattice.center = center lattice.pitch = pitch @@ -421,12 +422,12 @@ class Summary(object): # (x, alpha, z) to the Python API's format of a ragged nested # list of (z, ring, theta). universes = [] - for z in range(lattice.num_axial): + for z in range(n_axial): # Add a list for this axial level. universes.append([]) - x = lattice.num_rings - 1 - a = 2*lattice.num_rings - 2 - for r in range(lattice.num_rings - 1, 0, -1): + x = n_rings - 1 + a = 2*n_rings - 2 + for r in range(n_rings - 1, 0, -1): # Add a list for this ring. universes[-1].append([]) @@ -500,13 +501,13 @@ class Summary(object): # Retrieve the object corresponding to the fill type and ID if fill_type == 'normal': if isinstance(fill_id, Iterable): - fill = [self.get_material_by_id(mat) if mat > 0 else 'void' + fill = [self.get_material_by_id(mat) if mat > 0 else None for mat in fill_id] else: if fill_id > 0: fill = self.get_material_by_id(fill_id) else: - fill = 'void' + fill = None elif fill_type == 'universe': fill = self.get_universe_by_id(fill_id) else: diff --git a/openmc/surface.py b/openmc/surface.py index 52f0955f00..76f0d82e7d 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -38,7 +38,9 @@ class Surface(object): boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'}, optional Boundary condition that defines the behavior for particles hitting the surface. Defaults to transmissive boundary condition where particles - freely pass through the surface. + freely pass through the surface. Note that periodic boundary conditions + can only be applied to x-, y-, and z-planes, and only axis-aligned + periodicity is supported. name : str, optional Name of the surface. If not specified, the name will be the empty string. @@ -193,7 +195,7 @@ class Plane(Surface): surface_id : int, optional Unique identifier for the surface. If not specified, an identifier will automatically be assigned. - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'}, optional + boundary_type : {'transmission, 'vacuum', 'reflective'}, optional Boundary condition that defines the behavior for particles hitting the surface. Defaults to transmissive boundary condition where particles freely pass through the surface. @@ -218,9 +220,12 @@ class Plane(Surface): The 'C' parameter for the plane d : float The 'D' parameter for the plane - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'} + boundary_type : {'transmission, 'vacuum', 'reflective'} Boundary condition that defines the behavior for particles hitting the surface. + periodic_surface : openmc.Surface + If a periodic boundary condition is used, the surface with which this + one is periodic with coefficients : dict Dictionary of surface coefficients id : int @@ -238,6 +243,7 @@ class Plane(Surface): self._type = 'plane' self._coeff_keys = ['A', 'B', 'C', 'D'] + self._periodic_surface = None self.a = A self.b = B self.c = C @@ -259,6 +265,10 @@ class Plane(Surface): def d(self): return self.coefficients['D'] + @property + def periodic_surface(self): + return self._periodic_surface + @a.setter def a(self, A): check_type('A coefficient', A, Real) @@ -279,6 +289,40 @@ class Plane(Surface): check_type('D coefficient', D, Real) self._coefficients['D'] = D + @periodic_surface.setter + def periodic_surface(self, periodic_surface): + check_type('periodic surface', periodic_surface, Plane) + self._periodic_surface = periodic_surface + periodic_surface._periodic_surface = self + + def evaluate(self, point): + """Evaluate the surface equation at a given point. + + Parameters + ---------- + point : 3-tuple of float + The Cartesian coordinates, :math:`(x',y',z')`, at which the surface + equation should be evaluated. + + Returns + ------- + float + :math:`Ax' + By' + Cz' - d` + + """ + + x, y, z = point + return self.a*x + self.b*y + self.c*z - self.d + + def create_xml_subelement(self): + element = super(Plane, self).create_xml_subelement() + + # Add periodic surface pair information + if self.boundary_type == 'periodic': + if self.periodic_surface is not None: + element.set("periodic_surface_id", str(self.periodic_surface.id)) + return element + class XPlane(Plane): """A plane perpendicular to the x axis of the form :math:`x - x_0 = 0` @@ -291,7 +335,8 @@ class XPlane(Plane): boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'}, optional Boundary condition that defines the behavior for particles hitting the surface. Defaults to transmissive boundary condition where particles - freely pass through the surface. + freely pass through the surface. Only axis-aligned periodicity is + supported, i.e., x-planes can only be paired with x-planes. x0 : float, optional Location of the plane. Defaults to 0. name : str, optional @@ -304,6 +349,9 @@ class XPlane(Plane): boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'} Boundary condition that defines the behavior for particles hitting the surface. + periodic_surface : openmc.Surface + If a periodic boundary condition is used, the surface with which this + one is periodic with coefficients : dict Dictionary of surface coefficients id : int @@ -363,6 +411,23 @@ class XPlane(Plane): return (np.array([self.x0, -np.inf, -np.inf]), np.array([np.inf, np.inf, np.inf])) + def evaluate(self, point): + """Evaluate the surface equation at a given point. + + Parameters + ---------- + point : 3-tuple of float + The Cartesian coordinates, :math:`(x',y',z')`, at which the surface + equation should be evaluated. + + Returns + ------- + float + :math:`x' - x_0` + + """ + return point[0] - self.x0 + class YPlane(Plane): """A plane perpendicular to the y axis of the form :math:`y - y_0 = 0` @@ -375,7 +440,8 @@ class YPlane(Plane): boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'}, optional Boundary condition that defines the behavior for particles hitting the surface. Defaults to transmissive boundary condition where particles - freely pass through the surface. + freely pass through the surface. Only axis-aligned periodicity is + supported, i.e., x-planes can only be paired with x-planes. y0 : float, optional Location of the plane name : str, optional @@ -388,6 +454,9 @@ class YPlane(Plane): boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'} Boundary condition that defines the behavior for particles hitting the surface. + periodic_surface : openmc.Surface + If a periodic boundary condition is used, the surface with which this + one is periodic with coefficients : dict Dictionary of surface coefficients id : int @@ -448,6 +517,23 @@ class YPlane(Plane): return (np.array([-np.inf, self.y0, -np.inf]), np.array([np.inf, np.inf, np.inf])) + def evaluate(self, point): + """Evaluate the surface equation at a given point. + + Parameters + ---------- + point : 3-tuple of float + The Cartesian coordinates, :math:`(x',y',z')`, at which the surface + equation should be evaluated. + + Returns + ------- + float + :math:`y' - y_0` + + """ + return point[1] - self.y0 + class ZPlane(Plane): """A plane perpendicular to the z axis of the form :math:`z - z_0 = 0` @@ -460,7 +546,8 @@ class ZPlane(Plane): boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'}, optional Boundary condition that defines the behavior for particles hitting the surface. Defaults to transmissive boundary condition where particles - freely pass through the surface. + freely pass through the surface. Only axis-aligned periodicity is + supported, i.e., x-planes can only be paired with x-planes. z0 : float, optional Location of the plane. Defaults to 0. name : str, optional @@ -473,6 +560,9 @@ class ZPlane(Plane): boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'} Boundary condition that defines the behavior for particles hitting the surface. + periodic_surface : openmc.Surface + If a periodic boundary condition is used, the surface with which this + one is periodic with coefficients : dict Dictionary of surface coefficients id : int @@ -533,6 +623,23 @@ class ZPlane(Plane): return (np.array([-np.inf, -np.inf, self.z0]), np.array([np.inf, np.inf, np.inf])) + def evaluate(self, point): + """Evaluate the surface equation at a given point. + + Parameters + ---------- + point : 3-tuple of float + The Cartesian coordinates, :math:`(x',y',z')`, at which the surface + equation should be evaluated. + + Returns + ------- + float + :math:`z' - z_0` + + """ + return point[2] - self.z0 + class Cylinder(Surface): """A cylinder whose length is parallel to the x-, y-, or z-axis. @@ -542,7 +649,7 @@ class Cylinder(Surface): surface_id : int, optional Unique identifier for the surface. If not specified, an identifier will automatically be assigned. - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'}, optional + boundary_type : {'transmission, 'vacuum', 'reflective'}, optional Boundary condition that defines the behavior for particles hitting the surface. Defaults to transmissive boundary condition where particles freely pass through the surface. @@ -556,7 +663,7 @@ class Cylinder(Surface): ---------- r : float Radius of the cylinder - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'} + boundary_type : {'transmission, 'vacuum', 'reflective'} Boundary condition that defines the behavior for particles hitting the surface. coefficients : dict @@ -598,7 +705,7 @@ class XCylinder(Cylinder): surface_id : int, optional Unique identifier for the surface. If not specified, an identifier will automatically be assigned. - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'}, optional + boundary_type : {'transmission, 'vacuum', 'reflective'}, optional Boundary condition that defines the behavior for particles hitting the surface. Defaults to transmissive boundary condition where particles freely pass through the surface. @@ -618,7 +725,7 @@ class XCylinder(Cylinder): y-coordinate of the center of the cylinder z0 : float z-coordinate of the center of the cylinder - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'} + boundary_type : {'transmission, 'vacuum', 'reflective'} Boundary condition that defines the behavior for particles hitting the surface. coefficients : dict @@ -691,6 +798,25 @@ class XCylinder(Cylinder): return (np.array([-np.inf, -np.inf, -np.inf]), np.array([np.inf, np.inf, np.inf])) + def evaluate(self, point): + """Evaluate the surface equation at a given point. + + Parameters + ---------- + point : 3-tuple of float + The Cartesian coordinates, :math:`(x',y',z')`, at which the surface + equation should be evaluated. + + Returns + ------- + float + :math:`(y' - y_0)^2 + (z' - z_0)^2 - R^2` + + """ + y = point[1] - self.y0 + z = point[2] - self.z0 + return y**2 + z**2 - self.r**2 + class YCylinder(Cylinder): """An infinite cylinder whose length is parallel to the y-axis of the form @@ -701,7 +827,7 @@ class YCylinder(Cylinder): surface_id : int, optional Unique identifier for the surface. If not specified, an identifier will automatically be assigned. - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'}, optional + boundary_type : {'transmission, 'vacuum', 'reflective'}, optional Boundary condition that defines the behavior for particles hitting the surface. Defaults to transmissive boundary condition where particles freely pass through the surface. @@ -721,7 +847,7 @@ class YCylinder(Cylinder): x-coordinate of the center of the cylinder z0 : float z-coordinate of the center of the cylinder - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'} + boundary_type : {'transmission, 'vacuum', 'reflective'} Boundary condition that defines the behavior for particles hitting the surface. coefficients : dict @@ -794,6 +920,25 @@ class YCylinder(Cylinder): return (np.array([-np.inf, -np.inf, -np.inf]), np.array([np.inf, np.inf, np.inf])) + def evaluate(self, point): + """Evaluate the surface equation at a given point. + + Parameters + ---------- + point : 3-tuple of float + The Cartesian coordinates, :math:`(x',y',z')`, at which the surface + equation should be evaluated. + + Returns + ------- + float + :math:`(x' - x_0)^2 + (z' - z_0)^2 - R^2` + + """ + x = point[0] - self.x0 + z = point[2] - self.z0 + return x**2 + z**2 - self.r**2 + class ZCylinder(Cylinder): """An infinite cylinder whose length is parallel to the z-axis of the form @@ -804,7 +949,7 @@ class ZCylinder(Cylinder): surface_id : int, optional Unique identifier for the surface. If not specified, an identifier will automatically be assigned. - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'}, optional + boundary_type : {'transmission, 'vacuum', 'reflective'}, optional Boundary condition that defines the behavior for particles hitting the surface. Defaults to transmissive boundary condition where particles freely pass through the surface. @@ -824,7 +969,7 @@ class ZCylinder(Cylinder): x-coordinate of the center of the cylinder y0 : float y-coordinate of the center of the cylinder - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'} + boundary_type : {'transmission, 'vacuum', 'reflective'} Boundary condition that defines the behavior for particles hitting the surface. coefficients : dict @@ -897,6 +1042,25 @@ class ZCylinder(Cylinder): return (np.array([-np.inf, -np.inf, -np.inf]), np.array([np.inf, np.inf, np.inf])) + def evaluate(self, point): + """Evaluate the surface equation at a given point. + + Parameters + ---------- + point : 3-tuple of float + The Cartesian coordinates, :math:`(x',y',z')`, at which the surface + equation should be evaluated. + + Returns + ------- + float + :math:`(x' - x_0)^2 + (y' - y_0)^2 - R^2` + + """ + x = point[0] - self.x0 + y = point[1] - self.y0 + return x**2 + y**2 - self.r**2 + class Sphere(Surface): """A sphere of the form :math:`(x - x_0)^2 + (y - y_0)^2 + (z - z_0)^2 = R^2`. @@ -906,7 +1070,7 @@ class Sphere(Surface): surface_id : int, optional Unique identifier for the surface. If not specified, an identifier will automatically be assigned. - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'}, optional + boundary_type : {'transmission, 'vacuum', 'reflective'}, optional Boundary condition that defines the behavior for particles hitting the surface. Defaults to transmissive boundary condition where particles freely pass through the surface. @@ -931,7 +1095,7 @@ class Sphere(Surface): z-coordinate of the center of the sphere R : float Radius of the sphere - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'} + boundary_type : {'transmission, 'vacuum', 'reflective'} Boundary condition that defines the behavior for particles hitting the surface. coefficients : dict @@ -1025,6 +1189,26 @@ class Sphere(Surface): return (np.array([-np.inf, -np.inf, -np.inf]), np.array([np.inf, np.inf, np.inf])) + def evaluate(self, point): + """Evaluate the surface equation at a given point. + + Parameters + ---------- + point : 3-tuple of float + The Cartesian coordinates, :math:`(x',y',z')`, at which the surface + equation should be evaluated. + + Returns + ------- + float + :math:`(x' - x_0)^2 + (y' - y_0)^2 + (z' - z_0)^2 - R^2` + + """ + x = point[0] - self.x0 + y = point[1] - self.y0 + z = point[2] - self.z0 + return x**2 + y**2 + z**2 - self.r**2 + class Cone(Surface): """A conical surface parallel to the x-, y-, or z-axis. @@ -1034,7 +1218,7 @@ class Cone(Surface): surface_id : int, optional Unique identifier for the surface. If not specified, an identifier will automatically be assigned. - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'}, optional + boundary_type : {'transmission, 'vacuum', 'reflective'}, optional Boundary condition that defines the behavior for particles hitting the surface. Defaults to transmissive boundary condition where particles freely pass through the surface. @@ -1059,7 +1243,7 @@ class Cone(Surface): z-coordinate of the apex R2 : float Parameter related to the aperature - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'} + boundary_type : {'transmission, 'vacuum', 'reflective'} Boundary condition that defines the behavior for particles hitting the surface. coefficients : dict @@ -1131,7 +1315,7 @@ class XCone(Cone): surface_id : int, optional Unique identifier for the surface. If not specified, an identifier will automatically be assigned. - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'}, optional + boundary_type : {'transmission, 'vacuum', 'reflective'}, optional Boundary condition that defines the behavior for particles hitting the surface. Defaults to transmissive boundary condition where particles freely pass through the surface. @@ -1156,7 +1340,7 @@ class XCone(Cone): z-coordinate of the apex R2 : float Parameter related to the aperature - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'} + boundary_type : {'transmission, 'vacuum', 'reflective'} Boundary condition that defines the behavior for particles hitting the surface. coefficients : dict @@ -1177,6 +1361,26 @@ class XCone(Cone): self._type = 'x-cone' + def evaluate(self, point): + """Evaluate the surface equation at a given point. + + Parameters + ---------- + point : 3-tuple of float + The Cartesian coordinates, :math:`(x',y',z')`, at which the surface + equation should be evaluated. + + Returns + ------- + float + :math:`(y' - y_0)^2 + (z' - z_0)^2 - R^2(x' - x_0)^2` + + """ + x = point[0] - self.x0 + y = point[1] - self.y0 + z = point[2] - self.z0 + return y**2 + z**2 - self.r2*x**2 + class YCone(Cone): """A cone parallel to the y-axis of the form :math:`(x - x_0)^2 + (z - z_0)^2 = @@ -1187,7 +1391,7 @@ class YCone(Cone): surface_id : int, optional Unique identifier for the surface. If not specified, an identifier will automatically be assigned. - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'}, optional + boundary_type : {'transmission, 'vacuum', 'reflective'}, optional Boundary condition that defines the behavior for particles hitting the surface. Defaults to transmissive boundary condition where particles freely pass through the surface. @@ -1212,7 +1416,7 @@ class YCone(Cone): z-coordinate of the apex R2 : float Parameter related to the aperature - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'} + boundary_type : {'transmission, 'vacuum', 'reflective'} Boundary condition that defines the behavior for particles hitting the surface. coefficients : dict @@ -1233,6 +1437,26 @@ class YCone(Cone): self._type = 'y-cone' + def evaluate(self, point): + """Evaluate the surface equation at a given point. + + Parameters + ---------- + point : 3-tuple of float + The Cartesian coordinates, :math:`(x',y',z')`, at which the surface + equation should be evaluated. + + Returns + ------- + float + :math:`(x' - x_0)^2 + (z' - z_0)^2 - R^2(y' - y_0)^2` + + """ + x = point[0] - self.x0 + y = point[1] - self.y0 + z = point[2] - self.z0 + return x**2 + z**2 - self.r2*y**2 + class ZCone(Cone): """A cone parallel to the x-axis of the form :math:`(x - x_0)^2 + (y - y_0)^2 = @@ -1243,7 +1467,7 @@ class ZCone(Cone): surface_id : int, optional Unique identifier for the surface. If not specified, an identifier will automatically be assigned. - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'}, optional + boundary_type : {'transmission, 'vacuum', 'reflective'}, optional Boundary condition that defines the behavior for particles hitting the surface. Defaults to transmissive boundary condition where particles freely pass through the surface. @@ -1268,7 +1492,7 @@ class ZCone(Cone): z-coordinate of the apex R2 : float Parameter related to the aperature - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'} + boundary_type : {'transmission, 'vacuum', 'reflective'} Boundary condition that defines the behavior for particles hitting the surface. coefficients : dict @@ -1289,6 +1513,26 @@ class ZCone(Cone): self._type = 'z-cone' + def evaluate(self, point): + """Evaluate the surface equation at a given point. + + Parameters + ---------- + point : 3-tuple of float + The Cartesian coordinates, :math:`(x',y',z')`, at which the surface + equation should be evaluated. + + Returns + ------- + float + :math:`(x' - x_0)^2 + (y' - y_0)^2 - R^2(z' - z_0)^2` + + """ + x = point[0] - self.x0 + y = point[1] - self.y0 + z = point[2] - self.z0 + return x**2 + y**2 - self.r2*z**2 + class Quadric(Surface): """A surface of the form :math:`Ax^2 + By^2 + Cz^2 + Dxy + Eyz + Fxz + Gx + Hy + @@ -1434,6 +1678,27 @@ class Quadric(Surface): check_type('k coefficient', k, Real) self._coefficients['k'] = k + def evaluate(self, point): + """Evaluate the surface equation at a given point. + + Parameters + ---------- + point : 3-tuple of float + The Cartesian coordinates, :math:`(x',y',z')`, at which the surface + equation should be evaluated. + + Returns + ------- + float + :math:`Ax'^2 + By'^2 + Cz'^2 + Dx'y' + Ey'z' + Fx'z' + Gx' + Hy' + + Jz' + K = 0` + + """ + x, y, z = point + return x*(self.a*x + self.d*y + self.g) + \ + y*(self.b*y + self.e*z + self.h) + \ + z*(self.c*z + self.f*x + self.j) + self.k + class Halfspace(Region): """A positive or negative half-space region. @@ -1479,6 +1744,24 @@ class Halfspace(Region): def __invert__(self): return -self.surface if self.side == '+' else +self.surface + def __contains__(self, point): + """Check whether a point is contained in the half-space. + + Parameters + ---------- + point : 3-tuple of float + Cartesian coordinates, :math:`(x',y',z')`, of the point + + Returns + ------- + bool + Whether the point is in the half-space + + """ + + val = self.surface.evaluate(point) + return val >= 0. if self.side == '+' else val < 0. + @property def surface(self): return self._surface diff --git a/openmc/tallies.py b/openmc/tallies.py index 9559adcade..af19549a7c 100644 --- a/openmc/tallies.py +++ b/openmc/tallies.py @@ -129,51 +129,6 @@ class Tally(object): self._sp_filename = None self._results_read = False - def __deepcopy__(self, memo): - existing = memo.get(id(self)) - - # If this is the first time we have tried to copy this object, create a copy - if existing is None: - clone = type(self).__new__(type(self)) - clone.id = self.id - clone.name = self.name - clone.estimator = self.estimator - clone.num_realizations = self.num_realizations - clone._sum = copy.deepcopy(self._sum, memo) - clone._sum_sq = copy.deepcopy(self._sum_sq, memo) - clone._mean = copy.deepcopy(self._mean, memo) - clone._std_dev = copy.deepcopy(self._std_dev, memo) - clone._with_summary = self.with_summary - clone._with_batch_statistics = self.with_batch_statistics - clone._derived = self.derived - clone._sparse = self.sparse - clone._sp_filename = self._sp_filename - clone._results_read = self._results_read - - clone._filters = [] - for self_filter in self.filters: - clone.filters.append(copy.deepcopy(self_filter, memo)) - - clone._nuclides = [] - for nuclide in self.nuclides: - clone.nuclides.append(copy.deepcopy(nuclide, memo)) - - clone._scores = [] - for score in self.scores: - clone.scores.append(score) - - clone._triggers = [] - for trigger in self.triggers: - clone.triggers.append(trigger) - - memo[id(self)] = clone - - return clone - - # If this object has been copied before, return the first copy made - else: - return existing - def __eq__(self, other): if not isinstance(other, Tally): return False @@ -314,6 +269,10 @@ class Tally(object): if not self._results_read: import h5py + if h5py.__version__ == '2.6.0': + raise ImportError("h5py 2.6.0 has a known bug which makes it " + "incompatible with OpenMC's HDF5 files. " + "Please switch to a different version.") # Open the HDF5 statepoint file f = h5py.File(self._sp_filename, 'r') @@ -827,9 +786,7 @@ class Tally(object): # Search for each of this tally's scores in the other tally for score in self.scores: - if score not in other.scores: - all_scores_match = False - else: + if score in other.scores: no_scores_match = False # Search for each of the other tally's scores in this tally @@ -2877,7 +2834,7 @@ class Tally(object): return other * self**-1 - def __pos__(self): + def __abs__(self): """The absolute value of this tally. Returns @@ -3471,7 +3428,8 @@ class Tallies(cv.CheckedList): """ if not isinstance(tally, Tally): - msg = 'Unable to add a non-Tally "{0}" to the Tallies instance'.format(tally) + msg = 'Unable to add a non-Tally "{0}" to the ' \ + 'Tallies instance'.format(tally) raise TypeError(msg) if merge: @@ -3482,13 +3440,13 @@ class Tallies(cv.CheckedList): # If a mergeable tally is found if tally2.can_merge(tally): - # Replace tally 2 with the merged tally + # Replace tally2 with the merged tally merged_tally = tally2.merge(tally) self[i] = merged_tally merged = True break - # If not mergeable tally was found, simply add this tally + # If no mergeable tally was found, simply add this tally if not merged: super(Tallies, self).append(tally) diff --git a/openmc/trigger.py b/openmc/trigger.py index b8383bd271..537af1c8d8 100644 --- a/openmc/trigger.py +++ b/openmc/trigger.py @@ -39,25 +39,6 @@ class Trigger(object): self.threshold = threshold self._scores = [] - def __deepcopy__(self, memo): - existing = memo.get(id(self)) - - # If this is first time we have tried to copy this object, create a copy - if existing is None: - clone = type(self).__new__(type(self)) - clone._trigger_type = self._trigger_type - clone._threshold = self._threshold - - clone.scores = self.scores - - memo[id(self)] = clone - - return clone - - # If this object has been copied before, return the first copy made - else: - return existing - def __eq__(self, other): if str(self) == str(other): return True diff --git a/openmc/universe.py b/openmc/universe.py index 770e789da7..84a5acdcac 100644 --- a/openmc/universe.py +++ b/openmc/universe.py @@ -1,6 +1,7 @@ from collections import OrderedDict, Iterable from numbers import Integral from xml.etree import ElementTree as ET +import random import sys import warnings @@ -12,7 +13,6 @@ import openmc.checkvalue as cv if sys.version_info[0] >= 3: basestring = str - # A dictionary for storing IDs of cell elements that have already been written, # used to optimize the writing process WRITTEN_IDS = {} @@ -124,6 +124,149 @@ class Universe(object): else: self._name = '' + def find(self, point): + """Find cells/universes/lattices which contain a given point + + Parameters + ---------- + point : 3-tuple of float + Cartesian coordinates of the point + + Returns + ------- + list + Sequence of universes, cells, and lattices which are traversed to + find the given point + + """ + p = np.asarray(point) + for cell in self._cells.values(): + if p in cell: + if cell.fill_type in ('material', 'distribmat', 'void'): + return [self, cell] + elif cell.fill_type == 'universe': + if cell.translation is not None: + p -= cell.translation + if cell.rotation is not None: + p[:] = cell.rotation_matrix.dot(p) + return [self, cell] + cell.fill.find(p) + else: + return [self, cell] + cell.fill.find(p) + return [] + + def plot(self, center=(0., 0., 0.), width=(1., 1.), pixels=(200, 200), + basis='xy', color_by='cell', colors=None, filename=None, seed=None): + """Display a slice plot of the universe. + + Parameters + ---------- + center : Iterable of float + Coordinates at the center of the plot + width : Iterable of float + Width of the plot in each basis direction + pixels : Iterable of int + Number of pixels to use in each basis direction + basis : {'xy', 'xz', 'yz'} + The basis directions for the plot + color_by : {'cell', 'material'} + Indicate whether the plot should be colored by cell or by material + colors : dict + + Assigns colors to specific materials or cells. Keys are instances of + :class:`Cell` or :class:`Material` and values are RGB 3-tuples or RGBA + 4-tuples. Red, green, blue, and alpha should all be floats in the + range [0.0, 1.0], for example: + + .. code-block:: python + + # Make water blue + water = openmc.Cell(fill=h2o) + universe.plot(..., colors={water: (0., 0., 1.)) + + filename : str or None + Filename to save plot to. If no filename is given, the plot will be + displayed using the currently enabled matplotlib backend. + seed : hashable object or None + Hashable object which is used to seed the random number generator + used to select colors. If None, the generator is seeded from the + current time. + + """ + import matplotlib.pyplot as plt + + # Seed the random number generator + if seed is not None: + random.seed(seed) + + if colors is None: + # Create default dictionary if none supplied + colors = {} + else: + # Convert to RGBA if necessary + for obj, rgb in colors.items(): + if len(rgb) == 3: + colors[obj] = rgb + (1.0,) + + if basis == 'xy': + x_min = center[0] - 0.5*width[0] + x_max = center[0] + 0.5*width[0] + y_min = center[1] - 0.5*width[1] + y_max = center[1] + 0.5*width[1] + elif basis == 'yz': + # The x-axis will correspond to physical y and the y-axis will correspond to physical z + x_min = center[1] - 0.5*width[0] + x_max = center[1] + 0.5*width[0] + y_min = center[2] - 0.5*width[1] + y_max = center[2] + 0.5*width[1] + elif basis == 'xz': + # The y-axis will correspond to physical z + x_min = center[0] - 0.5*width[0] + x_max = center[0] + 0.5*width[0] + y_min = center[2] - 0.5*width[1] + y_max = center[2] + 0.5*width[1] + + # Determine locations to determine cells at + x_coords = np.linspace(x_min, x_max, pixels[0], endpoint=False) + \ + 0.5*(x_max - x_min)/pixels[0] + y_coords = np.linspace(y_max, y_min, pixels[1], endpoint=False) - \ + 0.5*(y_max - y_min)/pixels[1] + + # Search for locations and assign colors + img = np.zeros(pixels + (4,)) # Use RGBA form + for i, x in enumerate(x_coords): + for j, y in enumerate(y_coords): + if basis == 'xy': + path = self.find((x, y, center[2])) + elif basis == 'yz': + path = self.find((center[0], x, y)) + elif basis == 'xz': + path = self.find((x, center[1], y)) + + if len(path) > 0: + try: + if color_by == 'cell': + obj = path[-1] + elif color_by == 'material': + if path[-1].fill_type == 'material': + obj = path[-1].fill + else: + continue + except AttributeError: + continue + if obj not in colors: + colors[obj] = (random.random(), random.random(), + random.random(), 1.0) + img[j,i,:] = colors[obj] + + # Display image + plt.imshow(img, extent=(x_min, x_max, y_min, y_max)) + + # Show or save the plot + if filename is None: + plt.show() + else: + plt.savefig(filename) + def add_cell(self, cell): """Add a cell to the universe. diff --git a/setup.py b/setup.py index 770f280ad1..99e465c9d4 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,8 @@ except ImportError: kwargs = {'name': 'openmc', 'version': '0.7.1', - 'packages': ['openmc', 'openmc.data', 'openmc.mgxs', 'openmc.stats'], + 'packages': ['openmc', 'openmc.data', 'openmc.mgxs', 'openmc.model', + 'openmc.stats'], 'scripts': glob.glob('scripts/openmc-*'), # Metadata diff --git a/src/constants.F90 b/src/constants.F90 index 4eaf0931e9..f1106f228a 100644 --- a/src/constants.F90 +++ b/src/constants.F90 @@ -281,7 +281,7 @@ module constants EVENT_ABSORB = 2 ! Tally score type - integer, parameter :: N_SCORE_TYPES = 22 + integer, parameter :: N_SCORE_TYPES = 20 integer, parameter :: & SCORE_FLUX = -1, & ! flux SCORE_TOTAL = -2, & ! total reaction rate @@ -291,20 +291,18 @@ module constants SCORE_SCATTER_PN = -6, & ! system for scoring 0th through nth moment SCORE_NU_SCATTER_N = -7, & ! arbitrary nu-scattering moment SCORE_NU_SCATTER_PN = -8, & ! system for scoring 0th through nth nu-scatter moment - SCORE_TRANSPORT = -9, & ! transport reaction rate - SCORE_N_1N = -10, & ! (n,1n) rate - SCORE_ABSORPTION = -11, & ! absorption rate - SCORE_FISSION = -12, & ! fission rate - SCORE_NU_FISSION = -13, & ! neutron production rate - SCORE_KAPPA_FISSION = -14, & ! fission energy production rate - SCORE_CURRENT = -15, & ! partial current - SCORE_FLUX_YN = -16, & ! angular moment of flux - SCORE_TOTAL_YN = -17, & ! angular moment of total reaction rate - SCORE_SCATTER_YN = -18, & ! angular flux-weighted scattering moment (0:N) - SCORE_NU_SCATTER_YN = -19, & ! angular flux-weighted nu-scattering moment (0:N) - SCORE_EVENTS = -20, & ! number of events - SCORE_DELAYED_NU_FISSION = -21, & ! delayed neutron production rate - SCORE_INVERSE_VELOCITY = -22 ! flux-weighted inverse velocity + SCORE_ABSORPTION = -9, & ! absorption rate + SCORE_FISSION = -10, & ! fission rate + SCORE_NU_FISSION = -11, & ! neutron production rate + SCORE_KAPPA_FISSION = -12, & ! fission energy production rate + SCORE_CURRENT = -13, & ! partial current + SCORE_FLUX_YN = -14, & ! angular moment of flux + SCORE_TOTAL_YN = -15, & ! angular moment of total reaction rate + SCORE_SCATTER_YN = -16, & ! angular flux-weighted scattering moment (0:N) + SCORE_NU_SCATTER_YN = -17, & ! angular flux-weighted nu-scattering moment (0:N) + SCORE_EVENTS = -18, & ! number of events + SCORE_DELAYED_NU_FISSION = -19, & ! delayed neutron production rate + SCORE_INVERSE_VELOCITY = -20 ! flux-weighted inverse velocity ! Maximum scattering order supported integer, parameter :: MAX_ANG_ORDER = 10 diff --git a/src/endf.F90 b/src/endf.F90 index 64f26539a9..ad5e97a033 100644 --- a/src/endf.F90 +++ b/src/endf.F90 @@ -34,10 +34,6 @@ contains string = "nu-scatter-n" case (SCORE_NU_SCATTER_PN) string = "nu-scatter-pn" - case (SCORE_TRANSPORT) - string = "transport" - case (SCORE_N_1N) - string = "n1n" case (SCORE_ABSORPTION) string = "absorption" case (SCORE_FISSION) diff --git a/src/geometry.F90 b/src/geometry.F90 index 5f37f1047b..767e2db108 100644 --- a/src/geometry.F90 +++ b/src/geometry.F90 @@ -408,6 +408,7 @@ contains real(8) :: v ! y-component of direction real(8) :: w ! z-component of direction real(8) :: norm ! "norm" of surface normal + real(8) :: xyz(3) ! Saved global coordinate integer :: i_surface ! index in surfaces logical :: found ! particle found in universe? class(Surface), pointer :: surf @@ -462,12 +463,13 @@ contains ! Score surface currents since reflection causes the direction of the ! particle to change -- artificially move the particle slightly back in - ! case the surface crossing in coincident with a mesh boundary + ! case the surface crossing is coincident with a mesh boundary if (active_current_tallies % size() > 0) then + xyz = p % coord(1) % xyz p % coord(1) % xyz = p % coord(1) % xyz - TINY_BIT * p % coord(1) % uvw call score_surface_current(p) - p % coord(1) % xyz = p % coord(1) % xyz + TINY_BIT * p % coord(1) % uvw + p % coord(1) % xyz = xyz end if ! Reflect particle off surface @@ -505,6 +507,70 @@ contains &// trim(to_str(surf%id))) end if return + elseif (surf % bc == BC_PERIODIC .and. run_mode /= MODE_PLOTTING) then + ! ======================================================================= + ! PERIODIC BOUNDARY + + ! Do not handle periodic boundary conditions on lower universes + if (p % n_coord /= 1) then + call handle_lost_particle(p, "Cannot transfer particle " & + // trim(to_str(p % id)) // " across surface in a lower universe.& + & Boundary conditions must be applied to universe 0.") + return + end if + + ! Score surface currents since reflection causes the direction of the + ! particle to change -- artificially move the particle slightly back in + ! case the surface crossing is coincident with a mesh boundary + + if (active_current_tallies % size() > 0) then + xyz = p % coord(1) % xyz + p % coord(1) % xyz = p % coord(1) % xyz - TINY_BIT * p % coord(1) % uvw + call score_surface_current(p) + p % coord(1) % xyz = xyz + end if + + select type (surf) + type is (SurfaceXPlane) + select type (opposite => surfaces(surf % i_periodic) % obj) + type is (SurfaceXPlane) + p % coord(1) % xyz(1) = opposite % x0 + end select + + type is (SurfaceYPlane) + select type (opposite => surfaces(surf % i_periodic) % obj) + type is (SurfaceYPlane) + p % coord(1) % xyz(2) = opposite % y0 + end select + + type is (SurfaceZPlane) + select type (opposite => surfaces(surf % i_periodic) % obj) + type is (SurfaceZPlane) + p % coord(1) % xyz(3) = opposite % z0 + end select + end select + + ! Reassign particle's surface + p % surface = sign(surf % i_periodic, p % surface) + + ! Figure out what cell particle is in now + p % n_coord = 1 + call find_cell(p, found) + if (.not. found) then + call handle_lost_particle(p, "Couldn't find particle after hitting & + &periodic boundary on surface " // trim(to_str(surf%id)) // ".") + return + end if + + ! Set previous coordinate going slightly past surface crossing + p % last_xyz = p % coord(1) % xyz + TINY_BIT * p % coord(1) % uvw + + ! Diagnostic message + if (verbosity >= 10 .or. trace) then + call write_message(" Hit periodic boundary on surface " & + // trim(to_str(surf%id))) + end if + return end if ! ========================================================================== diff --git a/src/geometry_header.F90 b/src/geometry_header.F90 index e4f220e3de..1d1826fbfb 100644 --- a/src/geometry_header.F90 +++ b/src/geometry_header.F90 @@ -1,6 +1,6 @@ module geometry_header - use constants, only: HALF, TWO, THREE + use constants, only: HALF, TWO, THREE, INFINITY implicit none @@ -204,20 +204,22 @@ contains real(8), intent(in) :: global_xyz(3) integer :: i_xyz(3) - real(8) :: xyz(3) ! global_xyz alias + real(8) :: xyz(3) ! global xyz relative to the center real(8) :: alpha ! Skewed coord axis real(8) :: xyz_t(3) ! Local xyz - real(8) :: dists(4) ! Squared distances from cell centers + real(8) :: d, d_min ! Squared distance from cell centers integer :: i, j, k ! Iterators - integer :: loc(1) ! Minimum distance index + integer :: k_min ! Minimum distance index - xyz = global_xyz + xyz(1) = global_xyz(1) - this % center(1) + xyz(2) = global_xyz(2) - this % center(2) ! Index z direction. if (this % is_3d) then - i_xyz(3) = ceiling((xyz(3) - this % center(3))/this % pitch(2) + HALF)& - + this % n_axial/2 + xyz(3) = global_xyz(3) - this % center(3) + i_xyz(3) = ceiling(xyz(3)/this % pitch(2) + HALF*this % n_axial) else + xyz(3) = global_xyz(3) i_xyz(3) = 1 end if @@ -236,28 +238,33 @@ contains ! the four possible cells. Regular hexagonal tiles form a centroidal ! Voronoi tessellation so the global xyz should be in the hexagonal cell ! that it is closest to the center of. This method is used over a - ! method that uses the remainders of the floor divisions above becasue it + ! method that uses the remainders of the floor divisions above because it ! provides better finite precision performance. Squared distances are ! used becasue they are more computationally efficient than normal ! distances. k = 1 - do i=0,1 - do j=0,1 - xyz_t = this % get_local_xyz(xyz, i_xyz + (/j, i, 0/)) - dists(k) = xyz_t(1)**2 + xyz_t(2)**2 + d_min = INFINITY + do i = 0, 1 + do j = 0, 1 + xyz_t = this % get_local_xyz(global_xyz, i_xyz + [j, i, 0]) + d = xyz_t(1)**2 + xyz_t(2)**2 + if (d < d_min) then + d_min = d + k_min = k + end if k = k + 1 end do end do ! Select the minimum squared distance which corresponds to the cell the ! coordinates are in. - loc = minloc(dists) - if (loc(1) == 2) then - i_xyz = i_xyz + (/1, 0, 0/) - else if (loc(1) == 3) then - i_xyz = i_xyz + (/0, 1, 0/) - else if (loc(1) == 4) then - i_xyz = i_xyz + (/1, 1, 0/) + if (k_min == 2) then + i_xyz(1) = i_xyz(1) + 1 + else if (k_min == 3) then + i_xyz(2) = i_xyz(2) + 1 + else if (k_min == 4) then + i_xyz(1) = i_xyz(1) + 1 + i_xyz(2) = i_xyz(2) + 1 end if end function get_inds_hex @@ -306,7 +313,7 @@ contains (i_xyz(1) - this % n_rings) * this % pitch(1) / TWO) if (this % is_3d) then local_xyz(3) = xyz(3) - this % center(3) & - + (this % n_axial/2 - i_xyz(3) + 1) * this % pitch(2) + + (HALF*this % n_axial - i_xyz(3) + HALF) * this % pitch(2) else local_xyz(3) = xyz(3) end if diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 1d4caf45c8..3c6143e5a0 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -1142,6 +1142,8 @@ contains integer :: universe_num integer :: n_cells_in_univ integer :: coeffs_reqd + integer :: i_xmin, i_xmax, i_ymin, i_ymax, i_zmin, i_zmax + real(8) :: xmin, xmax, ymin, ymax, zmin, zmax integer, allocatable :: temp_int_array(:) real(8) :: phi, theta, psi real(8), allocatable :: coeffs(:) @@ -1458,6 +1460,13 @@ contains call fatal_error("No surfaces found in geometry.xml!") end if + xmin = INFINITY + xmax = -INFINITY + ymin = INFINITY + ymax = -INFINITY + zmin = INFINITY + zmax = -INFINITY + ! Allocate cells array allocate(surfaces(n_surfaces)) @@ -1549,10 +1558,28 @@ contains select type(s) type is (SurfaceXPlane) s%x0 = coeffs(1) + + ! Determine outer surfaces + xmin = min(xmin, s % x0) + xmax = max(xmax, s % x0) + if (xmin == s % x0) i_xmin = i + if (xmax == s % x0) i_xmax = i type is (SurfaceYPlane) s%y0 = coeffs(1) + + ! Determine outer surfaces + ymin = min(ymin, s % y0) + ymax = max(ymax, s % y0) + if (ymin == s % y0) i_ymin = i + if (ymax == s % y0) i_ymax = i type is (SurfaceZPlane) s%z0 = coeffs(1) + + ! Determine outer surfaces + zmin = min(zmin, s % z0) + zmax = max(zmax, s % z0) + if (zmin == s % z0) i_zmin = i + if (zmax == s % z0) i_zmax = i type is (SurfacePlane) s%A = coeffs(1) s%B = coeffs(2) @@ -1619,11 +1646,19 @@ contains case ('reflective', 'reflect', 'reflecting') s%bc = BC_REFLECT boundary_exists = .true. + case ('periodic') + s%bc = BC_PERIODIC + boundary_exists = .true. + + ! Check for specification of periodic surface + if (check_for_node(node_surf, "periodic_surface_id")) then + call get_node_value(node_surf, "periodic_surface_id", & + s % i_periodic) + end if case default call fatal_error("Unknown boundary condition '" // trim(word) // & &"' specified on surface " // trim(to_str(s%id))) end select - ! Add surface to dictionary call surface_dict % add_key(s%id, i) end do @@ -1634,6 +1669,67 @@ contains call fatal_error("No boundary conditions were applied to any surfaces!") end if + ! Determine opposite side for periodic boundaries + do i = 1, size(surfaces) + if (surfaces(i) % obj % bc == BC_PERIODIC) then + select type (surf => surfaces(i) % obj) + type is (SurfaceXPlane) + if (surf % i_periodic == NONE) then + if (i == i_xmin) then + surf % i_periodic = i_xmax + elseif (i == i_xmax) then + surf % i_periodic = i_xmin + else + call fatal_error("Periodic boundary condition applied to & + &interior surface.") + end if + else + surf % i_periodic = surface_dict % get_key(surf % i_periodic) + end if + + type is (SurfaceYPlane) + if (surf % i_periodic == NONE) then + if (i == i_ymin) then + surf % i_periodic = i_ymax + elseif (i == i_ymax) then + surf % i_periodic = i_ymin + else + call fatal_error("Periodic boundary condition applied to & + &interior surface.") + end if + else + surf % i_periodic = surface_dict % get_key(surf % i_periodic) + end if + + type is (SurfaceZPlane) + if (surf % i_periodic == NONE) then + if (i == i_zmin) then + surf % i_periodic = i_zmax + elseif (i == i_zmax) then + surf % i_periodic = i_zmin + else + call fatal_error("Periodic boundary condition applied to & + &interior surface.") + end if + else + surf % i_periodic = surface_dict % get_key(surf % i_periodic) + end if + + class default + call fatal_error("Periodic boundary condition applied to & + &non-planar surface.") + end select + + ! Make sure opposite surface is also periodic + associate (surf => surfaces(i) % obj) + if (surfaces(surf % i_periodic) % obj % bc /= BC_PERIODIC) then + call fatal_error("Could not find matching surface for periodic & + &boundary on surface " // trim(to_str(surf % id)) // ".") + end if + end associate + end if + end do + ! ========================================================================== ! READ LATTICES FROM GEOMETRY.XML @@ -3464,22 +3560,12 @@ contains j = j + n_bins - 1 case('transport') - t % score_bins(j) = SCORE_TRANSPORT - - ! Set tally estimator to analog - t % estimator = ESTIMATOR_ANALOG - case ('diffusion') - call fatal_error("Diffusion score no longer supported for tallies, & + call fatal_error("Transport score no longer supported for tallies, & &please remove") - case ('n1n') - if (run_CE) then - t % score_bins(j) = SCORE_N_1N - ! Set tally estimator to analog - t % estimator = ESTIMATOR_ANALOG - else - call fatal_error("Cannot tally n1n rate in multi-group mode!") - end if + case ('n1n') + call fatal_error("n1n score no longer supported for tallies, & + &please remove") case ('n2n', '(n,2n)') t % score_bins(j) = N_2N diff --git a/src/mgxs_header.F90 b/src/mgxs_header.F90 index 88c1b23e25..750a8df983 100644 --- a/src/mgxs_header.F90 +++ b/src/mgxs_header.F90 @@ -1485,8 +1485,13 @@ module mgxs_header nuc % scatter % energy(gin) % data(gout) mult_num(gout, gin) = mult_num(gout, gin) + atom_density * & nuscatt - mult_denom(gout, gin) = mult_denom(gout,gin) + atom_density * & - nuscatt / nuc % scatter % mult(gin) % data(gout) + if (nuc % scatter % mult(gin) % data(gout) > ZERO) then + mult_denom(gout, gin) = mult_denom(gout,gin) + atom_density * & + nuscatt / nuc % scatter % mult(gin) % data(gout) + else + ! Avoid division by zero + mult_denom(gout, gin) = mult_denom(gout,gin) + atom_density + end if end do end do @@ -1722,10 +1727,16 @@ module mgxs_header nuc % scatter(iazi, ipol) % obj % energy(gin) % data(gout) mult_num(gout, gin, iazi, ipol) = mult_num(gout, gin, iazi, ipol) + & atom_density * nuscatt - mult_denom(gout, gin, iazi, ipol) = & - mult_denom(gout, gin, iazi, ipol) + & - atom_density * nuscatt / & - nuc % scatter(iazi, ipol) % obj % mult(gin) % data(gout) + if (nuc % scatter(iazi, ipol) % obj % mult(gin) % data(gout) > ZERO) then + mult_denom(gout, gin, iazi, ipol) = & + mult_denom(gout, gin, iazi, ipol) + & + atom_density * nuscatt / & + nuc % scatter(iazi, ipol) % obj % mult(gin) % data(gout) + else + ! Avoid division by zero + mult_denom(gout, gin, iazi, ipol) = & + mult_denom(gout,gin, iazi, ipol) + atom_density + end if end do end do end do diff --git a/src/output.F90 b/src/output.F90 index 6e29c718a0..d7dfe7e1c2 100644 --- a/src/output.F90 +++ b/src/output.F90 @@ -777,8 +777,6 @@ contains score_names(abs(SCORE_TOTAL)) = "Total Reaction Rate" score_names(abs(SCORE_SCATTER)) = "Scattering Rate" score_names(abs(SCORE_NU_SCATTER)) = "Scattering Production Rate" - score_names(abs(SCORE_TRANSPORT)) = "Transport Rate" - score_names(abs(SCORE_N_1N)) = "(n,1n) Rate" score_names(abs(SCORE_ABSORPTION)) = "Absorption Rate" score_names(abs(SCORE_FISSION)) = "Fission Rate" score_names(abs(SCORE_NU_FISSION)) = "Nu-Fission Rate" diff --git a/src/relaxng/geometry.rnc b/src/relaxng/geometry.rnc index 1bca009a61..49b90e4e54 100644 --- a/src/relaxng/geometry.rnc +++ b/src/relaxng/geometry.rnc @@ -23,8 +23,9 @@ element geometry { (element type { xsd:string { maxLength = "15" } } | attribute type { xsd:string { maxLength = "15" } }) & (element coeffs { list { xsd:double+ } } | attribute coeffs { list { xsd:double+ } }) & - (element boundary { ( "transmit" | "reflective" | "vacuum" ) } | - attribute boundary { ( "transmit" | "reflective" | "vacuum" ) })? + (element boundary { ( "transmit" | "reflective" | "vacuum" | "periodic" ) } | + attribute boundary { ( "transmit" | "reflective" | "vacuum" | "periodic" ) })? & + (element periodic_surface_id { xsd:int } | attribute periodic_surface_id { xsd:int })? }* & element lattice { diff --git a/src/relaxng/geometry.rng b/src/relaxng/geometry.rng index ba8eab73d3..4a4fa30ebc 100644 --- a/src/relaxng/geometry.rng +++ b/src/relaxng/geometry.rng @@ -191,6 +191,7 @@ transmit reflective vacuum + periodic @@ -198,10 +199,21 @@ transmit reflective vacuum + periodic + + + + + + + + + + diff --git a/src/surface_header.F90 b/src/surface_header.F90 index 4686552176..68e5144b7c 100644 --- a/src/surface_header.F90 +++ b/src/surface_header.F90 @@ -1,6 +1,6 @@ module surface_header - use constants, only: ONE, TWO, ZERO, HALF, INFINITY, FP_COINCIDENT + use constants, only: NONE, ONE, TWO, ZERO, HALF, INFINITY, FP_COINCIDENT implicit none @@ -15,7 +15,8 @@ module surface_header neighbor_pos(:), & ! List of cells on positive side neighbor_neg(:) ! List of cells on negative side integer :: bc ! Boundary condition - character(len=104) :: name = "" ! User-defined name + integer :: i_periodic = NONE ! Index of corresponding periodic surface + character(len=104) :: name = "" ! User-defined name contains procedure :: sense procedure :: reflect diff --git a/src/tally.F90 b/src/tally.F90 index c4eaf30c8c..0c41b6f987 100644 --- a/src/tally.F90 +++ b/src/tally.F90 @@ -346,30 +346,6 @@ contains end if - case (SCORE_TRANSPORT) - ! Only analog estimators are available. - ! Skip any event where the particle didn't scatter - if (p % event /= EVENT_SCATTER) cycle SCORE_LOOP - ! get material macros - macro_total = material_xs % total - macro_scatt = material_xs % total - material_xs % absorption - ! Score total rate - p1 scatter rate Note estimator needs to be - ! adjusted since tallying is only occuring when a scatter has - ! happened. Effectively this means multiplying the estimator by - ! total/scatter macro - score = (macro_total - p % mu * macro_scatt) * (ONE / macro_scatt) - - - case (SCORE_N_1N) - ! Only analog estimators are available. - ! Skip any event where the particle didn't scatter - if (p % event /= EVENT_SCATTER) cycle SCORE_LOOP - ! Skip any events where weight of particle changed - if (p % wgt /= p % last_wgt) cycle SCORE_LOOP - ! All events that reach this point are (n,1n) reactions - score = p % last_wgt - - case (SCORE_ABSORPTION) if (t % estimator == ESTIMATOR_ANALOG) then if (survival_biasing) then @@ -1021,20 +997,6 @@ contains end if - case (SCORE_TRANSPORT) - ! Only analog estimators are available. - ! Skip any event where the particle didn't scatter - if (p % event /= EVENT_SCATTER) cycle SCORE_LOOP - ! Score total rate - p1 scatter rate Note estimator needs to be - ! adjusted since tallying is only occuring when a scatter has - ! happened. Effectively this means multiplying the estimator by - ! total/scatter macro - score = (material_xs % total - p % mu * material_xs % elastic) - if (material_xs % elastic /= ZERO) then - score = score / material_xs % elastic - end if - - case (SCORE_ABSORPTION) if (t % estimator == ESTIMATOR_ANALOG) then if (survival_biasing) then diff --git a/tests/input_set.py b/tests/input_set.py index 2c6841e254..13c0e99a72 100644 --- a/tests/input_set.py +++ b/tests/input_set.py @@ -15,8 +15,10 @@ class InputSet(object): self.settings.export_to_xml() self.materials.export_to_xml() self.geometry.export_to_xml() - if self.tallies is not None: self.tallies.export_to_xml() - if self.plots is not None: self.plots.export_to_xml() + if self.tallies is not None: + self.tallies.export_to_xml() + if self.plots is not None: + self.plots.export_to_xml() def build_default_materials_and_geometry(self): # Define materials. @@ -82,7 +84,7 @@ class InputSet(object): hot_water.add_s_alpha_beta('HH2O', '71t') rpv_steel = openmc.Material(name='Reactor pressure vessel steel', - material_id=5) + material_id=5) rpv_steel.set_density('g/cm3', 7.9) rpv_steel.add_nuclide("Fe-54", 0.05437098, 'wo') rpv_steel.add_nuclide("Fe-56", 0.88500663, 'wo') @@ -113,7 +115,7 @@ class InputSet(object): rpv_steel.add_nuclide("Cu-65", 0.0006304, 'wo') lower_rad_ref = openmc.Material(name='Lower radial reflector', - material_id=6) + material_id=6) lower_rad_ref.set_density('g/cm3', 4.32) lower_rad_ref.add_nuclide("H-1", 0.0095661, 'wo') lower_rad_ref.add_nuclide("O-16", 0.0759107, 'wo') @@ -189,7 +191,8 @@ class InputSet(object): bot_plate.add_nuclide("Cr-54", 0.004612692337, 'wo') bot_plate.add_s_alpha_beta('HH2O', '71t') - bot_nozzle = openmc.Material(name='Bottom nozzle region', material_id=9) + bot_nozzle = openmc.Material(name='Bottom nozzle region', + material_id=9) bot_nozzle.set_density('g/cm3', 2.53) bot_nozzle.add_nuclide("H-1", 0.0245014, 'wo') bot_nozzle.add_nuclide("O-16", 0.1944274, 'wo') @@ -252,7 +255,8 @@ class InputSet(object): top_fa.add_nuclide("Zr-96", 0.02511169542, 'wo') top_fa.add_s_alpha_beta('HH2O', '71t') - bot_fa = openmc.Material(name='Bottom of fuel assemblies', material_id=12) + bot_fa = openmc.Material(name='Bottom of fuel assemblies', + material_id=12) bot_fa.set_density('g/cm3', 1.762) bot_fa.add_nuclide("H-1", 0.0292856, 'wo') bot_fa.add_nuclide("O-16", 0.2323919, 'wo') @@ -350,7 +354,6 @@ class InputSet(object): # Define fuel lattices. l100 = openmc.RectLattice(name='Fuel assembly (lower half)', lattice_id=100) - l100.dimension = (17, 17) l100.lower_left = (-10.71, -10.71) l100.pitch = (1.26, 1.26) l100.universes = [ @@ -384,7 +387,6 @@ class InputSet(object): l101 = openmc.RectLattice(name='Fuel assembly (upper half)', lattice_id=101) - l101.dimension = (17, 17) l101.lower_left = (-10.71, -10.71) l101.pitch = (1.26, 1.26) l101.universes = [ @@ -444,7 +446,6 @@ class InputSet(object): # Define core lattices l200 = openmc.RectLattice(name='Core lattice (lower half)', lattice_id=200) - l200.dimension = (21, 21) l200.lower_left = (-224.91, -224.91) l200.pitch = (21.42, 21.42) l200.universes = [ @@ -472,7 +473,6 @@ class InputSet(object): l201 = openmc.RectLattice(name='Core lattice (lower half)', lattice_id=201) - l201.dimension = (21, 21) l201.lower_left = (-224.91, -224.91) l201.pitch = (21.42, 21.42) l201.universes = [ @@ -570,6 +570,109 @@ class InputSet(object): self.plots.add_plot(plot) + +class PinCellInputSet(object): + def __init__(self): + self.settings = openmc.Settings() + self.materials = openmc.Materials() + self.geometry = openmc.Geometry() + self.tallies = None + self.plots = None + + def export(self): + self.settings.export_to_xml() + self.materials.export_to_xml() + self.geometry.export_to_xml() + if self.tallies is not None: + self.tallies.export_to_xml() + if self.plots is not None: + self.plots.export_to_xml() + + def build_default_materials_and_geometry(self): + # Define materials. + fuel = openmc.Material(name='Fuel') + fuel.set_density('g/cm3', 10.29769) + fuel.add_nuclide("U-234", 4.4843e-6) + fuel.add_nuclide("U-235", 5.5815e-4) + fuel.add_nuclide("U-238", 2.2408e-2) + fuel.add_nuclide("O-16", 4.5829e-2) + + clad = openmc.Material(name='Cladding') + clad.set_density('g/cm3', 6.55) + clad.add_nuclide("Zr-90", 2.1827e-2) + clad.add_nuclide("Zr-91", 4.7600e-3) + clad.add_nuclide("Zr-92", 7.2758e-3) + clad.add_nuclide("Zr-94", 7.3734e-3) + clad.add_nuclide("Zr-96", 1.1879e-3) + + hot_water = openmc.Material(name='Hot borated water') + hot_water.set_density('g/cm3', 0.740582) + hot_water.add_nuclide("H-1", 4.9457e-2) + hot_water.add_nuclide("O-16", 2.4672e-2) + hot_water.add_nuclide("B-10", 8.0042e-6) + hot_water.add_nuclide("B-11", 3.2218e-5) + hot_water.add_s_alpha_beta('HH2O', '71t') + + # Define the materials file. + self.materials.default_xs = '71c' + self.materials += (fuel, clad, hot_water) + + # Instantiate ZCylinder surfaces + fuel_or = openmc.ZCylinder(x0=0, y0=0, R=0.39218, name='Fuel OR') + clad_or = openmc.ZCylinder(x0=0, y0=0, R=0.45720, name='Clad OR') + left = openmc.XPlane(x0=-0.63, name='left') + right = openmc.XPlane(x0=0.63, name='right') + bottom = openmc.YPlane(y0=-0.63, name='bottom') + top = openmc.YPlane(y0=0.63, name='top') + + left.boundary_type = 'reflective' + right.boundary_type = 'reflective' + top.boundary_type = 'reflective' + bottom.boundary_type = 'reflective' + + # Instantiate Cells + fuel_pin = openmc.Cell(name='cell 1') + cladding = openmc.Cell(name='cell 3') + water = openmc.Cell(name='cell 2') + + # Use surface half-spaces to define regions + fuel_pin.region = -fuel_or + cladding.region = +fuel_or & -clad_or + water.region = +clad_or & +left & -right & +bottom & -top + + # Register Materials with Cells + fuel_pin.fill = fuel + cladding.fill = clad + water.fill = hot_water + + # Instantiate Universe + root = openmc.Universe(universe_id=0, name='root universe') + + # Register Cells with Universe + root.add_cells([fuel_pin, cladding, water]) + + # Instantiate a Geometry, register the root Universe, and export to XML + self.geometry.root_universe = root + + def build_default_settings(self): + self.settings.batches = 10 + self.settings.inactive = 5 + self.settings.particles = 100 + self.settings.source = Source(space=Box([-0.63, -0.63, -1], + [0.63, 0.63, 1], + only_fissionable=True)) + + def build_defualt_plots(self): + plot = openmc.Plot() + plot.filename = 'mat' + plot.origin = (0.0, 0.0, 0) + plot.width = (1.26, 1.26) + plot.pixels = (300, 300) + plot.color = 'mat' + + self.plots.add_plot(plot) + + class MGInputSet(InputSet): def build_default_materials_and_geometry(self): # Define materials needed for 1D/1G slab problem @@ -595,21 +698,21 @@ class MGInputSet(InputSet): # Define surfaces. # Assembly/Problem Boundary - left = openmc.XPlane(x0=0.0, surface_id=200, - boundary_type='reflective') - right = openmc.XPlane(x0=10.0, surface_id=201, - boundary_type='reflective') + left = openmc.XPlane(x0=0.0, surface_id=200, + boundary_type='reflective') + right = openmc.XPlane(x0=10.0, surface_id=201, + boundary_type='reflective') bottom = openmc.YPlane(y0=0.0, surface_id=300, boundary_type='reflective') - top = openmc.YPlane(y0=10.0, surface_id=301, - boundary_type='reflective') + top = openmc.YPlane(y0=10.0, surface_id=301, + boundary_type='reflective') - down = openmc.ZPlane(z0=0.0, surface_id=0, - boundary_type='reflective') + down = openmc.ZPlane(z0=0.0, surface_id=0, + boundary_type='reflective') fuel_clad_intfc = openmc.ZPlane(z0=2.0, surface_id=1) clad_lwtr_intfc = openmc.ZPlane(z0=2.4, surface_id=2) - up = openmc.ZPlane(z0=5.0, surface_id=3, - boundary_type='reflective') + up = openmc.ZPlane(z0=5.0, surface_id=3, + boundary_type='reflective') # Define cells c1 = openmc.Cell(cell_id=1) @@ -625,7 +728,7 @@ class MGInputSet(InputSet): # Define root universe. root = openmc.Universe(universe_id=0, name='root universe') - root.add_cells((c1,c2,c3)) + root.add_cells((c1, c2, c3)) # Assign root universe to geometry self.geometry.root_universe = root diff --git a/tests/test_asymmetric_lattice/test_asymmetric_lattice.py b/tests/test_asymmetric_lattice/test_asymmetric_lattice.py index 504cc4746b..40def1f623 100644 --- a/tests/test_asymmetric_lattice/test_asymmetric_lattice.py +++ b/tests/test_asymmetric_lattice/test_asymmetric_lattice.py @@ -24,7 +24,6 @@ class AsymmetricLatticeTestHarness(PyAPITestHarness): # Construct a 3x3 lattice of fuel assemblies core_lat = openmc.RectLattice(name='3x3 Core Lattice', lattice_id=202) - core_lat.dimension = (3, 3) core_lat.lower_left = (-32.13, -32.13) core_lat.pitch = (21.42, 21.42) core_lat.universes = [[fuel, water, water], diff --git a/tests/test_distribmat/results_true.dat b/tests/test_distribmat/results_true.dat index 15a00ee7d0..bf96784d71 100644 --- a/tests/test_distribmat/results_true.dat +++ b/tests/test_distribmat/results_true.dat @@ -3,7 +3,7 @@ k-combined: Cell ID = 11 Name = - Material = [2, 3, void, 2] + Fill = [2, 3, None, 2] Region = -10000 Rotation = None Translation = None diff --git a/tests/test_distribmat/test_distribmat.py b/tests/test_distribmat/test_distribmat.py index d8f78c5cf1..96d41c3fbe 100644 --- a/tests/test_distribmat/test_distribmat.py +++ b/tests/test_distribmat/test_distribmat.py @@ -45,7 +45,7 @@ class DistribmatTestHarness(PyAPITestHarness): r0 = openmc.ZCylinder(R=0.3) c11 = openmc.Cell(cell_id=11) c11.region = -r0 - c11.fill = [dense_fuel, light_fuel, 'void', dense_fuel] + c11.fill = [dense_fuel, light_fuel, None, dense_fuel] c12 = openmc.Cell(cell_id=12) c12.region = +r0 c12.fill = moderator @@ -53,7 +53,6 @@ class DistribmatTestHarness(PyAPITestHarness): fuel_univ.add_cells((c11, c12)) lat = openmc.RectLattice(lattice_id=101) - lat.dimension = [2, 2] lat.lower_left = [-2.0, -2.0] lat.pitch = [2.0, 2.0] lat.universes = [[fuel_univ]*2]*2 diff --git a/tests/test_mgxs_library_ce_to_mg/inputs_true.dat b/tests/test_mgxs_library_ce_to_mg/inputs_true.dat new file mode 100644 index 0000000000..9633a46a80 --- /dev/null +++ b/tests/test_mgxs_library_ce_to_mg/inputs_true.dat @@ -0,0 +1 @@ +34d5891f6f17c2d4b686b814ba61ba0045bc4289e278b1c3c47dbba59b83837fcfe15f2b8d58e7a2b07627b73d51e40348d70e9ed36dbb7cc94468d61c068c4c \ No newline at end of file diff --git a/tests/test_mgxs_library_ce_to_mg/results_true.dat b/tests/test_mgxs_library_ce_to_mg/results_true.dat new file mode 100644 index 0000000000..16441af8c7 --- /dev/null +++ b/tests/test_mgxs_library_ce_to_mg/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +1.094839E+00 1.203524E-02 diff --git a/tests/test_mgxs_library_ce_to_mg/test_mgxs_library_ce_to_mg.py b/tests/test_mgxs_library_ce_to_mg/test_mgxs_library_ce_to_mg.py new file mode 100644 index 0000000000..0f7cba4a87 --- /dev/null +++ b/tests/test_mgxs_library_ce_to_mg/test_mgxs_library_ce_to_mg.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python + +import os +import sys +import glob +import hashlib +sys.path.insert(0, os.pardir) +from testing_harness import PyAPITestHarness +from input_set import PinCellInputSet +import openmc +import openmc.mgxs + + +class MGXSTestHarness(PyAPITestHarness): + def _build_inputs(self): + # Set the input set to use the pincell model + self._input_set = PinCellInputSet() + + # Generate inputs using parent class routine + super(MGXSTestHarness, self)._build_inputs() + + # Initialize a two-group structure + energy_groups = openmc.mgxs.EnergyGroups(group_edges=[0, 0.625e-6, + 20.]) + + # Initialize MGXS Library for a few cross section types + self.mgxs_lib = openmc.mgxs.Library(self._input_set.geometry) + self.mgxs_lib.by_nuclide = False + self.mgxs_lib.mgxs_types = ['total', 'absorption', 'nu-fission matrix', + 'nu-scatter matrix', 'multiplicity matrix'] + self.mgxs_lib.energy_groups = energy_groups + self.mgxs_lib.correction = None + self.mgxs_lib.legendre_order = 3 + self.mgxs_lib.domain_type = 'material' + self.mgxs_lib.build_library() + + # Initialize a tallies file + self._input_set.tallies = openmc.Tallies() + self.mgxs_lib.add_to_tallies_file(self._input_set.tallies, merge=False) + self._input_set.tallies.export_to_xml() + + def _run_openmc(self): + # Initial run + if self._opts.mpi_exec is not None: + returncode = openmc.run(mpi_procs=self._opts.mpi_np, + openmc_exec=self._opts.exe, + mpi_exec=self._opts.mpi_exec) + + else: + returncode = openmc.run(openmc_exec=self._opts.exe) + + assert returncode == 0, 'CE OpenMC calculation did not exit' \ + 'successfully.' + + # Build MG Inputs + # Get data needed to execute Library calculations. + statepoint = glob.glob(os.path.join(os.getcwd(), self._sp_name))[0] + sp = openmc.StatePoint(statepoint) + self.mgxs_lib.load_from_statepoint(sp) + self._input_set.mgxs_file, self._input_set.materials, \ + self._input_set.geometry = self.mgxs_lib.create_mg_mode() + + # Modify settings so we can run in MG mode + self._input_set.settings.cross_sections = './mgxs.xml' + self._input_set.settings.energy_mode = 'multi-group' + + # Write modified input files + self._input_set.settings.export_to_xml() + self._input_set.geometry.export_to_xml() + self._input_set.materials.export_to_xml() + self._input_set.mgxs_file.export_to_xml() + # Dont need tallies.xml, so remove the file + if os.path.exists('./tallies.xml'): + os.remove('./tallies.xml') + + # Re-run MG mode. + if self._opts.mpi_exec is not None: + returncode = openmc.run(mpi_procs=self._opts.mpi_np, + openmc_exec=self._opts.exe, + mpi_exec=self._opts.mpi_exec) + + else: + returncode = openmc.run(openmc_exec=self._opts.exe) + + def _cleanup(self): + super(MGXSTestHarness, self)._cleanup() + f = os.path.join(os.getcwd(), 'mgxs.xml') + if os.path.exists(f): + os.remove(f) + + +if __name__ == '__main__': + harness = MGXSTestHarness('statepoint.10.*', False) + harness.main() diff --git a/tests/test_mgxs_library_condense/inputs_true.dat b/tests/test_mgxs_library_condense/inputs_true.dat index 3643c9a2ef..79ca0ec660 100644 --- a/tests/test_mgxs_library_condense/inputs_true.dat +++ b/tests/test_mgxs_library_condense/inputs_true.dat @@ -1 +1 @@ -104e7fb527770ac5d3fc636da7716e8fb05d55761253d30516c899f466e6b38ffd881611a3d0cdf65c6af058c32f6f6758c68782be7a170d21024bdae751862f \ No newline at end of file +317a63a9dd3bfd84e969667b00f46018e56c04c356461a75103f63569e6b70c84d0da7f5e611faaf1b2631330b05ab4346223d3d843018ce0ce8876671a450c0 \ No newline at end of file diff --git a/tests/test_mgxs_library_condense/results_true.dat b/tests/test_mgxs_library_condense/results_true.dat index 184be68bfa..13c277b15f 100644 --- a/tests/test_mgxs_library_condense/results_true.dat +++ b/tests/test_mgxs_library_condense/results_true.dat @@ -1,85 +1,108 @@ material group in nuclide mean std. dev. -0 1 1 total 0.412084 0.02359 material group in nuclide mean std. dev. -0 1 1 total 0.076425 0.003691 material group in group out nuclide moment mean std. dev. -0 1 1 1 total P0 0.384780 0.022253 -1 1 1 1 total P1 0.039277 0.004308 -2 1 1 1 total P2 0.017574 0.002402 -3 1 1 1 total P3 0.012203 0.002164 material group out nuclide mean std. dev. -0 1 1 total 1.0 0.055333 material group in nuclide mean std. dev. -0 2 1 total 0.241262 0.00841 material group in nuclide mean std. dev. -0 2 1 total 0.0 0.0 material group in group out nuclide moment mean std. dev. -0 2 1 1 total P0 0.272369 0.006872 -1 2 1 1 total P1 0.031107 0.005483 -2 2 1 1 total P2 0.025999 0.006151 -3 2 1 1 total P3 0.003219 0.003312 material group out nuclide mean std. dev. -0 2 1 total 0.0 0.0 material group in nuclide mean std. dev. -0 3 1 total 0.400028 0.034667 material group in nuclide mean std. dev. -0 3 1 total 0.0 0.0 material group in group out nuclide moment mean std. dev. -0 3 1 1 total P0 0.794999 0.036548 -1 3 1 1 total P1 0.401537 0.016175 -2 3 1 1 total P2 0.143623 0.008719 -3 3 1 1 total P3 0.001991 0.004433 material group out nuclide mean std. dev. -0 3 1 total 0.0 0.0 material group in nuclide mean std. dev. -0 4 1 total 0.377402 0.072937 material group in nuclide mean std. dev. -0 4 1 total 0.0 0.0 material group in group out nuclide moment mean std. dev. -0 4 1 1 total P0 0.727311 0.080096 -1 4 1 1 total P1 0.355839 0.037901 -2 4 1 1 total P2 0.124483 0.015823 -3 4 1 1 total P3 0.012168 0.006224 material group out nuclide mean std. dev. -0 4 1 total 0.0 0.0 material group in nuclide mean std. dev. -0 5 1 total 0.0 0.0 material group in nuclide mean std. dev. -0 5 1 total 0.0 0.0 material group in group out nuclide moment mean std. dev. -0 5 1 1 total P0 0.0 0.0 -1 5 1 1 total P1 0.0 0.0 -2 5 1 1 total P2 0.0 0.0 -3 5 1 1 total P3 0.0 0.0 material group out nuclide mean std. dev. -0 5 1 total 0.0 0.0 material group in nuclide mean std. dev. -0 6 1 total 0.0 0.0 material group in nuclide mean std. dev. -0 6 1 total 0.0 0.0 material group in group out nuclide moment mean std. dev. -0 6 1 1 total P0 0.0 0.0 -1 6 1 1 total P1 0.0 0.0 -2 6 1 1 total P2 0.0 0.0 -3 6 1 1 total P3 0.0 0.0 material group out nuclide mean std. dev. -0 6 1 total 0.0 0.0 material group in nuclide mean std. dev. -0 7 1 total 0.0 0.0 material group in nuclide mean std. dev. -0 7 1 total 0.0 0.0 material group in group out nuclide moment mean std. dev. -0 7 1 1 total P0 0.0 0.0 -1 7 1 1 total P1 0.0 0.0 -2 7 1 1 total P2 0.0 0.0 -3 7 1 1 total P3 0.0 0.0 material group out nuclide mean std. dev. -0 7 1 total 0.0 0.0 material group in nuclide mean std. dev. -0 8 1 total 0.0 0.0 material group in nuclide mean std. dev. -0 8 1 total 0.0 0.0 material group in group out nuclide moment mean std. dev. -0 8 1 1 total P0 0.0 0.0 -1 8 1 1 total P1 0.0 0.0 -2 8 1 1 total P2 0.0 0.0 -3 8 1 1 total P3 0.0 0.0 material group out nuclide mean std. dev. -0 8 1 total 0.0 0.0 material group in nuclide mean std. dev. -0 9 1 total 0.600536 0.748875 material group in nuclide mean std. dev. -0 9 1 total 0.0 0.0 material group in group out nuclide moment mean std. dev. -0 9 1 1 total P0 0.720380 0.771015 -1 9 1 1 total P1 0.119844 0.184691 -2 9 1 1 total P2 0.038522 0.064485 -3 9 1 1 total P3 0.056023 0.050595 material group out nuclide mean std. dev. -0 9 1 total 0.0 0.0 material group in nuclide mean std. dev. -0 10 1 total 0.235515 0.613974 material group in nuclide mean std. dev. -0 10 1 total 0.0 0.0 material group in group out nuclide moment mean std. dev. -0 10 1 1 total P0 0.501009 0.708534 -1 10 1 1 total P1 0.265494 0.375465 -2 10 1 1 total P2 0.141979 0.200788 -3 10 1 1 total P3 0.074258 0.105017 material group out nuclide mean std. dev. -0 10 1 total 0.0 0.0 material group in nuclide mean std. dev. -0 11 1 total 0.510145 0.741941 material group in nuclide mean std. dev. -0 11 1 total 0.0 0.0 material group in group out nuclide moment mean std. dev. -0 11 1 1 total P0 0.804661 0.817658 -1 11 1 1 total P1 0.312803 0.315315 -2 11 1 1 total P2 0.168113 0.172935 -3 11 1 1 total P3 0.003808 0.037911 material group out nuclide mean std. dev. -0 11 1 total 0.0 0.0 material group in nuclide mean std. dev. -0 12 1 total 0.73836 0.825631 material group in nuclide mean std. dev. -0 12 1 total 0.0 0.0 material group in group out nuclide moment mean std. dev. -0 12 1 1 total P0 0.943429 0.856119 -1 12 1 1 total P1 0.220164 0.163180 -2 12 1 1 total P2 0.052884 0.042440 -3 12 1 1 total P3 0.039939 0.032867 material group out nuclide mean std. dev. -0 12 1 total 0.0 0.0 \ No newline at end of file +0 10000 1 total 0.453624 0.021053 + material group in nuclide mean std. dev. +0 10000 1 total 0.400852 0.022858 + material group in nuclide mean std. dev. +0 10000 1 total 0.400852 0.022858 + material group in nuclide mean std. dev. +0 10000 1 total 0.064903 0.004313 + material group in nuclide mean std. dev. +0 10000 1 total 0.028048 0.00458 + material group in nuclide mean std. dev. +0 10000 1 total 0.036855 0.002622 + material group in nuclide mean std. dev. +0 10000 1 total 0.090649 0.00641 + material group in nuclide mean std. dev. +0 10000 1 total 7.137955 0.507364 + material group in nuclide mean std. dev. +0 10000 1 total 0.388721 0.01783 + material group in nuclide mean std. dev. +0 10000 1 total 0.389304 0.023076 + material group in group out nuclide moment mean std. dev. +0 10000 1 1 total P0 0.389304 0.023146 +1 10000 1 1 total P1 0.046224 0.005907 +2 10000 1 1 total P2 0.017984 0.002883 +3 10000 1 1 total P3 0.006628 0.002457 + material group in group out nuclide moment mean std. dev. +0 10000 1 1 total P0 0.389304 0.023146 +1 10000 1 1 total P1 0.046224 0.005907 +2 10000 1 1 total P2 0.017984 0.002883 +3 10000 1 1 total P3 0.006628 0.002457 + material group in group out nuclide mean std. dev. +0 10000 1 1 total 1.0 0.066111 + material group in group out nuclide mean std. dev. +0 10000 1 1 total 0.085835 0.005592 + material group out nuclide mean std. dev. +0 10000 1 total 1.0 0.046071 + material group in nuclide mean std. dev. +0 10001 1 total 0.311594 0.013793 + material group in nuclide mean std. dev. +0 10001 1 total 0.279255 0.02919 + material group in nuclide mean std. dev. +0 10001 1 total 0.279255 0.02919 + material group in nuclide mean std. dev. +0 10001 1 total 0.00221 0.000286 + material group in nuclide mean std. dev. +0 10001 1 total 0.00221 0.000286 + material group in nuclide mean std. dev. +0 10001 1 total 0.0 0.0 + material group in nuclide mean std. dev. +0 10001 1 total 0.0 0.0 + material group in nuclide mean std. dev. +0 10001 1 total 0.0 0.0 + material group in nuclide mean std. dev. +0 10001 1 total 0.309384 0.013551 + material group in nuclide mean std. dev. +0 10001 1 total 0.307987 0.029308 + material group in group out nuclide moment mean std. dev. +0 10001 1 1 total P0 0.307987 0.029308 +1 10001 1 1 total P1 0.030617 0.007464 +2 10001 1 1 total P2 0.018911 0.004323 +3 10001 1 1 total P3 0.006235 0.003338 + material group in group out nuclide moment mean std. dev. +0 10001 1 1 total P0 0.307987 0.029308 +1 10001 1 1 total P1 0.030617 0.007464 +2 10001 1 1 total P2 0.018911 0.004323 +3 10001 1 1 total P3 0.006235 0.003338 + material group in group out nuclide mean std. dev. +0 10001 1 1 total 1.0 0.095039 + material group in group out nuclide mean std. dev. +0 10001 1 1 total 0.0 0.0 + material group out nuclide mean std. dev. +0 10001 1 total 0.0 0.0 + material group in nuclide mean std. dev. +0 10002 1 total 0.904999 0.043964 + material group in nuclide mean std. dev. +0 10002 1 total 0.499184 0.040914 + material group in nuclide mean std. dev. +0 10002 1 total 0.499184 0.040914 + material group in nuclide mean std. dev. +0 10002 1 total 0.00606 0.000555 + material group in nuclide mean std. dev. +0 10002 1 total 0.00606 0.000555 + material group in nuclide mean std. dev. +0 10002 1 total 0.0 0.0 + material group in nuclide mean std. dev. +0 10002 1 total 0.0 0.0 + material group in nuclide mean std. dev. +0 10002 1 total 0.0 0.0 + material group in nuclide mean std. dev. +0 10002 1 total 0.898938 0.043493 + material group in nuclide mean std. dev. +0 10002 1 total 0.903415 0.043959 + material group in group out nuclide moment mean std. dev. +0 10002 1 1 total P0 0.903415 0.043586 +1 10002 1 1 total P1 0.410417 0.015877 +2 10002 1 1 total P2 0.143301 0.007187 +3 10002 1 1 total P3 0.008739 0.003571 + material group in group out nuclide moment mean std. dev. +0 10002 1 1 total P0 0.903415 0.043586 +1 10002 1 1 total P1 0.410417 0.015877 +2 10002 1 1 total P2 0.143301 0.007187 +3 10002 1 1 total P3 0.008739 0.003571 + material group in group out nuclide mean std. dev. +0 10002 1 1 total 1.0 0.056867 + material group in group out nuclide mean std. dev. +0 10002 1 1 total 0.0 0.0 + material group out nuclide mean std. dev. +0 10002 1 total 0.0 0.0 diff --git a/tests/test_mgxs_library_condense/test_mgxs_library_condense.py b/tests/test_mgxs_library_condense/test_mgxs_library_condense.py index 561232b224..5571b59f2e 100644 --- a/tests/test_mgxs_library_condense/test_mgxs_library_condense.py +++ b/tests/test_mgxs_library_condense/test_mgxs_library_condense.py @@ -6,27 +6,28 @@ import glob import hashlib sys.path.insert(0, os.pardir) from testing_harness import PyAPITestHarness +from input_set import PinCellInputSet import openmc import openmc.mgxs class MGXSTestHarness(PyAPITestHarness): def _build_inputs(self): - - # The openmc.mgxs module needs a summary.h5 file - self._input_set.settings.output = {'summary': True} + # Set the input set to use the pincell model + self._input_set = PinCellInputSet() # Generate inputs using parent class routine super(MGXSTestHarness, self)._build_inputs() # Initialize a two-group structure - energy_groups = openmc.mgxs.EnergyGroups(group_edges=[0, 0.625e-6, 20.]) + energy_groups = openmc.mgxs.EnergyGroups(group_edges=[0, 0.625e-6, + 20.]) # Initialize MGXS Library for a few cross section types self.mgxs_lib = openmc.mgxs.Library(self._input_set.geometry) self.mgxs_lib.by_nuclide = False - self.mgxs_lib.mgxs_types = ['transport', 'nu-fission', - 'nu-scatter matrix', 'chi'] + # Test all MGXS types + self.mgxs_lib.mgxs_types = openmc.mgxs.MGXS_TYPES self.mgxs_lib.energy_groups = energy_groups self.mgxs_lib.legendre_order = 3 self.mgxs_lib.domain_type = 'material' @@ -57,7 +58,7 @@ class MGXSTestHarness(PyAPITestHarness): for mgxs_type in condense_lib.mgxs_types: mgxs = condense_lib.get_mgxs(domain, mgxs_type) df = mgxs.get_pandas_dataframe() - outstr += df.to_string() + outstr += df.to_string() + '\n' # Hash the results if necessary if hash_output: diff --git a/tests/test_mgxs_library_distribcell/inputs_true.dat b/tests/test_mgxs_library_distribcell/inputs_true.dat index 21927c8008..dc67b7c562 100644 --- a/tests/test_mgxs_library_distribcell/inputs_true.dat +++ b/tests/test_mgxs_library_distribcell/inputs_true.dat @@ -1 +1 @@ -018bbbc2099f7b94180b391e46e42fc9a82498c60b3f8f7f4c91480ea373427932d287fe571d53b2397f329e71485e7155d7644f0f995bbcb458ba3e872ab043 \ No newline at end of file +88849ac150f9c389e67de96356dfceb0bde08643f68ca25699e67d263995b95893d7340a2b08b2f0f5075fc5020f73553c5287ec6c56ace2f35ce0214961e123 \ No newline at end of file diff --git a/tests/test_mgxs_library_distribcell/results_true.dat b/tests/test_mgxs_library_distribcell/results_true.dat index fa55249d13..5000d60c3b 100644 --- a/tests/test_mgxs_library_distribcell/results_true.dat +++ b/tests/test_mgxs_library_distribcell/results_true.dat @@ -1,8 +1,36 @@ avg(distribcell) group in nuclide mean std. dev. -0 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,... 1 total 0.718919 0.520644 avg(distribcell) group in nuclide mean std. dev. -0 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,... 1 total 0.0 0.0 avg(distribcell) group in group out nuclide moment mean std. dev. +0 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,... 1 total 1.145934 0.553822 + avg(distribcell) group in nuclide mean std. dev. +0 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,... 1 total 0.718919 0.520644 + avg(distribcell) group in nuclide mean std. dev. +0 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,... 1 total 0.718919 0.520644 + avg(distribcell) group in nuclide mean std. dev. +0 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,... 1 total 0.019762 0.010629 + avg(distribcell) group in nuclide mean std. dev. +0 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,... 1 total 0.019762 0.010629 + avg(distribcell) group in nuclide mean std. dev. +0 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,... 1 total 0.0 0.0 + avg(distribcell) group in nuclide mean std. dev. +0 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,... 1 total 0.0 0.0 + avg(distribcell) group in nuclide mean std. dev. +0 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,... 1 total 0.0 0.0 + avg(distribcell) group in nuclide mean std. dev. +0 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,... 1 total 1.126172 0.54344 + avg(distribcell) group in nuclide mean std. dev. +0 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,... 1 total 1.142547 0.570131 + avg(distribcell) group in group out nuclide moment mean std. dev. 0 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,... 1 1 total P0 1.142547 0.570131 1 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,... 1 1 total P1 0.447381 0.216322 2 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,... 1 1 total P2 0.141202 0.066504 -3 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,... 1 1 total P3 0.039228 0.024621 avg(distribcell) group out nuclide mean std. dev. -0 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,... 1 total 0.0 0.0 \ No newline at end of file +3 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,... 1 1 total P3 0.039228 0.024621 + avg(distribcell) group in group out nuclide moment mean std. dev. +0 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,... 1 1 total P0 1.142547 0.570131 +1 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,... 1 1 total P1 0.447381 0.216322 +2 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,... 1 1 total P2 0.141202 0.066504 +3 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,... 1 1 total P3 0.039228 0.024621 + avg(distribcell) group in group out nuclide mean std. dev. +0 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,... 1 1 total 1.0 0.529717 + avg(distribcell) group in group out nuclide mean std. dev. +0 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,... 1 1 total 0.0 0.0 + avg(distribcell) group out nuclide mean std. dev. +0 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,... 1 total 0.0 0.0 diff --git a/tests/test_mgxs_library_distribcell/test_mgxs_library_distribcell.py b/tests/test_mgxs_library_distribcell/test_mgxs_library_distribcell.py index 32f5ea1bd8..30593e54b5 100644 --- a/tests/test_mgxs_library_distribcell/test_mgxs_library_distribcell.py +++ b/tests/test_mgxs_library_distribcell/test_mgxs_library_distribcell.py @@ -12,10 +12,6 @@ import openmc.mgxs class MGXSTestHarness(PyAPITestHarness): def _build_inputs(self): - - # The openmc.mgxs module needs a summary.h5 file - self._input_set.settings.output = {'summary': True} - # Generate inputs using parent class routine super(MGXSTestHarness, self)._build_inputs() @@ -26,8 +22,8 @@ class MGXSTestHarness(PyAPITestHarness): # for one material-filled cell in the geometry self.mgxs_lib = openmc.mgxs.Library(self._input_set.geometry) self.mgxs_lib.by_nuclide = False - self.mgxs_lib.mgxs_types = ['transport', 'nu-fission', - 'nu-scatter matrix', 'chi'] + # Test all MGXS types + self.mgxs_lib.mgxs_types = openmc.mgxs.MGXS_TYPES self.mgxs_lib.energy_groups = energy_groups self.mgxs_lib.legendre_order = 3 self.mgxs_lib.domain_type = 'distribcell' @@ -59,7 +55,7 @@ class MGXSTestHarness(PyAPITestHarness): for mgxs_type in avg_lib.mgxs_types: mgxs = avg_lib.get_mgxs(domain, mgxs_type) df = mgxs.get_pandas_dataframe() - outstr += df.to_string() + outstr += df.to_string() + '\n' # Hash the results if necessary if hash_output: diff --git a/tests/test_mgxs_library_hdf5/inputs_true.dat b/tests/test_mgxs_library_hdf5/inputs_true.dat index 3643c9a2ef..79ca0ec660 100644 --- a/tests/test_mgxs_library_hdf5/inputs_true.dat +++ b/tests/test_mgxs_library_hdf5/inputs_true.dat @@ -1 +1 @@ -104e7fb527770ac5d3fc636da7716e8fb05d55761253d30516c899f466e6b38ffd881611a3d0cdf65c6af058c32f6f6758c68782be7a170d21024bdae751862f \ No newline at end of file +317a63a9dd3bfd84e969667b00f46018e56c04c356461a75103f63569e6b70c84d0da7f5e611faaf1b2631330b05ab4346223d3d843018ce0ce8876671a450c0 \ No newline at end of file diff --git a/tests/test_mgxs_library_hdf5/results_true.dat b/tests/test_mgxs_library_hdf5/results_true.dat index 3cae577471..7391b2e427 100644 --- a/tests/test_mgxs_library_hdf5/results_true.dat +++ b/tests/test_mgxs_library_hdf5/results_true.dat @@ -1,240 +1,195 @@ -domain=1 type=transport -[ 0.37274472 0.86160691] -[ 0.02426918 0.03234902] -domain=1 type=nu-fission -[ 0.02178897 0.71407658] -[ 0.00118187 0.04055185] -domain=1 type=nu-scatter matrix -[[[ 3.81546297e-01 4.43012537e-02 2.06462886e-02 1.36952959e-02] - [ 1.55945353e-03 -5.97269486e-04 -2.38789528e-04 1.75508083e-04]] +domain=10000 type=total +[ 0.41482549 0.66016992] +[ 0.02279291 0.04751893] +domain=10000 type=transport +[ 0.35685964 0.64764766] +[ 0.0254936 0.02370374] +domain=10000 type=nu-transport +[ 0.35685964 0.64764766] +[ 0.0254936 0.02370374] +domain=10000 type=absorption +[ 0.02740784 0.26451074] +[ 0.0026925 0.02336708] +domain=10000 type=capture +[ 0.01984455 0.07171935] +[ 0.0026433 0.02520786] +domain=10000 type=fission +[ 0.00756329 0.19279139] +[ 0.00050848 0.01710592] +domain=10000 type=nu-fission +[ 0.01943174 0.46977478] +[ 0.00132298 0.041682 ] +domain=10000 type=kappa-fission +[ 1.47456982 37.28689641] +[ 0.09923532 3.30837772] +domain=10000 type=scatter +[ 0.38741765 0.39565918] +[ 0.02062573 0.02512506] +domain=10000 type=nu-scatter +[ 0.38518839 0.4123894 ] +[ 0.02694562 0.01542528] +domain=10000 type=scatter matrix +[[[ 3.84199458e-01 5.18702843e-02 2.00688453e-02 9.47771571e-03] + [ 9.88930393e-04 -2.07234596e-04 -1.03366181e-04 2.34290623e-04]] - [[ 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00] - [ 4.03915981e-01 -1.13103276e-02 -1.48065932e-02 -6.85505346e-03]]] -[[[ 0.02403322 0.00472203 0.00253903 0.00222437] - [ 0.00051015 0.00022485 0.00022157 0.00020939]] + [[ 9.24639909e-04 -7.67704968e-04 4.93788872e-04 -1.71497229e-04] + [ 4.11464759e-01 1.64817280e-02 6.37149049e-03 -1.04991221e-02]]] +[[[ 0.02700101 0.00698255 0.0028465 0.00223352] + [ 0.00048242 0.00014901 0.00018432 0.00012817]] - [[ 0. 0. 0. 0. ] - [ 0.01896646 0.00783919 0.00862908 0.00904704]]] -domain=1 type=chi + [[ 0.00092488 0.00076791 0.00049392 0.00017154] + [ 0.01524494 0.00450173 0.01055075 0.01043819]]] +domain=10000 type=nu-scatter matrix +[[[ 3.84199458e-01 5.18702843e-02 2.00688453e-02 9.47771571e-03] + [ 9.88930393e-04 -2.07234596e-04 -1.03366181e-04 2.34290623e-04]] + + [[ 9.24639909e-04 -7.67704968e-04 4.93788872e-04 -1.71497229e-04] + [ 4.11464759e-01 1.64817280e-02 6.37149049e-03 -1.04991221e-02]]] +[[[ 0.02700101 0.00698255 0.0028465 0.00223352] + [ 0.00048242 0.00014901 0.00018432 0.00012817]] + + [[ 0.00092488 0.00076791 0.00049392 0.00017154] + [ 0.01524494 0.00450173 0.01055075 0.01043819]]] +domain=10000 type=multiplicity matrix +[[ 1. 1.] + [ 1. 1.]] +[[ 0.07851646 0.68718427] + [ 1.41421356 0.04113035]] +domain=10000 type=nu-fission matrix +[[ 0.02014243 0. ] + [ 0.45436647 0. ]] +[[ 0.00314909 0. ] + [ 0.02742551 0. ]] +domain=10000 type=chi [ 1. 0.] -[ 0.05533329 0. ] -domain=2 type=transport -[ 0.23725441 0.28593027] -[ 0.00818357 0.04879593] -domain=2 type=nu-fission +[ 0.04607052 0. ] +domain=10001 type=total +[ 0.31373767 0.3008214 ] +[ 0.0155819 0.02805245] +domain=10001 type=transport +[ 0.27322787 0.31237484] +[ 0.03311537 0.04960583] +domain=10001 type=nu-transport +[ 0.27322787 0.31237484] +[ 0.03311537 0.04960583] +domain=10001 type=absorption +[ 0.00157499 0.00540038] +[ 0.00032255 0.00061814] +domain=10001 type=capture +[ 0.00157499 0.00540038] +[ 0.00032255 0.00061814] +domain=10001 type=fission [ 0. 0.] [ 0. 0.] -domain=2 type=nu-scatter matrix -[[[ 0.27311543 0.03586102 0.02970389 0.00224892] +domain=10001 type=nu-fission +[ 0. 0.] +[ 0. 0.] +domain=10001 type=kappa-fission +[ 0. 0.] +[ 0. 0.] +domain=10001 type=scatter +[ 0.31216268 0.29542102] +[ 0.01532192 0.02744549] +domain=10001 type=nu-scatter +[ 0.31012074 0.29626427] +[ 0.03378811 0.04379223] +domain=10001 type=scatter matrix +[[[ 0.31012074 0.03822959 0.02074494 0.0079643 ] [ 0. 0. 0. 0. ]] [[ 0. 0. 0. 0. ] - [ 0.26405068 -0.02187959 -0.01529469 0.01403395]]] -[[[ 0.00625287 0.00587756 0.00664018 0.00337568] + [ 0.29626427 -0.01121364 0.00883657 -0.00327007]]] +[[[ 0.03378811 0.008484 0.00469561 0.00373162] [ 0. 0. 0. 0. ]] [[ 0. 0. 0. 0. ] - [ 0.04539742 0.01221814 0.01027609 0.01431818]]] -domain=2 type=chi -[ 0. 0.] -[ 0. 0.] -domain=3 type=transport -[ 0.28690578 1.41815062] -[ 0.02740142 0.26530756] -domain=3 type=nu-fission -[ 0. 0.] -[ 0. 0.] -domain=3 type=nu-scatter matrix -[[[ 0.64334557 0.38340871 0.15218526 0.00303724] - [ 0.02618721 0.00736219 -0.00273849 -0.00271989]] - - [[ 0. 0. 0. 0. ] - [ 1.92421362 0.4984312 0.09120485 0.01705441]]] -[[[ 0.02837604 0.01644677 0.00957372 0.00464802] - [ 0.00166461 0.00093414 0.00075617 0.00055807]] - - [[ 0. 0. 0. 0. ] - [ 0.28406198 0.06342067 0.01372628 0.01391602]]] -domain=3 type=chi -[ 0. 0.] -[ 0. 0.] -domain=4 type=transport -[ 0.24244686 1.25395921] -[ 0.06103082 0.38836257] -domain=4 type=nu-fission -[ 0. 0.] -[ 0. 0.] -domain=4 type=nu-scatter matrix -[[[ 0.54394096 0.32601136 0.13113269 0.01210477] - [ 0.023662 0.00752551 -0.00272975 -0.0031405 ]] - - [[ 0. 0. 0. 0. ] - [ 1.76464845 0.50069481 0.09902596 0.03297543]]] -[[[ 0.06542705 0.03860196 0.0174751 0.00607268] - [ 0.00308328 0.00130111 0.00084112 0.00057761]] - - [[ 0. 0. 0. 0. ] - [ 0.41620952 0.12217802 0.03871874 0.02510259]]] -domain=4 type=chi -[ 0. 0.] -[ 0. 0.] -domain=5 type=transport -[ 0. 0.] -[ 0. 0.] -domain=5 type=nu-fission -[ 0. 0.] -[ 0. 0.] -domain=5 type=nu-scatter matrix -[[[ 0. 0. 0. 0.] - [ 0. 0. 0. 0.]] - - [[ 0. 0. 0. 0.] - [ 0. 0. 0. 0.]]] -[[[ 0. 0. 0. 0.] - [ 0. 0. 0. 0.]] - - [[ 0. 0. 0. 0.] - [ 0. 0. 0. 0.]]] -domain=5 type=chi -[ 0. 0.] -[ 0. 0.] -domain=6 type=transport -[ 0. 0.] -[ 0. 0.] -domain=6 type=nu-fission -[ 0. 0.] -[ 0. 0.] -domain=6 type=nu-scatter matrix -[[[ 0. 0. 0. 0.] - [ 0. 0. 0. 0.]] - - [[ 0. 0. 0. 0.] - [ 0. 0. 0. 0.]]] -[[[ 0. 0. 0. 0.] - [ 0. 0. 0. 0.]] - - [[ 0. 0. 0. 0.] - [ 0. 0. 0. 0.]]] -domain=6 type=chi -[ 0. 0.] -[ 0. 0.] -domain=7 type=transport -[ 0. 0.] -[ 0. 0.] -domain=7 type=nu-fission -[ 0. 0.] -[ 0. 0.] -domain=7 type=nu-scatter matrix -[[[ 0. 0. 0. 0.] - [ 0. 0. 0. 0.]] - - [[ 0. 0. 0. 0.] - [ 0. 0. 0. 0.]]] -[[[ 0. 0. 0. 0.] - [ 0. 0. 0. 0.]] - - [[ 0. 0. 0. 0.] - [ 0. 0. 0. 0.]]] -domain=7 type=chi -[ 0. 0.] -[ 0. 0.] -domain=8 type=transport -[ 0. 0.] -[ 0. 0.] -domain=8 type=nu-fission -[ 0. 0.] -[ 0. 0.] -domain=8 type=nu-scatter matrix -[[[ 0. 0. 0. 0.] - [ 0. 0. 0. 0.]] - - [[ 0. 0. 0. 0.] - [ 0. 0. 0. 0.]]] -[[[ 0. 0. 0. 0.] - [ 0. 0. 0. 0.]] - - [[ 0. 0. 0. 0.] - [ 0. 0. 0. 0.]]] -domain=8 type=chi -[ 0. 0.] -[ 0. 0.] -domain=9 type=transport -[ 0.60053598 0. ] -[ 0.74887543 0. ] -domain=9 type=nu-fission -[ 0. 0.] -[ 0. 0.] -domain=9 type=nu-scatter matrix -[[[ 0.72037987 0.11984389 0.03852204 0.05602285] + [ 0.04379223 0.01618037 0.01150396 0.00732885]]] +domain=10001 type=nu-scatter matrix +[[[ 0.31012074 0.03822959 0.02074494 0.0079643 ] [ 0. 0. 0. 0. ]] [[ 0. 0. 0. 0. ] - [ 0. 0. 0. 0. ]]] -[[[ 0.77101455 0.18469083 0.06448453 0.05059534] + [ 0.29626427 -0.01121364 0.00883657 -0.00327007]]] +[[[ 0.03378811 0.008484 0.00469561 0.00373162] [ 0. 0. 0. 0. ]] [[ 0. 0. 0. 0. ] - [ 0. 0. 0. 0. ]]] -domain=9 type=chi + [ 0.04379223 0.01618037 0.01150396 0.00732885]]] +domain=10001 type=multiplicity matrix +[[ 1. 0.] + [ 0. 1.]] +[[ 0.1087787 0. ] + [ 0. 0.14242717]] +domain=10001 type=nu-fission matrix +[[ 0. 0.] + [ 0. 0.]] +[[ 0. 0.] + [ 0. 0.]] +domain=10001 type=chi [ 0. 0.] [ 0. 0.] -domain=10 type=transport -[ 0.23551495 0. ] -[ 0.61397415 0. ] -domain=10 type=nu-fission +domain=10002 type=total +[ 0.66457226 2.05238401] +[ 0.03121475 0.22434291] +domain=10002 type=transport +[ 0.29056526 1.51643801] +[ 0.02385185 0.23519727] +domain=10002 type=nu-transport +[ 0.29056526 1.51643801] +[ 0.02385185 0.23519727] +domain=10002 type=absorption +[ 0.0006904 0.03168726] +[ 4.41475687e-05 3.74655858e-03] +domain=10002 type=capture +[ 0.0006904 0.03168726] +[ 4.41475687e-05 3.74655858e-03] +domain=10002 type=fission [ 0. 0.] [ 0. 0.] -domain=10 type=nu-scatter matrix -[[[ 0.50100891 0.26549396 0.14197875 0.07425836] - [ 0. 0. 0. 0. ]] +domain=10002 type=nu-fission +[ 0. 0.] +[ 0. 0.] +domain=10002 type=kappa-fission +[ 0. 0.] +[ 0. 0.] +domain=10002 type=scatter +[ 0.66388186 2.02069676] +[ 0.03117268 0.22060445] +domain=10002 type=nu-scatter +[ 0.6712692 2.03538833] +[ 0.02618637 0.25806033] +domain=10002 type=scatter matrix +[[[ 6.39901485e-01 3.81167449e-01 1.52391898e-01 9.14802229e-03] + [ 3.13677198e-02 8.75772321e-03 -2.56790106e-03 -3.78480288e-03]] - [[ 0. 0. 0. 0. ] - [ 0. 0. 0. 0. ]]] -[[[ 0.70853359 0.37546516 0.20078827 0.10501718] - [ 0. 0. 0. 0. ]] + [[ 4.43343134e-04 3.99960414e-04 3.19562707e-04 2.13846969e-04] + [ 2.03494499e+00 5.09940513e-01 1.11174609e-01 2.49884357e-02]]] +[[[ 2.47091228e-02 1.62432649e-02 8.15627770e-03 3.88856214e-03] + [ 1.72811290e-03 9.25670501e-04 1.01398475e-03 8.17075571e-04]] - [[ 0. 0. 0. 0. ] - [ 0. 0. 0. 0. ]]] -domain=10 type=chi -[ 0. 0.] -[ 0. 0.] -domain=11 type=transport -[ 0.18632392 0.94598628] -[ 0.63212919 1.59113341] -domain=11 type=nu-fission -[ 0. 0.] -[ 0. 0.] -domain=11 type=nu-scatter matrix -[[[ 0.47812753 0.32367878 0.14337507 0.05400336] - [ 0.03187517 0.00858456 -0.01246962 -0.01132019]] + [[ 4.44850393e-04 4.01320183e-04 3.20649143e-04 2.14573997e-04] + [ 2.57799889e-01 5.12359063e-02 1.30198170e-02 8.31235256e-03]]] +domain=10002 type=nu-scatter matrix +[[[ 6.39901485e-01 3.81167449e-01 1.52391898e-01 9.14802229e-03] + [ 3.13677198e-02 8.75772321e-03 -2.56790106e-03 -3.78480288e-03]] - [[ 0. 0. 0. 0. ] - [ 1.20124973 0.28661101 0.21819147 -0.04851424]]] -[[[ 0.67617444 0.45775092 0.20276296 0.07637229] - [ 0.0450783 0.0121404 0.01763471 0.01600917]] + [[ 4.43343134e-04 3.99960414e-04 3.19562707e-04 2.13846969e-04] + [ 2.03494499e+00 5.09940513e-01 1.11174609e-01 2.49884357e-02]]] +[[[ 2.47091228e-02 1.62432649e-02 8.15627770e-03 3.88856214e-03] + [ 1.72811290e-03 9.25670501e-04 1.01398475e-03 8.17075571e-04]] - [[ 0. 0. 0. 0. ] - [ 1.69882367 0.40532917 0.30856933 0.0686095 ]]] -domain=11 type=chi -[ 0. 0.] -[ 0. 0.] -domain=12 type=transport -[ 0.21329208 1.3909745 ] -[ 0.27144387 2.13734565] -domain=12 type=nu-fission -[ 0. 0.] -[ 0. 0.] -domain=12 type=nu-scatter matrix -[[[ 0.40859392 0.22254143 0.0909719 0.03100368] - [ 0.02723959 -0.01008785 -0.00694631 0.00969231]] - - [[ 0. 0. 0. 0. ] - [ 1.57432766 0.22974802 0.01417839 0.03899727]]] -[[[ 0.27812309 0.14577636 0.06962553 0.03598053] - [ 0.02955488 0.01094529 0.00753673 0.01051613]] - - [[ 0. 0. 0. 0. ] - [ 2.22643553 0.32491277 0.02005128 0.05515046]]] -domain=12 type=chi + [[ 4.44850393e-04 4.01320183e-04 3.20649143e-04 2.14573997e-04] + [ 2.57799889e-01 5.12359063e-02 1.30198170e-02 8.31235256e-03]]] +domain=10002 type=multiplicity matrix +[[ 1. 1.] + [ 1. 1.]] +[[ 0.03860919 0.06766735] + [ 1.41421356 0.13592921]] +domain=10002 type=nu-fission matrix +[[ 0. 0.] + [ 0. 0.]] +[[ 0. 0.] + [ 0. 0.]] +domain=10002 type=chi [ 0. 0.] [ 0. 0.] diff --git a/tests/test_mgxs_library_hdf5/test_mgxs_library_hdf5.py b/tests/test_mgxs_library_hdf5/test_mgxs_library_hdf5.py index 2d7ed2ef3d..000a1f8cb9 100644 --- a/tests/test_mgxs_library_hdf5/test_mgxs_library_hdf5.py +++ b/tests/test_mgxs_library_hdf5/test_mgxs_library_hdf5.py @@ -7,27 +7,28 @@ import hashlib import h5py sys.path.insert(0, os.pardir) from testing_harness import PyAPITestHarness +from input_set import PinCellInputSet import openmc import openmc.mgxs class MGXSTestHarness(PyAPITestHarness): def _build_inputs(self): - - # The openmc.mgxs module needs a summary.h5 file - self._input_set.settings.output = {'summary': True} + # Set the input set to use the pincell model + self._input_set = PinCellInputSet() # Generate inputs using parent class routine super(MGXSTestHarness, self)._build_inputs() # Initialize a two-group structure - energy_groups = openmc.mgxs.EnergyGroups(group_edges=[0, 0.625e-6, 20.]) + energy_groups = openmc.mgxs.EnergyGroups(group_edges=[0, 0.625e-6, + 20.]) # Initialize MGXS Library for a few cross section types self.mgxs_lib = openmc.mgxs.Library(self._input_set.geometry) self.mgxs_lib.by_nuclide = False - self.mgxs_lib.mgxs_types = ['transport', 'nu-fission', - 'nu-scatter matrix', 'chi'] + # Test all MGXS types + self.mgxs_lib.mgxs_types = openmc.mgxs.MGXS_TYPES self.mgxs_lib.energy_groups = energy_groups self.mgxs_lib.legendre_order = 3 self.mgxs_lib.domain_type = 'material' @@ -75,7 +76,6 @@ class MGXSTestHarness(PyAPITestHarness): return outstr - def _cleanup(self): super(MGXSTestHarness, self)._cleanup() f = os.path.join(os.getcwd(), 'tallies.xml') diff --git a/tests/test_mgxs_library_no_nuclides/inputs_true.dat b/tests/test_mgxs_library_no_nuclides/inputs_true.dat index 3643c9a2ef..79ca0ec660 100644 --- a/tests/test_mgxs_library_no_nuclides/inputs_true.dat +++ b/tests/test_mgxs_library_no_nuclides/inputs_true.dat @@ -1 +1 @@ -104e7fb527770ac5d3fc636da7716e8fb05d55761253d30516c899f466e6b38ffd881611a3d0cdf65c6af058c32f6f6758c68782be7a170d21024bdae751862f \ No newline at end of file +317a63a9dd3bfd84e969667b00f46018e56c04c356461a75103f63569e6b70c84d0da7f5e611faaf1b2631330b05ab4346223d3d843018ce0ce8876671a450c0 \ No newline at end of file diff --git a/tests/test_mgxs_library_no_nuclides/results_true.dat b/tests/test_mgxs_library_no_nuclides/results_true.dat index 94150a202a..599cee6c49 100644 --- a/tests/test_mgxs_library_no_nuclides/results_true.dat +++ b/tests/test_mgxs_library_no_nuclides/results_true.dat @@ -1,265 +1,231 @@ material group in nuclide mean std. dev. -1 1 1 total 0.372745 0.024269 -0 1 2 total 0.861607 0.032349 material group in nuclide mean std. dev. -1 1 1 total 0.021789 0.001182 -0 1 2 total 0.714077 0.040552 material group in group out nuclide moment mean std. dev. -12 1 1 1 total P0 0.381546 0.024033 -13 1 1 1 total P1 0.044301 0.004722 -14 1 1 1 total P2 0.020646 0.002539 -15 1 1 1 total P3 0.013695 0.002224 -8 1 1 2 total P0 0.001559 0.000510 -9 1 1 2 total P1 -0.000597 0.000225 -10 1 1 2 total P2 -0.000239 0.000222 -11 1 1 2 total P3 0.000176 0.000209 -4 1 2 1 total P0 0.000000 0.000000 -5 1 2 1 total P1 0.000000 0.000000 -6 1 2 1 total P2 0.000000 0.000000 -7 1 2 1 total P3 0.000000 0.000000 -0 1 2 2 total P0 0.403916 0.018966 -1 1 2 2 total P1 -0.011310 0.007839 -2 1 2 2 total P2 -0.014807 0.008629 -3 1 2 2 total P3 -0.006855 0.009047 material group out nuclide mean std. dev. -1 1 1 total 1.0 0.055333 -0 1 2 total 0.0 0.000000 material group in nuclide mean std. dev. -1 2 1 total 0.237254 0.008184 -0 2 2 total 0.285930 0.048796 material group in nuclide mean std. dev. -1 2 1 total 0.0 0.0 -0 2 2 total 0.0 0.0 material group in group out nuclide moment mean std. dev. -12 2 1 1 total P0 0.273115 0.006253 -13 2 1 1 total P1 0.035861 0.005878 -14 2 1 1 total P2 0.029704 0.006640 -15 2 1 1 total P3 0.002249 0.003376 -8 2 1 2 total P0 0.000000 0.000000 -9 2 1 2 total P1 0.000000 0.000000 -10 2 1 2 total P2 0.000000 0.000000 -11 2 1 2 total P3 0.000000 0.000000 -4 2 2 1 total P0 0.000000 0.000000 -5 2 2 1 total P1 0.000000 0.000000 -6 2 2 1 total P2 0.000000 0.000000 -7 2 2 1 total P3 0.000000 0.000000 -0 2 2 2 total P0 0.264051 0.045397 -1 2 2 2 total P1 -0.021880 0.012218 -2 2 2 2 total P2 -0.015295 0.010276 -3 2 2 2 total P3 0.014034 0.014318 material group out nuclide mean std. dev. -1 2 1 total 0.0 0.0 -0 2 2 total 0.0 0.0 material group in nuclide mean std. dev. -1 3 1 total 0.286906 0.027401 -0 3 2 total 1.418151 0.265308 material group in nuclide mean std. dev. -1 3 1 total 0.0 0.0 -0 3 2 total 0.0 0.0 material group in group out nuclide moment mean std. dev. -12 3 1 1 total P0 0.643346 0.028376 -13 3 1 1 total P1 0.383409 0.016447 -14 3 1 1 total P2 0.152185 0.009574 -15 3 1 1 total P3 0.003037 0.004648 -8 3 1 2 total P0 0.026187 0.001665 -9 3 1 2 total P1 0.007362 0.000934 -10 3 1 2 total P2 -0.002738 0.000756 -11 3 1 2 total P3 -0.002720 0.000558 -4 3 2 1 total P0 0.000000 0.000000 -5 3 2 1 total P1 0.000000 0.000000 -6 3 2 1 total P2 0.000000 0.000000 -7 3 2 1 total P3 0.000000 0.000000 -0 3 2 2 total P0 1.924214 0.284062 -1 3 2 2 total P1 0.498431 0.063421 -2 3 2 2 total P2 0.091205 0.013726 -3 3 2 2 total P3 0.017054 0.013916 material group out nuclide mean std. dev. -1 3 1 total 0.0 0.0 -0 3 2 total 0.0 0.0 material group in nuclide mean std. dev. -1 4 1 total 0.242447 0.061031 -0 4 2 total 1.253959 0.388363 material group in nuclide mean std. dev. -1 4 1 total 0.0 0.0 -0 4 2 total 0.0 0.0 material group in group out nuclide moment mean std. dev. -12 4 1 1 total P0 0.543941 0.065427 -13 4 1 1 total P1 0.326011 0.038602 -14 4 1 1 total P2 0.131133 0.017475 -15 4 1 1 total P3 0.012105 0.006073 -8 4 1 2 total P0 0.023662 0.003083 -9 4 1 2 total P1 0.007526 0.001301 -10 4 1 2 total P2 -0.002730 0.000841 -11 4 1 2 total P3 -0.003140 0.000578 -4 4 2 1 total P0 0.000000 0.000000 -5 4 2 1 total P1 0.000000 0.000000 -6 4 2 1 total P2 0.000000 0.000000 -7 4 2 1 total P3 0.000000 0.000000 -0 4 2 2 total P0 1.764648 0.416210 -1 4 2 2 total P1 0.500695 0.122178 -2 4 2 2 total P2 0.099026 0.038719 -3 4 2 2 total P3 0.032975 0.025103 material group out nuclide mean std. dev. -1 4 1 total 0.0 0.0 -0 4 2 total 0.0 0.0 material group in nuclide mean std. dev. -1 5 1 total 0.0 0.0 -0 5 2 total 0.0 0.0 material group in nuclide mean std. dev. -1 5 1 total 0.0 0.0 -0 5 2 total 0.0 0.0 material group in group out nuclide moment mean std. dev. -12 5 1 1 total P0 0.0 0.0 -13 5 1 1 total P1 0.0 0.0 -14 5 1 1 total P2 0.0 0.0 -15 5 1 1 total P3 0.0 0.0 -8 5 1 2 total P0 0.0 0.0 -9 5 1 2 total P1 0.0 0.0 -10 5 1 2 total P2 0.0 0.0 -11 5 1 2 total P3 0.0 0.0 -4 5 2 1 total P0 0.0 0.0 -5 5 2 1 total P1 0.0 0.0 -6 5 2 1 total P2 0.0 0.0 -7 5 2 1 total P3 0.0 0.0 -0 5 2 2 total P0 0.0 0.0 -1 5 2 2 total P1 0.0 0.0 -2 5 2 2 total P2 0.0 0.0 -3 5 2 2 total P3 0.0 0.0 material group out nuclide mean std. dev. -1 5 1 total 0.0 0.0 -0 5 2 total 0.0 0.0 material group in nuclide mean std. dev. -1 6 1 total 0.0 0.0 -0 6 2 total 0.0 0.0 material group in nuclide mean std. dev. -1 6 1 total 0.0 0.0 -0 6 2 total 0.0 0.0 material group in group out nuclide moment mean std. dev. -12 6 1 1 total P0 0.0 0.0 -13 6 1 1 total P1 0.0 0.0 -14 6 1 1 total P2 0.0 0.0 -15 6 1 1 total P3 0.0 0.0 -8 6 1 2 total P0 0.0 0.0 -9 6 1 2 total P1 0.0 0.0 -10 6 1 2 total P2 0.0 0.0 -11 6 1 2 total P3 0.0 0.0 -4 6 2 1 total P0 0.0 0.0 -5 6 2 1 total P1 0.0 0.0 -6 6 2 1 total P2 0.0 0.0 -7 6 2 1 total P3 0.0 0.0 -0 6 2 2 total P0 0.0 0.0 -1 6 2 2 total P1 0.0 0.0 -2 6 2 2 total P2 0.0 0.0 -3 6 2 2 total P3 0.0 0.0 material group out nuclide mean std. dev. -1 6 1 total 0.0 0.0 -0 6 2 total 0.0 0.0 material group in nuclide mean std. dev. -1 7 1 total 0.0 0.0 -0 7 2 total 0.0 0.0 material group in nuclide mean std. dev. -1 7 1 total 0.0 0.0 -0 7 2 total 0.0 0.0 material group in group out nuclide moment mean std. dev. -12 7 1 1 total P0 0.0 0.0 -13 7 1 1 total P1 0.0 0.0 -14 7 1 1 total P2 0.0 0.0 -15 7 1 1 total P3 0.0 0.0 -8 7 1 2 total P0 0.0 0.0 -9 7 1 2 total P1 0.0 0.0 -10 7 1 2 total P2 0.0 0.0 -11 7 1 2 total P3 0.0 0.0 -4 7 2 1 total P0 0.0 0.0 -5 7 2 1 total P1 0.0 0.0 -6 7 2 1 total P2 0.0 0.0 -7 7 2 1 total P3 0.0 0.0 -0 7 2 2 total P0 0.0 0.0 -1 7 2 2 total P1 0.0 0.0 -2 7 2 2 total P2 0.0 0.0 -3 7 2 2 total P3 0.0 0.0 material group out nuclide mean std. dev. -1 7 1 total 0.0 0.0 -0 7 2 total 0.0 0.0 material group in nuclide mean std. dev. -1 8 1 total 0.0 0.0 -0 8 2 total 0.0 0.0 material group in nuclide mean std. dev. -1 8 1 total 0.0 0.0 -0 8 2 total 0.0 0.0 material group in group out nuclide moment mean std. dev. -12 8 1 1 total P0 0.0 0.0 -13 8 1 1 total P1 0.0 0.0 -14 8 1 1 total P2 0.0 0.0 -15 8 1 1 total P3 0.0 0.0 -8 8 1 2 total P0 0.0 0.0 -9 8 1 2 total P1 0.0 0.0 -10 8 1 2 total P2 0.0 0.0 -11 8 1 2 total P3 0.0 0.0 -4 8 2 1 total P0 0.0 0.0 -5 8 2 1 total P1 0.0 0.0 -6 8 2 1 total P2 0.0 0.0 -7 8 2 1 total P3 0.0 0.0 -0 8 2 2 total P0 0.0 0.0 -1 8 2 2 total P1 0.0 0.0 -2 8 2 2 total P2 0.0 0.0 -3 8 2 2 total P3 0.0 0.0 material group out nuclide mean std. dev. -1 8 1 total 0.0 0.0 -0 8 2 total 0.0 0.0 material group in nuclide mean std. dev. -1 9 1 total 0.600536 0.748875 -0 9 2 total 0.000000 0.000000 material group in nuclide mean std. dev. -1 9 1 total 0.0 0.0 -0 9 2 total 0.0 0.0 material group in group out nuclide moment mean std. dev. -12 9 1 1 total P0 0.720380 0.771015 -13 9 1 1 total P1 0.119844 0.184691 -14 9 1 1 total P2 0.038522 0.064485 -15 9 1 1 total P3 0.056023 0.050595 -8 9 1 2 total P0 0.000000 0.000000 -9 9 1 2 total P1 0.000000 0.000000 -10 9 1 2 total P2 0.000000 0.000000 -11 9 1 2 total P3 0.000000 0.000000 -4 9 2 1 total P0 0.000000 0.000000 -5 9 2 1 total P1 0.000000 0.000000 -6 9 2 1 total P2 0.000000 0.000000 -7 9 2 1 total P3 0.000000 0.000000 -0 9 2 2 total P0 0.000000 0.000000 -1 9 2 2 total P1 0.000000 0.000000 -2 9 2 2 total P2 0.000000 0.000000 -3 9 2 2 total P3 0.000000 0.000000 material group out nuclide mean std. dev. -1 9 1 total 0.0 0.0 -0 9 2 total 0.0 0.0 material group in nuclide mean std. dev. -1 10 1 total 0.235515 0.613974 -0 10 2 total 0.000000 0.000000 material group in nuclide mean std. dev. -1 10 1 total 0.0 0.0 -0 10 2 total 0.0 0.0 material group in group out nuclide moment mean std. dev. -12 10 1 1 total P0 0.501009 0.708534 -13 10 1 1 total P1 0.265494 0.375465 -14 10 1 1 total P2 0.141979 0.200788 -15 10 1 1 total P3 0.074258 0.105017 -8 10 1 2 total P0 0.000000 0.000000 -9 10 1 2 total P1 0.000000 0.000000 -10 10 1 2 total P2 0.000000 0.000000 -11 10 1 2 total P3 0.000000 0.000000 -4 10 2 1 total P0 0.000000 0.000000 -5 10 2 1 total P1 0.000000 0.000000 -6 10 2 1 total P2 0.000000 0.000000 -7 10 2 1 total P3 0.000000 0.000000 -0 10 2 2 total P0 0.000000 0.000000 -1 10 2 2 total P1 0.000000 0.000000 -2 10 2 2 total P2 0.000000 0.000000 -3 10 2 2 total P3 0.000000 0.000000 material group out nuclide mean std. dev. -1 10 1 total 0.0 0.0 -0 10 2 total 0.0 0.0 material group in nuclide mean std. dev. -1 11 1 total 0.186324 0.632129 -0 11 2 total 0.945986 1.591133 material group in nuclide mean std. dev. -1 11 1 total 0.0 0.0 -0 11 2 total 0.0 0.0 material group in group out nuclide moment mean std. dev. -12 11 1 1 total P0 0.478128 0.676174 -13 11 1 1 total P1 0.323679 0.457751 -14 11 1 1 total P2 0.143375 0.202763 -15 11 1 1 total P3 0.054003 0.076372 -8 11 1 2 total P0 0.031875 0.045078 -9 11 1 2 total P1 0.008585 0.012140 -10 11 1 2 total P2 -0.012470 0.017635 -11 11 1 2 total P3 -0.011320 0.016009 -4 11 2 1 total P0 0.000000 0.000000 -5 11 2 1 total P1 0.000000 0.000000 -6 11 2 1 total P2 0.000000 0.000000 -7 11 2 1 total P3 0.000000 0.000000 -0 11 2 2 total P0 1.201250 1.698824 -1 11 2 2 total P1 0.286611 0.405329 -2 11 2 2 total P2 0.218191 0.308569 -3 11 2 2 total P3 -0.048514 0.068609 material group out nuclide mean std. dev. -1 11 1 total 0.0 0.0 -0 11 2 total 0.0 0.0 material group in nuclide mean std. dev. -1 12 1 total 0.213292 0.271444 -0 12 2 total 1.390975 2.137346 material group in nuclide mean std. dev. -1 12 1 total 0.0 0.0 -0 12 2 total 0.0 0.0 material group in group out nuclide moment mean std. dev. -12 12 1 1 total P0 0.408594 0.278123 -13 12 1 1 total P1 0.222541 0.145776 -14 12 1 1 total P2 0.090972 0.069626 -15 12 1 1 total P3 0.031004 0.035981 -8 12 1 2 total P0 0.027240 0.029555 -9 12 1 2 total P1 -0.010088 0.010945 -10 12 1 2 total P2 -0.006946 0.007537 -11 12 1 2 total P3 0.009692 0.010516 -4 12 2 1 total P0 0.000000 0.000000 -5 12 2 1 total P1 0.000000 0.000000 -6 12 2 1 total P2 0.000000 0.000000 -7 12 2 1 total P3 0.000000 0.000000 -0 12 2 2 total P0 1.574328 2.226436 -1 12 2 2 total P1 0.229748 0.324913 -2 12 2 2 total P2 0.014178 0.020051 -3 12 2 2 total P3 0.038997 0.055150 material group out nuclide mean std. dev. -1 12 1 total 0.0 0.0 -0 12 2 total 0.0 0.0 \ No newline at end of file +1 10000 1 total 0.414825 0.022793 +0 10000 2 total 0.660170 0.047519 + material group in nuclide mean std. dev. +1 10000 1 total 0.356860 0.025494 +0 10000 2 total 0.647648 0.023704 + material group in nuclide mean std. dev. +1 10000 1 total 0.356860 0.025494 +0 10000 2 total 0.647648 0.023704 + material group in nuclide mean std. dev. +1 10000 1 total 0.027408 0.002692 +0 10000 2 total 0.264511 0.023367 + material group in nuclide mean std. dev. +1 10000 1 total 0.019845 0.002643 +0 10000 2 total 0.071719 0.025208 + material group in nuclide mean std. dev. +1 10000 1 total 0.007563 0.000508 +0 10000 2 total 0.192791 0.017106 + material group in nuclide mean std. dev. +1 10000 1 total 0.019432 0.001323 +0 10000 2 total 0.469775 0.041682 + material group in nuclide mean std. dev. +1 10000 1 total 1.474570 0.099235 +0 10000 2 total 37.286896 3.308378 + material group in nuclide mean std. dev. +1 10000 1 total 0.387418 0.020626 +0 10000 2 total 0.395659 0.025125 + material group in nuclide mean std. dev. +1 10000 1 total 0.385188 0.026946 +0 10000 2 total 0.412389 0.015425 + material group in group out nuclide moment mean std. dev. +12 10000 1 1 total P0 0.384199 0.027001 +13 10000 1 1 total P1 0.051870 0.006983 +14 10000 1 1 total P2 0.020069 0.002846 +15 10000 1 1 total P3 0.009478 0.002234 +8 10000 1 2 total P0 0.000989 0.000482 +9 10000 1 2 total P1 -0.000207 0.000149 +10 10000 1 2 total P2 -0.000103 0.000184 +11 10000 1 2 total P3 0.000234 0.000128 +4 10000 2 1 total P0 0.000925 0.000925 +5 10000 2 1 total P1 -0.000768 0.000768 +6 10000 2 1 total P2 0.000494 0.000494 +7 10000 2 1 total P3 -0.000171 0.000172 +0 10000 2 2 total P0 0.411465 0.015245 +1 10000 2 2 total P1 0.016482 0.004502 +2 10000 2 2 total P2 0.006371 0.010551 +3 10000 2 2 total P3 -0.010499 0.010438 + material group in group out nuclide moment mean std. dev. +12 10000 1 1 total P0 0.384199 0.027001 +13 10000 1 1 total P1 0.051870 0.006983 +14 10000 1 1 total P2 0.020069 0.002846 +15 10000 1 1 total P3 0.009478 0.002234 +8 10000 1 2 total P0 0.000989 0.000482 +9 10000 1 2 total P1 -0.000207 0.000149 +10 10000 1 2 total P2 -0.000103 0.000184 +11 10000 1 2 total P3 0.000234 0.000128 +4 10000 2 1 total P0 0.000925 0.000925 +5 10000 2 1 total P1 -0.000768 0.000768 +6 10000 2 1 total P2 0.000494 0.000494 +7 10000 2 1 total P3 -0.000171 0.000172 +0 10000 2 2 total P0 0.411465 0.015245 +1 10000 2 2 total P1 0.016482 0.004502 +2 10000 2 2 total P2 0.006371 0.010551 +3 10000 2 2 total P3 -0.010499 0.010438 + material group in group out nuclide mean std. dev. +3 10000 1 1 total 1.0 0.078516 +2 10000 1 2 total 1.0 0.687184 +1 10000 2 1 total 1.0 1.414214 +0 10000 2 2 total 1.0 0.041130 + material group in group out nuclide mean std. dev. +3 10000 1 1 total 0.020142 0.003149 +2 10000 1 2 total 0.000000 0.000000 +1 10000 2 1 total 0.454366 0.027426 +0 10000 2 2 total 0.000000 0.000000 + material group out nuclide mean std. dev. +1 10000 1 total 1.0 0.046071 +0 10000 2 total 0.0 0.000000 + material group in nuclide mean std. dev. +1 10001 1 total 0.313738 0.015582 +0 10001 2 total 0.300821 0.028052 + material group in nuclide mean std. dev. +1 10001 1 total 0.273228 0.033115 +0 10001 2 total 0.312375 0.049606 + material group in nuclide mean std. dev. +1 10001 1 total 0.273228 0.033115 +0 10001 2 total 0.312375 0.049606 + material group in nuclide mean std. dev. +1 10001 1 total 0.001575 0.000323 +0 10001 2 total 0.005400 0.000618 + material group in nuclide mean std. dev. +1 10001 1 total 0.001575 0.000323 +0 10001 2 total 0.005400 0.000618 + material group in nuclide mean std. dev. +1 10001 1 total 0.0 0.0 +0 10001 2 total 0.0 0.0 + material group in nuclide mean std. dev. +1 10001 1 total 0.0 0.0 +0 10001 2 total 0.0 0.0 + material group in nuclide mean std. dev. +1 10001 1 total 0.0 0.0 +0 10001 2 total 0.0 0.0 + material group in nuclide mean std. dev. +1 10001 1 total 0.312163 0.015322 +0 10001 2 total 0.295421 0.027445 + material group in nuclide mean std. dev. +1 10001 1 total 0.310121 0.033788 +0 10001 2 total 0.296264 0.043792 + material group in group out nuclide moment mean std. dev. +12 10001 1 1 total P0 0.310121 0.033788 +13 10001 1 1 total P1 0.038230 0.008484 +14 10001 1 1 total P2 0.020745 0.004696 +15 10001 1 1 total P3 0.007964 0.003732 +8 10001 1 2 total P0 0.000000 0.000000 +9 10001 1 2 total P1 0.000000 0.000000 +10 10001 1 2 total P2 0.000000 0.000000 +11 10001 1 2 total P3 0.000000 0.000000 +4 10001 2 1 total P0 0.000000 0.000000 +5 10001 2 1 total P1 0.000000 0.000000 +6 10001 2 1 total P2 0.000000 0.000000 +7 10001 2 1 total P3 0.000000 0.000000 +0 10001 2 2 total P0 0.296264 0.043792 +1 10001 2 2 total P1 -0.011214 0.016180 +2 10001 2 2 total P2 0.008837 0.011504 +3 10001 2 2 total P3 -0.003270 0.007329 + material group in group out nuclide moment mean std. dev. +12 10001 1 1 total P0 0.310121 0.033788 +13 10001 1 1 total P1 0.038230 0.008484 +14 10001 1 1 total P2 0.020745 0.004696 +15 10001 1 1 total P3 0.007964 0.003732 +8 10001 1 2 total P0 0.000000 0.000000 +9 10001 1 2 total P1 0.000000 0.000000 +10 10001 1 2 total P2 0.000000 0.000000 +11 10001 1 2 total P3 0.000000 0.000000 +4 10001 2 1 total P0 0.000000 0.000000 +5 10001 2 1 total P1 0.000000 0.000000 +6 10001 2 1 total P2 0.000000 0.000000 +7 10001 2 1 total P3 0.000000 0.000000 +0 10001 2 2 total P0 0.296264 0.043792 +1 10001 2 2 total P1 -0.011214 0.016180 +2 10001 2 2 total P2 0.008837 0.011504 +3 10001 2 2 total P3 -0.003270 0.007329 + material group in group out nuclide mean std. dev. +3 10001 1 1 total 1.0 0.108779 +2 10001 1 2 total 0.0 0.000000 +1 10001 2 1 total 0.0 0.000000 +0 10001 2 2 total 1.0 0.142427 + material group in group out nuclide mean std. dev. +3 10001 1 1 total 0.0 0.0 +2 10001 1 2 total 0.0 0.0 +1 10001 2 1 total 0.0 0.0 +0 10001 2 2 total 0.0 0.0 + material group out nuclide mean std. dev. +1 10001 1 total 0.0 0.0 +0 10001 2 total 0.0 0.0 + material group in nuclide mean std. dev. +1 10002 1 total 0.664572 0.031215 +0 10002 2 total 2.052384 0.224343 + material group in nuclide mean std. dev. +1 10002 1 total 0.290565 0.023852 +0 10002 2 total 1.516438 0.235197 + material group in nuclide mean std. dev. +1 10002 1 total 0.290565 0.023852 +0 10002 2 total 1.516438 0.235197 + material group in nuclide mean std. dev. +1 10002 1 total 0.000690 0.000044 +0 10002 2 total 0.031687 0.003747 + material group in nuclide mean std. dev. +1 10002 1 total 0.000690 0.000044 +0 10002 2 total 0.031687 0.003747 + material group in nuclide mean std. dev. +1 10002 1 total 0.0 0.0 +0 10002 2 total 0.0 0.0 + material group in nuclide mean std. dev. +1 10002 1 total 0.0 0.0 +0 10002 2 total 0.0 0.0 + material group in nuclide mean std. dev. +1 10002 1 total 0.0 0.0 +0 10002 2 total 0.0 0.0 + material group in nuclide mean std. dev. +1 10002 1 total 0.663882 0.031173 +0 10002 2 total 2.020697 0.220604 + material group in nuclide mean std. dev. +1 10002 1 total 0.671269 0.026186 +0 10002 2 total 2.035388 0.258060 + material group in group out nuclide moment mean std. dev. +12 10002 1 1 total P0 0.639901 0.024709 +13 10002 1 1 total P1 0.381167 0.016243 +14 10002 1 1 total P2 0.152392 0.008156 +15 10002 1 1 total P3 0.009148 0.003889 +8 10002 1 2 total P0 0.031368 0.001728 +9 10002 1 2 total P1 0.008758 0.000926 +10 10002 1 2 total P2 -0.002568 0.001014 +11 10002 1 2 total P3 -0.003785 0.000817 +4 10002 2 1 total P0 0.000443 0.000445 +5 10002 2 1 total P1 0.000400 0.000401 +6 10002 2 1 total P2 0.000320 0.000321 +7 10002 2 1 total P3 0.000214 0.000215 +0 10002 2 2 total P0 2.034945 0.257800 +1 10002 2 2 total P1 0.509941 0.051236 +2 10002 2 2 total P2 0.111175 0.013020 +3 10002 2 2 total P3 0.024988 0.008312 + material group in group out nuclide moment mean std. dev. +12 10002 1 1 total P0 0.639901 0.024709 +13 10002 1 1 total P1 0.381167 0.016243 +14 10002 1 1 total P2 0.152392 0.008156 +15 10002 1 1 total P3 0.009148 0.003889 +8 10002 1 2 total P0 0.031368 0.001728 +9 10002 1 2 total P1 0.008758 0.000926 +10 10002 1 2 total P2 -0.002568 0.001014 +11 10002 1 2 total P3 -0.003785 0.000817 +4 10002 2 1 total P0 0.000443 0.000445 +5 10002 2 1 total P1 0.000400 0.000401 +6 10002 2 1 total P2 0.000320 0.000321 +7 10002 2 1 total P3 0.000214 0.000215 +0 10002 2 2 total P0 2.034945 0.257800 +1 10002 2 2 total P1 0.509941 0.051236 +2 10002 2 2 total P2 0.111175 0.013020 +3 10002 2 2 total P3 0.024988 0.008312 + material group in group out nuclide mean std. dev. +3 10002 1 1 total 1.0 0.038609 +2 10002 1 2 total 1.0 0.067667 +1 10002 2 1 total 1.0 1.414214 +0 10002 2 2 total 1.0 0.135929 + material group in group out nuclide mean std. dev. +3 10002 1 1 total 0.0 0.0 +2 10002 1 2 total 0.0 0.0 +1 10002 2 1 total 0.0 0.0 +0 10002 2 2 total 0.0 0.0 + material group out nuclide mean std. dev. +1 10002 1 total 0.0 0.0 +0 10002 2 total 0.0 0.0 diff --git a/tests/test_mgxs_library_no_nuclides/test_mgxs_library_no_nuclides.py b/tests/test_mgxs_library_no_nuclides/test_mgxs_library_no_nuclides.py index 6ee8813d03..2c0a2e278c 100644 --- a/tests/test_mgxs_library_no_nuclides/test_mgxs_library_no_nuclides.py +++ b/tests/test_mgxs_library_no_nuclides/test_mgxs_library_no_nuclides.py @@ -6,27 +6,28 @@ import glob import hashlib sys.path.insert(0, os.pardir) from testing_harness import PyAPITestHarness +from input_set import PinCellInputSet import openmc import openmc.mgxs class MGXSTestHarness(PyAPITestHarness): def _build_inputs(self): - - # The openmc.mgxs module needs a summary.h5 file - self._input_set.settings.output = {'summary': True} + # Set the input set to use the pincell model + self._input_set = PinCellInputSet() # Generate inputs using parent class routine super(MGXSTestHarness, self)._build_inputs() # Initialize a two-group structure - energy_groups = openmc.mgxs.EnergyGroups(group_edges=[0, 0.625e-6, 20.]) + energy_groups = openmc.mgxs.EnergyGroups(group_edges=[0, 0.625e-6, + 20.]) # Initialize MGXS Library for a few cross section types self.mgxs_lib = openmc.mgxs.Library(self._input_set.geometry) self.mgxs_lib.by_nuclide = False - self.mgxs_lib.mgxs_types = ['transport', 'nu-fission', - 'nu-scatter matrix', 'chi'] + # Test all MGXS types + self.mgxs_lib.mgxs_types = openmc.mgxs.MGXS_TYPES self.mgxs_lib.energy_groups = energy_groups self.mgxs_lib.legendre_order = 3 self.mgxs_lib.domain_type = 'material' @@ -53,7 +54,7 @@ class MGXSTestHarness(PyAPITestHarness): for mgxs_type in self.mgxs_lib.mgxs_types: mgxs = self.mgxs_lib.get_mgxs(domain, mgxs_type) df = mgxs.get_pandas_dataframe() - outstr += df.to_string() + outstr += df.to_string() + '\n' # Hash the results if necessary if hash_output: diff --git a/tests/test_mgxs_library_nuclides/inputs_true.dat b/tests/test_mgxs_library_nuclides/inputs_true.dat index 9e25fe96a6..8dbb564c65 100644 --- a/tests/test_mgxs_library_nuclides/inputs_true.dat +++ b/tests/test_mgxs_library_nuclides/inputs_true.dat @@ -1 +1 @@ -791a2bd647b8bae03aafc39e29ff1ce1ffc44063b0d757ccba4e1eda6eb73b8a275020f4f5774b17dede49fbf15549787279c8b2fc45caba0097155b32e56fa8 \ No newline at end of file +eebb1469278f470b5859ed83e9b6526e7c4e3fed503bd22e414c6dc13b19b8e4cb6a44e3c14269e6e173f43056eda78268f455662ae119280bc18ea6a071dac7 \ No newline at end of file diff --git a/tests/test_mgxs_library_nuclides/results_true.dat b/tests/test_mgxs_library_nuclides/results_true.dat index 06f838206f..4f47bd417f 100644 --- a/tests/test_mgxs_library_nuclides/results_true.dat +++ b/tests/test_mgxs_library_nuclides/results_true.dat @@ -1 +1 @@ -1ee58383dc8ac46c5e0d72321cbc34b0dba531435d5e0e632cbbf9572eb7d669c8c8ad9f370345325afa0bdeb2f818b0f5204b7c4a7c4aaf58ded7acbd715ef8 \ No newline at end of file +a631b8a347f344d822e6300ed2576caa7c05a74daedeb4aaaabfb89570942cff1bbd47ad7f81306e668e12266404f7abdcf680fdfeb5a4835579892e32bf57e8 \ No newline at end of file diff --git a/tests/test_mgxs_library_nuclides/test_mgxs_library_nuclides.py b/tests/test_mgxs_library_nuclides/test_mgxs_library_nuclides.py index 47c1ec60ae..da613d78a1 100644 --- a/tests/test_mgxs_library_nuclides/test_mgxs_library_nuclides.py +++ b/tests/test_mgxs_library_nuclides/test_mgxs_library_nuclides.py @@ -6,27 +6,28 @@ import glob import hashlib sys.path.insert(0, os.pardir) from testing_harness import PyAPITestHarness +from input_set import PinCellInputSet import openmc import openmc.mgxs class MGXSTestHarness(PyAPITestHarness): def _build_inputs(self): - - # The openmc.mgxs module needs a summary.h5 file - self._input_set.settings.output = {'summary': True} + # Set the input set to use the pincell model + self._input_set = PinCellInputSet() # Generate inputs using parent class routine super(MGXSTestHarness, self)._build_inputs() # Initialize a two-group structure - energy_groups = openmc.mgxs.EnergyGroups(group_edges=[0, 0.625e-6, 20.]) + energy_groups = openmc.mgxs.EnergyGroups(group_edges=[0, 0.625e-6, + 20.]) # Initialize MGXS Library for a few cross section types self.mgxs_lib = openmc.mgxs.Library(self._input_set.geometry) self.mgxs_lib.by_nuclide = True - self.mgxs_lib.mgxs_types = ['transport', 'nu-fission', - 'nu-scatter matrix', 'chi'] + # Test all MGXS types + self.mgxs_lib.mgxs_types = openmc.mgxs.MGXS_TYPES self.mgxs_lib.energy_groups = energy_groups self.mgxs_lib.legendre_order = 3 self.mgxs_lib.domain_type = 'material' @@ -53,7 +54,7 @@ class MGXSTestHarness(PyAPITestHarness): for mgxs_type in self.mgxs_lib.mgxs_types: mgxs = self.mgxs_lib.get_mgxs(domain, mgxs_type) df = mgxs.get_pandas_dataframe() - outstr += df.to_string() + outstr += df.to_string() + '\n' # Hash the results if necessary if hash_output: diff --git a/tests/test_multipole/results_true.dat b/tests/test_multipole/results_true.dat index 83d7e762e8..d138fa16a9 100644 --- a/tests/test_multipole/results_true.dat +++ b/tests/test_multipole/results_true.dat @@ -3,7 +3,7 @@ k-combined: Cell ID = 11 Name = - Material = 2 + Fill = Material 2 Region = -10000 Rotation = None Temperature = [ 500. 0. 700. 800.] diff --git a/tests/test_periodic/inputs_true.dat b/tests/test_periodic/inputs_true.dat new file mode 100644 index 0000000000..d50d0b8592 --- /dev/null +++ b/tests/test_periodic/inputs_true.dat @@ -0,0 +1 @@ +af589996f2930337afe34ba9894098ff5efe3b29b6e927117220b718bf29b630ffdbc931754d465a8e8100125a8aa997dbe10aab322b43f69d59710573996a6d \ No newline at end of file diff --git a/tests/test_periodic/results_true.dat b/tests/test_periodic/results_true.dat new file mode 100644 index 0000000000..b0bdb22c7d --- /dev/null +++ b/tests/test_periodic/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +1.040109E+00 6.527490E-02 diff --git a/tests/test_periodic/tallies.xml b/tests/test_periodic/tallies.xml new file mode 100644 index 0000000000..595d7c0ddd --- /dev/null +++ b/tests/test_periodic/tallies.xml @@ -0,0 +1,14 @@ + + + + regular + -200. -1e50 + 200. 1e50 + 50 1 + + + collision + + fission + + diff --git a/tests/test_periodic/test_periodic.py b/tests/test_periodic/test_periodic.py new file mode 100644 index 0000000000..558514575a --- /dev/null +++ b/tests/test_periodic/test_periodic.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python + +import os +import sys +sys.path.insert(0, os.pardir) +from testing_harness import PyAPITestHarness +import openmc + + +class PeriodicTest(PyAPITestHarness): + def _build_inputs(self): + # Define materials + water = openmc.Material(1) + water.add_nuclide('H-1', 2.0) + water.add_nuclide('O-16', 1.0) + water.add_s_alpha_beta('HH2O', '71t') + water.set_density('g/cc', 1.0) + + fuel = openmc.Material(2) + fuel.add_nuclide('U-235', 1.0) + fuel.set_density('g/cc', 4.5) + + materials = openmc.Materials((water, fuel)) + materials.default_xs = '71c' + materials.export_to_xml() + + # Define geometry + x_min = openmc.XPlane(1, x0=-5., boundary_type='periodic') + x_max = openmc.XPlane(2, x0=5., boundary_type='periodic') + x_max.periodic_surface = x_min + + y_min = openmc.YPlane(3, y0=-5., boundary_type='periodic') + y_max = openmc.YPlane(4, y0=5., boundary_type='periodic') + + z_min = openmc.ZPlane(5, z0=-5., boundary_type='reflective') + z_max = openmc.ZPlane(6, z0=5., boundary_type='reflective') + z_cyl = openmc.ZCylinder(7, x0=-2.5, y0=2.5, R=2.0) + + outside_cyl = openmc.Cell(1, fill=water, region=( + +x_min & -x_max & +y_min & -y_max & +z_min & -z_max & +z_cyl)) + inside_cyl = openmc.Cell(2, fill=fuel, region=+z_min & -z_max & -z_cyl) + root_universe = openmc.Universe(0, cells=(outside_cyl, inside_cyl)) + + geometry = openmc.Geometry() + geometry.root_universe = root_universe + geometry.export_to_xml() + + # Define settings + settings = openmc.Settings() + settings.particles = 1000 + settings.batches = 4 + settings.inactive = 0 + settings.source = openmc.Source(space=openmc.stats.Box( + *outside_cyl.region.bounding_box)) + settings.export_to_xml() + + +if __name__ == '__main__': + harness = PeriodicTest('statepoint.4.h5') + harness.main() diff --git a/tests/test_tallies/inputs_true.dat b/tests/test_tallies/inputs_true.dat index be789fc838..e3d37be300 100644 --- a/tests/test_tallies/inputs_true.dat +++ b/tests/test_tallies/inputs_true.dat @@ -1 +1 @@ -0597eff3fddbc45a09b5b324c9704e540b694b07c136f2040426fdcfe5ec544f036073e4afa34a5fb0fbd721a4c0a609b9b68bf17ce4ec78302023b46b71930c \ No newline at end of file +ea09926d8f5c6c96529bf5529f4deb3be78eda2da80adbbf3440147c337587358c2b1823bc72df9463676135573eb481dcd361b735f18365216645ee81092f1e \ No newline at end of file diff --git a/tests/test_tallies/results_true.dat b/tests/test_tallies/results_true.dat index fd5eb91a1a..ff3a828454 100644 --- a/tests/test_tallies/results_true.dat +++ b/tests/test_tallies/results_true.dat @@ -1 +1 @@ -9f14aaa1694489032b3ce193ad29ecf6ac8976c88c2dd6b26d4c30ae88348e249a9b702b1d39c22204350b8f3bd689800c1b6a6003f19c7bdaf64084a209a2cc \ No newline at end of file +a0c7d6ca246ecd7dd5fed06373af142390971401c4e97744f29e55810ab9c231c97c4d8947cdf0b3d2df0ae829a9ddf768e5b2d889bbea34f2b6db0e567db884 \ No newline at end of file diff --git a/tests/test_tallies/test_tallies.py b/tests/test_tallies/test_tallies.py index 52d4084fde..9e40d4185d 100644 --- a/tests/test_tallies/test_tallies.py +++ b/tests/test_tallies/test_tallies.py @@ -160,9 +160,6 @@ class TalliesTestHarness(PyAPITestHarness): total_tallies[2].estimator = 'analog' total_tallies[3].estimator = 'collision' - questionable_tally = Tally() - questionable_tally.scores = ['transport', 'n1n'] - all_nuclide_tallies = [Tally(), Tally()] for t in all_nuclide_tallies: t.filters = [cell_filter] @@ -182,7 +179,6 @@ class TalliesTestHarness(PyAPITestHarness): self._input_set.tallies += flux_tallies self._input_set.tallies += (scatter_tally1, scatter_tally2) self._input_set.tallies += total_tallies - self._input_set.tallies.append(questionable_tally) self._input_set.tallies += all_nuclide_tallies self._input_set.export() diff --git a/tests/test_triso/inputs_true.dat b/tests/test_triso/inputs_true.dat new file mode 100644 index 0000000000..3e54e5e6fe --- /dev/null +++ b/tests/test_triso/inputs_true.dat @@ -0,0 +1 @@ +2dcfd1a17cba671874e60192a7355deb57e2e51467a474fd168c8b51e454a977edb34df07ae11625c0a43906112152c75113e442a9a8f240a4c9d1a11ee4771d \ No newline at end of file diff --git a/tests/test_triso/plots.xml b/tests/test_triso/plots.xml new file mode 100644 index 0000000000..60ae7d9d8f --- /dev/null +++ b/tests/test_triso/plots.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/tests/test_triso/results_true.dat b/tests/test_triso/results_true.dat new file mode 100644 index 0000000000..ea7da21edf --- /dev/null +++ b/tests/test_triso/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +1.662675E+00 1.475968E-02 diff --git a/tests/test_triso/test_triso.py b/tests/test_triso/test_triso.py new file mode 100644 index 0000000000..d1ac4e5cdc --- /dev/null +++ b/tests/test_triso/test_triso.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python + +import os +import sys +import glob +import random +from math import sqrt + +import numpy as np + +sys.path.insert(0, os.pardir) +from testing_harness import PyAPITestHarness +import openmc +import openmc.model + + +class TRISOTestHarness(PyAPITestHarness): + def _build_inputs(self): + # Define TRISO matrials + fuel = openmc.Material() + fuel.set_density('g/cm3', 10.5) + fuel.add_nuclide('U-235', 0.14154) + fuel.add_nuclide('U-238', 0.85846) + fuel.add_nuclide('C-Nat', 0.5) + fuel.add_nuclide('O-16', 1.5) + + porous_carbon = openmc.Material() + porous_carbon.set_density('g/cm3', 1.0) + porous_carbon.add_nuclide('C-Nat', 1.0) + porous_carbon.add_s_alpha_beta('Graph', '71t') + + ipyc = openmc.Material() + ipyc.set_density('g/cm3', 1.90) + ipyc.add_nuclide('C-Nat', 1.0) + ipyc.add_s_alpha_beta('Graph', '71t') + + sic = openmc.Material() + sic.set_density('g/cm3', 3.20) + sic.add_element('Si', 1.0) + sic.add_nuclide('C-Nat', 1.0) + + opyc = openmc.Material() + opyc.set_density('g/cm3', 1.87) + opyc.add_nuclide('C-Nat', 1.0) + opyc.add_s_alpha_beta('Graph', '71t') + + graphite = openmc.Material() + graphite.set_density('g/cm3', 1.1995) + graphite.add_nuclide('C-Nat', 1.0) + graphite.add_s_alpha_beta('Graph', '71t') + + # Create TRISO particles + spheres = [openmc.Sphere(R=r*1e-4) + for r in [212.5, 312.5, 347.5, 382.5]] + c1 = openmc.Cell(fill=fuel, region=-spheres[0]) + c2 = openmc.Cell(fill=porous_carbon, region=+spheres[0] & -spheres[1]) + c3 = openmc.Cell(fill=ipyc, region=+spheres[1] & -spheres[2]) + c4 = openmc.Cell(fill=sic, region=+spheres[2] & -spheres[3]) + c5 = openmc.Cell(fill=opyc, region=+spheres[3]) + inner_univ = openmc.Universe(cells=[c1, c2, c3, c4, c5]) + + outer_radius = 422.5*1e-4 + trisos = [] + random.seed(1) + for i in range(100): + # Randomly sample location + lim = 0.5 - outer_radius*1.001 + x = random.uniform(-lim, lim) + y = random.uniform(-lim, lim) + z = random.uniform(-lim, lim) + t = openmc.model.TRISO(outer_radius, inner_univ, (x, y, z)) + + # Make sure TRISO doesn't overlap with another + for tp in trisos: + xp, yp, zp = tp.center + distance = sqrt((x - xp)**2 + (y - yp)**2 + (z - zp)**2) + if distance <= 2*outer_radius: + break + else: + trisos.append(t) + + # Define box to contain lattice + min_x = openmc.XPlane(x0=-0.5, boundary_type='reflective') + max_x = openmc.XPlane(x0=0.5, boundary_type='reflective') + min_y = openmc.YPlane(y0=-0.5, boundary_type='reflective') + max_y = openmc.YPlane(y0=0.5, boundary_type='reflective') + min_z = openmc.ZPlane(z0=-0.5, boundary_type='reflective') + max_z = openmc.ZPlane(z0=0.5, boundary_type='reflective') + box = openmc.Cell(region=+min_x & -max_x & +min_y & -max_y & +min_z & -max_z) + + # Create lattice + ll, ur = box.region.bounding_box + shape = (3, 3, 3) + pitch = (ur - ll) / shape + lattice = openmc.model.create_triso_lattice( + trisos, ll, pitch, shape, graphite) + box.fill = lattice + + root = openmc.Universe(0, cells=[box]) + geom = openmc.Geometry(root) + geom.export_to_xml() + + settings = openmc.Settings() + settings.batches = 5 + settings.inactive = 0 + settings.particles = 100 + settings.source = openmc.Source(space=openmc.stats.Point()) + settings.export_to_xml() + + mats = openmc.Materials([fuel, porous_carbon, ipyc, sic, opyc, graphite]) + mats.default_xs = '71c' + mats.export_to_xml() + + +if __name__ == '__main__': + harness = TRISOTestHarness('statepoint.5.h5') + harness.main() diff --git a/tests/testing_harness.py b/tests/testing_harness.py index 78e5553e8c..d360184045 100644 --- a/tests/testing_harness.py +++ b/tests/testing_harness.py @@ -6,7 +6,6 @@ import hashlib from optparse import OptionParser import os import shutil -from subprocess import Popen, STDOUT, PIPE, call import sys import numpy as np @@ -18,6 +17,7 @@ import openmc class TestHarness(object): """General class for running OpenMC regression tests.""" + def __init__(self, statepoint_name, tallies_present=False): self._sp_name = statepoint_name self._tallies = tallies_present @@ -74,13 +74,13 @@ class TestHarness(object): def _test_output_created(self): """Make sure statepoint.* and tallies.out have been created.""" statepoint = glob.glob(os.path.join(os.getcwd(), self._sp_name)) - assert len(statepoint) == 1, 'Either multiple or no statepoint files ' \ - 'exist.' + assert len(statepoint) == 1, 'Either multiple or no statepoint files' \ + ' exist.' assert statepoint[0].endswith('h5'), \ - 'Statepoint file is not a HDF5 file.' + 'Statepoint file is not a HDF5 file.' if self._tallies: assert os.path.exists(os.path.join(os.getcwd(), 'tallies.out')), \ - 'Tally output file does not exist.' + 'Tally output file does not exist.' def _get_results(self, hash_output=False): """Digest info in the statepoint and return as a string.""" @@ -98,7 +98,7 @@ class TestHarness(object): tally_num = 1 for tally_ind in sp.tallies: tally = sp.tallies[tally_ind] - results = np.zeros((tally.sum.size*2, )) + results = np.zeros((tally.sum.size * 2, )) results[0::2] = tally.sum.ravel() results[1::2] = tally.sum_sq.ravel() results = ['{0:12.6E}'.format(x) for x in results] @@ -133,9 +133,10 @@ class TestHarness(object): def _cleanup(self): """Delete statepoints, tally, and test files.""" - output = glob.glob(os.path.join(os.getcwd(), 'statepoint.*.*')) + output = glob.glob(os.path.join(os.getcwd(), 'statepoint.*.h5')) output.append(os.path.join(os.getcwd(), 'tallies.out')) output.append(os.path.join(os.getcwd(), 'results_test.dat')) + output.append(os.path.join(os.getcwd(), 'summary.h5')) for f in output: if os.path.exists(f): os.remove(f) @@ -143,6 +144,7 @@ class TestHarness(object): class HashedTestHarness(TestHarness): """Specialized TestHarness that hashes the results.""" + def _get_results(self): """Digest info in the statepoint and return as a string.""" return super(HashedTestHarness, self)._get_results(True) @@ -150,6 +152,7 @@ class HashedTestHarness(TestHarness): class CMFDTestHarness(TestHarness): """Specialized TestHarness for running OpenMC CMFD tests.""" + def _get_results(self): """Digest info in the statepoint and return as a string.""" # Read the statepoint file. @@ -183,6 +186,7 @@ class CMFDTestHarness(TestHarness): class ParticleRestartTestHarness(TestHarness): """Specialized TestHarness for running OpenMC particle restart tests.""" + def _run_openmc(self): # Set arguments args = {'openmc_exec': self._opts.exe} @@ -203,9 +207,9 @@ class ParticleRestartTestHarness(TestHarness): """Make sure the restart file has been created.""" particle = glob.glob(os.path.join(os.getcwd(), self._sp_name)) assert len(particle) == 1, 'Either multiple or no particle restart ' \ - 'files exist.' + 'files exist.' assert particle[0].endswith('h5'), \ - 'Particle restart file is not a HDF5 file.' + 'Particle restart file is not a HDF5 file.' def _get_results(self): """Digest info in the statepoint and return as a string.""" @@ -228,10 +232,10 @@ class ParticleRestartTestHarness(TestHarness): outstr += 'particle energy:\n' outstr += "{0:12.6E}\n".format(p.energy) outstr += 'particle xyz:\n' - outstr += "{0:12.6E} {1:12.6E} {2:12.6E}\n".format(p.xyz[0],p.xyz[1], + outstr += "{0:12.6E} {1:12.6E} {2:12.6E}\n".format(p.xyz[0], p.xyz[1], p.xyz[2]) outstr += 'particle uvw:\n' - outstr += "{0:12.6E} {1:12.6E} {2:12.6E}\n".format(p.uvw[0],p.uvw[1], + outstr += "{0:12.6E} {1:12.6E} {2:12.6E}\n".format(p.uvw[0], p.uvw[1], p.uvw[2]) return outstr @@ -239,13 +243,15 @@ class ParticleRestartTestHarness(TestHarness): class PyAPITestHarness(TestHarness): def __init__(self, statepoint_name, tallies_present=False, mg=False): - super(PyAPITestHarness, self).__init__(statepoint_name, tallies_present) + super(PyAPITestHarness, self).__init__(statepoint_name, + tallies_present) self.parser.add_option('--build-inputs', dest='build_only', action='store_true', default=False) if mg: self._input_set = MGInputSet() else: self._input_set = InputSet() + def main(self): """Accept commandline arguments and either run or update tests.""" (self._opts, self._args) = self.parser.parse_args() @@ -320,7 +326,8 @@ class PyAPITestHarness(TestHarness): compare = filecmp.cmp('inputs_test.dat', 'inputs_true.dat') if not compare: f = open('inputs_test.dat') - for line in f.readlines(): print(line) + for line in f.readlines(): + print(line) f.close() os.rename('inputs_test.dat', 'inputs_error.dat') assert compare, 'Input files are broken.'