diff --git a/CMakeLists.txt b/CMakeLists.txt index 1d5cea51a..6c8b5e17e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,6 @@ option(profile "Compile with profiling flags" OFF) option(debug "Compile with debug flags" OFF) option(optimize "Turn on all compiler optimization flags" OFF) option(coverage "Compile with coverage analysis flags" OFF) -option(mpif08 "Use Fortran 2008 MPI interface" OFF) option(dagmc "Enable support for DAGMC (CAD) geometry" OFF) # Maximum number of nested coordinates levels @@ -345,7 +344,6 @@ add_library(libopenmc SHARED src/photon_physics.F90 src/physics_common.F90 src/physics.F90 - src/physics_mg.F90 src/plot.F90 src/plot_header.F90 src/progress_header.F90 @@ -408,6 +406,7 @@ add_library(libopenmc SHARED src/distribution_spatial.cpp src/eigenvalue.cpp src/endf.cpp + src/error.cpp src/initialize.cpp src/finalize.cpp src/geometry.cpp @@ -423,6 +422,8 @@ add_library(libopenmc SHARED src/nuclide.cpp src/output.cpp src/particle.cpp + src/physics_common.cpp + src/physics_mg.cpp src/plot.cpp src/position.cpp src/pugixml/pugixml_c.cpp @@ -441,10 +442,13 @@ add_library(libopenmc SHARED src/string_functions.cpp src/summary.cpp src/surface.cpp + src/tallies/tally.cpp src/tallies/tally_filter.cpp + src/timer.cpp src/thermal.cpp src/xml_interface.cpp - src/xsdata.cpp) + src/xsdata.cpp + src/tallies/tally.cpp) set_target_properties(libopenmc PROPERTIES OUTPUT_NAME openmc @@ -471,9 +475,6 @@ if (HDF5_IS_PARALLEL) endif() if (MPI_ENABLED) target_compile_definitions(libopenmc PUBLIC -DOPENMC_MPI) - if (mpif08) - target_compile_definitions(libopenmc PRIVATE -DOPENMC_MPIF08) - endif() endif() # Set git SHA1 hash as a compile definition diff --git a/examples/jupyter/pandas-dataframes.ipynb b/examples/jupyter/pandas-dataframes.ipynb index 72d5d7313..140a0d684 100644 --- a/examples/jupyter/pandas-dataframes.ipynb +++ b/examples/jupyter/pandas-dataframes.ipynb @@ -215,9 +215,7 @@ { "cell_type": "code", "execution_count": 9, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "# OpenMC simulation parameters\n", @@ -281,18 +279,7 @@ "cell_type": "code", "execution_count": 11, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# Run openmc in plotting mode\n", "openmc.plot_geometry(output=False)" @@ -305,7 +292,7 @@ "outputs": [ { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPoAAAD6AgMAAAD1grKuAAAABGdBTUEAALGPC/xhBQAAAAFzUkdC\nAK7OHOkAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAxQTFRF\n////chIS6YCRTb/E6kGE+wAAAAFiS0dEAIgFHUgAAAAJcEhZcwAAAEgAAABIAEbJaz4AAAPZSURB\nVGje7Zs7buMwEIZ9iey50gyNjQpXKTYudIScgkdQYTfut1idwkdQkQNsYQO2Qj0sPiVK+mlQDmwg\nwIcgg8Cc4fCTSK5W4OeFkM8rHv+2I/rgxPZEPZgR7XtQxKdXYuUXJSUnBQ/9WCgo4vOSJ+WFUvF7\nE08mlia+rn7VcKXP8sRszFX8b2MdX2y6v1Tw6MZUw4H4ojfIjD8mvn/qRL5p4+vvlMqvp2EhR8WB\nzfiz20hXORmP9fi/bM9EeUFvV5H/0yRkeSbiGRfFJErxD9ENdz7Mbhig/h89fvtFdMiI/ePUIXV4\nlXju8K3DKv9NThOZ3q2KmUy6grxFES8rjeyic+FFQav+ncg3fXjH+Ts+/iibztFqOiZuZP/Z3Oaf\nPX40NGgST2r+uvQkXXp6cKvmr+r0e1Eef5um3+JHP3IFF1D/seNZJgaDmvY0Gav1s+2f1fqpIcub\nlfKGt6apotG/NVx3SInWtLX+7Vg/Pv1YqOsnun6JSVdOXT/X7vk75f938QP+8OmSBs0fXtymMhJb\nf8qlPynYmpKCh7OB1fzNalOj1sl0ZAruHLiA+RM73pDe/VjMVP89+aTXwjyc/x5n+u991895/utr\nJTy8/06TXh0r/5JOa2JmYmqi4r/vUm/H4wLmT+z4anhr05X+q6KUXhtzr/9qSff5L5uMT//V/NdU\n4YuBTPa/8P67l/6r44ds+hYuoP5jx9ciy6XTWlibBrmx8V/TdMfjkP+6pOsu/lvM9N90sf7r+f6m\n/65n+S8p/itN15v0UkW3/+48+PRfJX6S9Joo4g+G/1qYG9KroqP/WypcuvyXPf13wH89/hHef7MB\n6R3Cqn55U4rv4kfH3zaSgQuYP7HjVf89tXrbO+hfLdr+Ozv/SP1dgtQ/Ov8C+i/3+q/Zf2D/HWi6\nbjT6rym9I/v/03/b+LHS4cTg/utTsV7/net/Afzz4f0XGX84/2j9xZ4/sePR/of2X7D/o+vPo/sv\n6h9B/Bfxr9j1Hz2eN/hO8/wfff4A848+f/1A/530/I0+/8PvH9D3H9HnT+R49P0b+v4PfP/4E/wX\nfP8Mvf9G37/D/ovuP8SeP7Hj0f0vdP8tqP9O339cyv7p3P1fdP8Z3v9G999j13/seMax8x/o+ZN7\n+O+E8zdP/8XOf8Hnz9Dzb7HnT+x49PxlCp7/BM+fOv13wvnXBfivt2lMvD8TyH/Hnb+Gz3+j589j\nz5/Y8ej9h4D+W7qQmf57efqv239n3T+C7z+h969i13/seMax+3/o/cMcu/8Y2H9n3p+J6r98pv8m\n4fwXuH+M3n+OO3++AX9clR+4PhbRAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE3LTA2LTA3VDEzOjE4\nOjQ5LTA0OjAwxfC/BgAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNy0wNi0wN1QxMzoxODo0OS0wNDow\nMLStB7oAAAAASUVORK5CYII=\n", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPoAAAD6AgMAAAD1grKuAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAADFBMVEX///9yEhLpgJFNv8TqQYT7AAAAAWJLR0QAiAUdSAAAAAd0SU1FB+IKCxApGTbiG9IAAAPZSURBVGje7Zs7buMwEIZ9iey50gyNjQpXKTYudIScgkdQYTfut1idwkdQkQNsYQO2Qj0sPiVK+mlQDmwgwIcgg8Cc4fCTSK5W4OeFkM8rHv+2I/rgxPZEPZgR7XtQxKdXYuUXJSUnBQ/9WCgo4vOSJ+WFUvF7E08mlia+rn7VcKXP8sRszFX8b2MdX2y6v1Tw6MZUw4H4ojfIjD8mvn/qRL5p4+vvlMqvp2EhR8WBzfiz20hXORmP9fi/bM9EeUFvV5H/0yRkeSbiGRfFJErxD9ENdz7Mbhig/h89fvtFdMiI/ePUIXV4lXju8K3DKv9NThOZ3q2KmUy6grxFES8rjeyic+FFQav+ncg3fXjH+Ts+/iibztFqOiZuZP/Z3OafPX40NGgST2r+uvQkXXp6cKvmr+r0e1Eef5um3+JHP3IFF1D/seNZJgaDmvY0Gav1s+2f1fqpIcublfKGt6apotG/NVx3SInWtLX+7Vg/Pv1YqOsnun6JSVdOXT/X7vk75f938QP+8OmSBs0fXtymMhJbf8qlPynYmpKCh7OB1fzNalOj1sl0ZAruHLiA+RM73pDe/VjMVP89+aTXwjyc/x5n+u991895/utrJTy8/06TXh0r/5JOa2JmYmqi4r/vUm/H4wLmT+z4anhr05X+q6KUXhtzr/9qSff5L5uMT//V/NdU4YuBTPa/8P67l/6r44ds+hYuoP5jx9ciy6XTWlibBrmx8V/TdMfjkP+6pOsu/lvM9N90sf7r+f6m/65n+S8p/itN15v0UkW3/+48+PRfJX6S9Joo4g+G/1qYG9KroqP/WypcuvyXPf13wH89/hHef7MB6R3Cqn55U4rv4kfH3zaSgQuYP7HjVf89tXrbO+hfLdr+Ozv/SP1dgtQ/Ov8C+i/3+q/Zf2D/HWi6bjT6rym9I/v/03/b+LHS4cTg/utTsV7/net/Afzz4f0XGX84/2j9xZ4/sePR/of2X7D/o+vPo/sv6h9B/Bfxr9j1Hz2eN/hO8/wfff4A848+f/1A/530/I0+/8PvH9D3H9HnT+R49P0b+v4PfP/4E/wXfP8Mvf9G37/D/ovuP8SeP7Hj0f0vdP8tqP9O339cyv7p3P1fdP8Z3v9G999j13/seMax8x/o+ZN7+O+E8zdP/8XOf8Hnz9Dzb7HnT+x49PxlCp7/BM+fOv13wvnXBfivt2lMvD8TyH/Hnb+Gz3+j589jz5/Y8ej9h4D+W7qQmf57efqv239n3T+C7z+h969i13/seMax+3/o/cMcu/8Y2H9n3p+J6r98pv8m4fwXuH+M3n+OO3++AX9clR+4PhbRAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE4LTEwLTExVDE2OjQxOjI1KzAyOjAwV3beHQAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxOC0xMC0xMVQxNjo0MToyNSswMjowMCYrZqEAAAAASUVORK5CYII=\n", "text/plain": [ "" ] @@ -333,9 +320,7 @@ { "cell_type": "code", "execution_count": 13, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "# Instantiate an empty Tallies object\n", @@ -396,7 +381,7 @@ "# Instantiate the tally\n", "tally = openmc.Tally(name='cell tally')\n", "tally.filters = [cell_filter]\n", - "tally.scores = ['scatter-y2']\n", + "tally.scores = ['scatter']\n", "tally.nuclides = ['U235', 'U238']\n", "\n", "# Add mesh and tally to Tallies\n", @@ -436,9 +421,7 @@ { "cell_type": "code", "execution_count": 17, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "# Export to \"tallies.xml\"\n", @@ -487,27 +470,27 @@ " %%%%%%%%%%%\n", "\n", " | The OpenMC Monte Carlo Code\n", - " Copyright | 2011-2017 Massachusetts Institute of Technology\n", + " Copyright | 2011-2018 MIT and OpenMC contributors\n", " License | http://openmc.readthedocs.io/en/latest/license.html\n", - " Version | 0.9.0\n", - " Git SHA1 | 897db1389dec7871026442bae96d9410ade85192\n", - " Date/Time | 2017-06-07 13:18:50\n", - " MPI Processes | 1\n", - " OpenMP Threads | 12\n", + " Version | 0.10.0\n", + " Git SHA1 | 199126b2fcc5cb094f2cc820ae13e1a972cacddd\n", + " Date/Time | 2018-10-11 16:41:25\n", + " OpenMP Threads | 8\n", "\n", " Reading settings XML file...\n", - " Reading geometry XML file...\n", - " Reading materials XML file...\n", " Reading cross sections XML file...\n", - " Reading U235 from /home/johnny/Github/openmc/scripts/nndc_hdf5/U235.h5\n", - " Reading U238 from /home/johnny/Github/openmc/scripts/nndc_hdf5/U238.h5\n", - " Reading O16 from /home/johnny/Github/openmc/scripts/nndc_hdf5/O16.h5\n", - " Reading H1 from /home/johnny/Github/openmc/scripts/nndc_hdf5/H1.h5\n", - " Reading B10 from /home/johnny/Github/openmc/scripts/nndc_hdf5/B10.h5\n", - " Reading Zr90 from /home/johnny/Github/openmc/scripts/nndc_hdf5/Zr90.h5\n", + " Reading materials XML file...\n", + " Reading geometry XML file...\n", + " Building neighboring cells lists for each surface...\n", + " Reading U235 from /home/jan/openmc/nndc_hdf5/U235.h5\n", + " Reading U238 from /home/jan/openmc/nndc_hdf5/U238.h5\n", + " Reading O16 from /home/jan/openmc/nndc_hdf5/O16.h5\n", + " Reading H1 from /home/jan/openmc/nndc_hdf5/H1.h5\n", + " Reading B10 from /home/jan/openmc/nndc_hdf5/B10.h5\n", + " Reading Zr90 from /home/jan/openmc/nndc_hdf5/Zr90.h5\n", " Maximum neutron transport energy: 2.00000E+07 eV for U235\n", " Reading tallies XML file...\n", - " Building neighboring cells lists for each surface...\n", + " Writing summary.h5 file...\n", " Initializing source particles...\n", "\n", " ====================> K EIGENVALUE SIMULATION <====================\n", @@ -534,7 +517,7 @@ " 18/1 0.67696 0.68367 +/- 0.00625\n", " 19/1 0.65444 0.68158 +/- 0.00615\n", " 20/1 0.69766 0.68266 +/- 0.00583\n", - " Triggers unsatisfied, max unc./thresh. is 1.17617 for absorption in tally 10002\n", + " Triggers unsatisfied, max unc./thresh. is 1.17617 for absorption in tally 3\n", " The estimated number of batches is 26\n", " Creating state point statepoint.020.h5...\n", " 21/1 0.64126 0.68007 +/- 0.00603\n", @@ -548,20 +531,20 @@ "\n", " =======================> TIMING STATISTICS <=======================\n", "\n", - " Total time for initialization = 3.6433E-01 seconds\n", - " Reading cross sections = 2.7963E-01 seconds\n", - " Total time in simulation = 1.4715E+00 seconds\n", - " Time in transport only = 1.3171E+00 seconds\n", - " Time in inactive batches = 2.2329E-01 seconds\n", - " Time in active batches = 1.2482E+00 seconds\n", - " Time synchronizing fission bank = 2.3145E-03 seconds\n", - " Sampling source sites = 1.4054E-03 seconds\n", - " SEND/RECV source sites = 7.6241E-04 seconds\n", - " Time accumulating tallies = 5.8822E-04 seconds\n", - " Total time for finalization = 5.0159E-05 seconds\n", - " Total time elapsed = 1.8511E+00 seconds\n", - " Calculation Rate (inactive) = 55980.8 neutrons/second\n", - " Calculation Rate (active) = 30044.0 neutrons/second\n", + " Total time for initialization = 6.5303E-01 seconds\n", + " Reading cross sections = 5.8105E-01 seconds\n", + " Total time in simulation = 4.6015E+00 seconds\n", + " Time in transport only = 3.5767E+00 seconds\n", + " Time in inactive batches = 4.5008E-01 seconds\n", + " Time in active batches = 4.1514E+00 seconds\n", + " Time synchronizing fission bank = 2.3493E-03 seconds\n", + " Sampling source sites = 1.7160E-03 seconds\n", + " SEND/RECV source sites = 4.7010E-04 seconds\n", + " Time accumulating tallies = 2.3040E-04 seconds\n", + " Total time for finalization = 2.6451E-02 seconds\n", + " Total time elapsed = 5.3123E+00 seconds\n", + " Calculation Rate (inactive) = 27772.9 particles/second\n", + " Calculation Rate (active) = 12646.3 particles/second\n", "\n", " ============================> RESULTS <============================\n", "\n", @@ -572,16 +555,6 @@ " Leakage Fraction = 0.34011 +/- 0.00283\n", "\n" ] - }, - { - "data": { - "text/plain": [ - "0" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" } ], "source": [ @@ -630,7 +603,7 @@ "output_type": "stream", "text": [ "Tally\n", - "\tID =\t10000\n", + "\tID =\t1\n", "\tName =\tmesh tally\n", "\tFilters =\tMeshFilter, EnergyFilter\n", "\tNuclides =\ttotal \n", @@ -664,13 +637,13 @@ "name": "stdout", "output_type": "stream", "text": [ - "[[[ 0.17581417]]\n", + "[[[0.17581417]]\n", "\n", - " [[ 0.30578219]]\n", + " [[0.06842901]]\n", "\n", - " [[ 0.06842901]]\n", + " [[0.30578219]]\n", "\n", - " [[ 0.12436752]]]\n" + " [[0.12436752]]]\n" ] } ], @@ -693,18 +666,18 @@ "data": { "text/html": [ "
\n", - "\n", "\n", " \n", @@ -776,9 +749,9 @@ " \n", " \n", " \n", - " \n", " \n", " \n", + " \n", " \n", " \n", " \n", @@ -787,9 +760,9 @@ " \n", " \n", " \n", - " \n", " \n", " \n", + " \n", " \n", " \n", " \n", @@ -798,9 +771,9 @@ " \n", " \n", " \n", - " \n", " \n", " \n", + " \n", " \n", " \n", " \n", @@ -809,9 +782,9 @@ " \n", " \n", " \n", - " \n", " \n", " \n", + " \n", " \n", " \n", " \n", @@ -820,9 +793,9 @@ " \n", " \n", " \n", - " \n", " \n", " \n", + " \n", " \n", " \n", " \n", @@ -831,9 +804,9 @@ " \n", " \n", " \n", - " \n", " \n", " \n", + " \n", " \n", " \n", " \n", @@ -842,9 +815,9 @@ " \n", " \n", " \n", - " \n", " \n", " \n", + " \n", " \n", " \n", " \n", @@ -853,9 +826,9 @@ " \n", " \n", " \n", - " \n", " \n", " \n", + " \n", " \n", " \n", " \n", @@ -864,9 +837,9 @@ " \n", " \n", " \n", - " \n", " \n", " \n", + " \n", " \n", " \n", " \n", @@ -875,9 +848,9 @@ " \n", " \n", " \n", - " \n", " \n", " \n", + " \n", " \n", " \n", " \n", @@ -886,9 +859,9 @@ " \n", " \n", " \n", - " \n", " \n", " \n", + " \n", " \n", " \n", " \n", @@ -897,9 +870,9 @@ " \n", " \n", " \n", - " \n", " \n", " \n", + " \n", " \n", " \n", " \n", @@ -908,9 +881,9 @@ " \n", " \n", " \n", - " \n", " \n", " \n", + " \n", " \n", " \n", " \n", @@ -919,9 +892,9 @@ " \n", " \n", " \n", - " \n", " \n", " \n", + " \n", " \n", " \n", " \n", @@ -930,9 +903,9 @@ " \n", " \n", " \n", - " \n", " \n", " \n", + " \n", " \n", " \n", " \n", @@ -941,9 +914,9 @@ " \n", " \n", " \n", - " \n", " \n", " \n", + " \n", " \n", " \n", " \n", @@ -961,22 +934,22 @@ "1 1 1 1 0.00e+00 6.25e-01 nu-fission 5.46e-04 \n", "2 1 1 1 6.25e-01 2.00e+07 fission 8.42e-05 \n", "3 1 1 1 6.25e-01 2.00e+07 nu-fission 2.22e-04 \n", - "4 1 2 1 0.00e+00 6.25e-01 fission 1.85e-04 \n", - "5 1 2 1 0.00e+00 6.25e-01 nu-fission 4.52e-04 \n", - "6 1 2 1 6.25e-01 2.00e+07 fission 6.82e-05 \n", - "7 1 2 1 6.25e-01 2.00e+07 nu-fission 1.81e-04 \n", - "8 1 3 1 0.00e+00 6.25e-01 fission 2.05e-04 \n", - "9 1 3 1 0.00e+00 6.25e-01 nu-fission 5.00e-04 \n", - "10 1 3 1 6.25e-01 2.00e+07 fission 7.53e-05 \n", - "11 1 3 1 6.25e-01 2.00e+07 nu-fission 1.99e-04 \n", - "12 1 4 1 0.00e+00 6.25e-01 fission 2.06e-04 \n", - "13 1 4 1 0.00e+00 6.25e-01 nu-fission 5.03e-04 \n", - "14 1 4 1 6.25e-01 2.00e+07 fission 6.65e-05 \n", - "15 1 4 1 6.25e-01 2.00e+07 nu-fission 1.75e-04 \n", - "16 1 5 1 0.00e+00 6.25e-01 fission 2.03e-04 \n", - "17 1 5 1 0.00e+00 6.25e-01 nu-fission 4.94e-04 \n", - "18 1 5 1 6.25e-01 2.00e+07 fission 6.26e-05 \n", - "19 1 5 1 6.25e-01 2.00e+07 nu-fission 1.64e-04 \n", + "4 2 1 1 0.00e+00 6.25e-01 fission 1.85e-04 \n", + "5 2 1 1 0.00e+00 6.25e-01 nu-fission 4.52e-04 \n", + "6 2 1 1 6.25e-01 2.00e+07 fission 6.82e-05 \n", + "7 2 1 1 6.25e-01 2.00e+07 nu-fission 1.81e-04 \n", + "8 3 1 1 0.00e+00 6.25e-01 fission 2.05e-04 \n", + "9 3 1 1 0.00e+00 6.25e-01 nu-fission 5.00e-04 \n", + "10 3 1 1 6.25e-01 2.00e+07 fission 7.53e-05 \n", + "11 3 1 1 6.25e-01 2.00e+07 nu-fission 1.99e-04 \n", + "12 4 1 1 0.00e+00 6.25e-01 fission 2.06e-04 \n", + "13 4 1 1 0.00e+00 6.25e-01 nu-fission 5.03e-04 \n", + "14 4 1 1 6.25e-01 2.00e+07 fission 6.65e-05 \n", + "15 4 1 1 6.25e-01 2.00e+07 nu-fission 1.75e-04 \n", + "16 5 1 1 0.00e+00 6.25e-01 fission 2.03e-04 \n", + "17 5 1 1 0.00e+00 6.25e-01 nu-fission 4.94e-04 \n", + "18 5 1 1 6.25e-01 2.00e+07 fission 6.26e-05 \n", + "19 5 1 1 6.25e-01 2.00e+07 nu-fission 1.64e-04 \n", "\n", " std. dev. \n", " \n", @@ -1025,12 +998,14 @@ "outputs": [ { "data": { - "image/png": "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\nJenaNP0hSRMy6l4qKSQN3r9VMbPebNasWdTV1bFkyRJWrlzJkiVLqKurY9asWbVuWp/UYXhIqgMW\nAJOABmCqpIaKYpOAMWmYDlxXTV1JI4DTgF/u95qYWa+2adMmli5dysSJE+nXrx8TJ05k6dKlbNq0\nqdZN65OqOfM4HlgfEU9FxO+BW4EpFWWmAEujcB8wSNLQKup+BZgFxP6uiJmZdZ9q7rYaBmwsvd8E\nnFBFmWHt1ZU0BXgmIh6UtNeFS5pOcTbDkCFDaG5urqLJlqOlpcXb1Q54gwcP5swzz2TAgAE8//zz\nHHXUUWzdupXBgwd7/62BmtyqK+n1wGcpuqzaFRHXA9cDNDY2hn96svP5Jz2tJzjnnHNYuHAhAwcO\nRBKS2LZtG+eff7733xqoptvqGWBE6f3wNK6aMnsb/xZgNPCgpA1p/M8lvTGn8WbWd6xatYo5c+Zw\n5JFHAnDkkUcyZ84cVq1aVeOW9U2KaP9yg6R+wJPAyRQH/tXAuRHxSKnM+4CLgckU3VLXRsTx1dRN\n9TcAjRHxQnttaWxsjDVr1mStoHXMZx7WE9TV1fHb3/6W/v37v7bP7tixg/r6enbt2lXr5vUaku6P\niMaOynV45hEROymC4S7gMeC2iHhE0gxJM1KxFcBTwHpgMfDx9uruw/qYWR83duxY7r333j3G3Xvv\nvYwdO7ZGLerbqrrmERErKAKiPG5R6XUAF1Vbt40yo6pph5n1XXPnzuWCCy7ga1/7Grt27WLVqlVc\ncMEFzJs3r9ZN65P8bCszO2C1dSfmSSedtMf7c889l3PPPXePcR11x9v+8+NJzOyAFRFtDiM/8+29\nTnNwdA+Hh5mZZXN4mJlZNoeHmZllc3iYmVk2h4eZmWVzeJiZWTaHh5mZZXN4mJlZNoeHmZllc3iY\nmVk2h4eZmWVzeJiZWTaHh5mZZXN4mJlZNoeHmZllc3iYmVk2h4eZmWVzeJiZWTaHh5mZZXN4mJlZ\nNoeHmZllc3iYmVk2h4eZmWVzeJiZWTaHh5mZZXN4mJlZNoeHmZllc3iYmVk2h4eZmWVzeJiZWTaH\nh5mZZXN4mJlZNoeHmZllc3iYmVk2h4eZmWWrKjwknS7pCUnrJc1uY7okXZumPyRpQkd1Jf1TKvuA\npJWSju6cVTIzs67WYXhIqgMWAJOABmCqpIaKYpOAMWmYDlxXRd2rI2J8RBwHfBv43P6vjpmZdYdq\nzjyOB9ZHxFMR8XvgVmBKRZkpwNIo3AcMkjS0vboR8Uqp/gAg9nNdzMysm/SroswwYGPp/SbghCrK\nDOuorqR5wHnAFmBi1a02M7OaqiY8ukxEzAXmSpoDXAxcXllG0nSKrjCGDBlCc3Nzt7axL2hpafF2\ntR7H+2xtVRMezwAjSu+Hp3HVlOlfRV2AbwAraCM8IuJ64HqAxsbGaGpqqqLJlqO5uRlvV+tRvrPc\n+2yNVXPNYzUwRtJoSQcB5wDLKsosA85Ld129G9gSEZvbqytpTKn+FODx/VwXMzPrJh2eeUTETkkX\nA3cBdcCSiHhE0ow0fRHFWcNkYD2wDZjWXt006yslvQ3YDTwNzOjUNTMzsy5T1TWPiFhBERDlcYtK\nrwO4qNq6afwHs1pqZmYHDH/D3MzMsjk8zMwsm8PDzMyyOTzMzCybw8PMzLI5PMzMLJvDw8zMsjk8\nzMwsm8PDzMyyOTzMzCybw8PMzLI5PMzMLJvDw8zMsjk8zMwsm8PDzMyyOTzMzCybw8PMzLI5PMzM\nLJvDw8zMsjk8zMwsm8PDzMyyOTzMzCybw8PMzLI5PMzMLJvDw8zMsjk8zMwsm8PDzMyyOTzMzCyb\nw8PMzLI5PMzMLJvDw8zMsjk8zMwsm8PDzMyyOTzMzCybw8PMzLI5PMzMLJvDw8zMsjk8zMwsW1Xh\nIel0SU9IWi9pdhvTJenaNP0hSRM6qivpakmPp/K3SxrUOatkZmZdrcPwkFQHLAAmAQ3AVEkNFcUm\nAWPSMB24roq63wX+OCLGA08Cc/Z7bczMrFtUc+ZxPLA+Ip6KiN8DtwJTKspMAZZG4T5gkKSh7dWN\niJURsTPVvw8Y3gnrY2Zm3aCa8BgGbCy935TGVVOmmroAfwPcWUVbzMzsANCv1g2QNBfYCXxjL9On\nU3SFMWTIEJqbm7uvcX1ES0uLt6v1ON5na6ua8HgGGFF6PzyNq6ZM//bqSjofeD9wckREWwuPiOuB\n6wEaGxujqampiiZbjubmZrxdrUf5znLvszVWTbfVamCMpNGSDgLOAZZVlFkGnJfuuno3sCUiNrdX\nV9LpwCzgjIjY1knrY2Zm3aDDM4+I2CnpYuAuoA5YEhGPSJqRpi8CVgCTgfXANmBae3XTrL8KHAx8\nVxLAfRExozNXzszMukZV1zwiYgVFQJTHLSq9DuCiauum8cdktdTMzA4Y/oa5mZllc3iYmVk2h4eZ\n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\n", + "image/png": "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\n", "text/plain": [ - "" + "
" ] }, - "metadata": {}, + "metadata": { + "needs_background": "light" + }, "output_type": "display_data" } ], @@ -1048,7 +1023,7 @@ { "data": { "text/plain": [ - "" + "" ] }, "execution_count": 24, @@ -1057,12 +1032,14 @@ }, { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ - "" + "
" ] }, - "metadata": {}, + "metadata": { + "needs_background": "light" + }, "output_type": "display_data" } ], @@ -1098,12 +1075,12 @@ "output_type": "stream", "text": [ "Tally\n", - "\tID =\t10001\n", + "\tID =\t2\n", "\tName =\tcell tally\n", "\tFilters =\tCellFilter\n", "\tNuclides =\tU235 U238 \n", - "\tScores =\t['scatter-Y0,0', 'scatter-Y1,-1', 'scatter-Y1,0', 'scatter-Y1,1', 'scatter-Y2,-2', 'scatter-Y2,-1', 'scatter-Y2,0', 'scatter-Y2,1', 'scatter-Y2,2']\n", - "\tEstimator =\tanalog\n", + "\tScores =\t['scatter']\n", + "\tEstimator =\ttracklength\n", "\n" ] } @@ -1125,18 +1102,18 @@ "data": { "text/html": [ "
\n", - "\n", "
412110.00e+006.25e-01fission
512110.00e+006.25e-01nu-fission
612116.25e-012.00e+07fission
712116.25e-012.00e+07nu-fission
813110.00e+006.25e-01fission
913110.00e+006.25e-01nu-fission
1013116.25e-012.00e+07fission
1113116.25e-012.00e+07nu-fission
1214110.00e+006.25e-01fission
1314110.00e+006.25e-01nu-fission
1414116.25e-012.00e+07fission
1514116.25e-012.00e+07nu-fission
1615110.00e+006.25e-01fission
1715110.00e+006.25e-01nu-fission
1815116.25e-012.00e+07fission
1915116.25e-012.00e+07nu-fission
\n", " \n", @@ -1152,172 +1129,28 @@ " \n", " \n", " \n", - " \n", + " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", " \n", - " \n", + " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", " \n", " \n", "
0100001U235scatter-Y0,03.86e-026.85e-04scatter3.81e-021.65e-04
110000U235scatter-Y1,-16.95e-043.15e-04
210000U235scatter-Y1,0-1.06e-043.79e-04
310000U235scatter-Y1,1-3.63e-043.18e-04
410000U235scatter-Y2,-21.20e-041.59e-04
510000U235scatter-Y2,-13.93e-051.86e-04
610000U235scatter-Y2,01.81e-041.85e-04
710000U235scatter-Y2,11.24e-041.81e-04
810000U235scatter-Y2,22.06e-042.26e-04
9100001U238scatter-Y0,0scatter2.33e+001.10e-02
1010000U238scatter-Y1,-12.90e-022.33e-03
1110000U238scatter-Y1,03.45e-032.38e-03
1210000U238scatter-Y1,1-2.72e-022.76e-03
1310000U238scatter-Y2,-2-2.02e-031.44e-03
1410000U238scatter-Y2,-18.07e-061.49e-03
1510000U238scatter-Y2,0-3.74e-071.79e-03
1610000U238scatter-Y2,16.54e-041.49e-03
1710000U238scatter-Y2,2-1.93e-031.36e-039.59e-03
\n", "
" ], "text/plain": [ - " cell nuclide score mean std. dev.\n", - "0 10000 U235 scatter-Y0,0 3.86e-02 6.85e-04\n", - "1 10000 U235 scatter-Y1,-1 6.95e-04 3.15e-04\n", - "2 10000 U235 scatter-Y1,0 -1.06e-04 3.79e-04\n", - "3 10000 U235 scatter-Y1,1 -3.63e-04 3.18e-04\n", - "4 10000 U235 scatter-Y2,-2 1.20e-04 1.59e-04\n", - "5 10000 U235 scatter-Y2,-1 3.93e-05 1.86e-04\n", - "6 10000 U235 scatter-Y2,0 1.81e-04 1.85e-04\n", - "7 10000 U235 scatter-Y2,1 1.24e-04 1.81e-04\n", - "8 10000 U235 scatter-Y2,2 2.06e-04 2.26e-04\n", - "9 10000 U238 scatter-Y0,0 2.33e+00 1.10e-02\n", - "10 10000 U238 scatter-Y1,-1 2.90e-02 2.33e-03\n", - "11 10000 U238 scatter-Y1,0 3.45e-03 2.38e-03\n", - "12 10000 U238 scatter-Y1,1 -2.72e-02 2.76e-03\n", - "13 10000 U238 scatter-Y2,-2 -2.02e-03 1.44e-03\n", - "14 10000 U238 scatter-Y2,-1 8.07e-06 1.49e-03\n", - "15 10000 U238 scatter-Y2,0 -3.74e-07 1.79e-03\n", - "16 10000 U238 scatter-Y2,1 6.54e-04 1.49e-03\n", - "17 10000 U238 scatter-Y2,2 -1.93e-03 1.36e-03" + " cell nuclide score mean std. dev.\n", + "0 1 U235 scatter 3.81e-02 1.65e-04\n", + "1 1 U238 scatter 2.33e+00 9.59e-03" ] }, "execution_count": 26, @@ -1349,15 +1182,14 @@ "name": "stdout", "output_type": "stream", "text": [ - "[[[ 0.00136183 0.01104314]\n", - " [ 0.00022601 0.00068479]]]\n" + "[[[0.00958717]\n", + " [0.00016469]]]\n" ] } ], "source": [ - "# Get the standard deviations for two of the spherical harmonic\n", - "# scattering reaction rates \n", - "data = tally.get_values(scores=['scatter-Y2,2', 'scatter-Y0,0'], \n", + "# Get the standard deviations the total scattering rate\n", + "data = tally.get_values(scores=['scatter'], \n", " nuclides=['U238', 'U235'], value='std_dev')\n", "print(data)" ] @@ -1379,7 +1211,7 @@ "output_type": "stream", "text": [ "Tally\n", - "\tID =\t10002\n", + "\tID =\t3\n", "\tName =\tdistribcell tally\n", "\tFilters =\tDistribcellFilter\n", "\tNuclides =\ttotal \n", @@ -1413,25 +1245,25 @@ "name": "stdout", "output_type": "stream", "text": [ - "[[[ 0.03500496]]\n", + "[[[0.03500496]]\n", "\n", - " [[ 0.03004793]]\n", + " [[0.02745568]]\n", "\n", - " [[ 0.02536586]]\n", + " [[0.02988488]]\n", "\n", - " [[ 0.03403647]]\n", + " [[0.04474905]]\n", "\n", - " [[ 0.02498 ]]\n", + " [[0.03697764]]\n", "\n", - " [[ 0.01892844]]\n", + " [[0.0409214 ]]\n", "\n", - " [[ 0.02662923]]\n", + " [[0.03366461]]\n", "\n", - " [[ 0.02875671]]\n", + " [[0.03210393]]\n", "\n", - " [[ 0.01945598]]\n", + " [[0.03216398]]\n", "\n", - " [[ 0.02612378]]]\n" + " [[0.04003553]]]\n" ] } ], @@ -1459,18 +1291,18 @@ "data": { "text/html": [ "
\n", - "\n", "\n", " \n", @@ -1514,265 +1346,265 @@ " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", @@ -1780,13 +1612,13 @@ " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", @@ -1797,51 +1629,51 @@ "" ], "text/plain": [ - " level 1 level 2 level 3 distribcell score \\\n", - " univ cell lat univ cell \n", - " id id id x y id id \n", - "558 10002 10003 10001 16 7 10000 10002 279 absorption \n", - "559 10002 10003 10001 16 7 10000 10002 279 scatter \n", - "560 10002 10003 10001 16 8 10000 10002 280 absorption \n", - "561 10002 10003 10001 16 8 10000 10002 280 scatter \n", - "562 10002 10003 10001 16 9 10000 10002 281 absorption \n", - "563 10002 10003 10001 16 9 10000 10002 281 scatter \n", - "564 10002 10003 10001 16 10 10000 10002 282 absorption \n", - "565 10002 10003 10001 16 10 10000 10002 282 scatter \n", - "566 10002 10003 10001 16 11 10000 10002 283 absorption \n", - "567 10002 10003 10001 16 11 10000 10002 283 scatter \n", - "568 10002 10003 10001 16 12 10000 10002 284 absorption \n", - "569 10002 10003 10001 16 12 10000 10002 284 scatter \n", - "570 10002 10003 10001 16 13 10000 10002 285 absorption \n", - "571 10002 10003 10001 16 13 10000 10002 285 scatter \n", - "572 10002 10003 10001 16 14 10000 10002 286 absorption \n", - "573 10002 10003 10001 16 14 10000 10002 286 scatter \n", - "574 10002 10003 10001 16 15 10000 10002 287 absorption \n", - "575 10002 10003 10001 16 15 10000 10002 287 scatter \n", - "576 10002 10003 10001 16 16 10000 10002 288 absorption \n", - "577 10002 10003 10001 16 16 10000 10002 288 scatter \n", + " level 1 level 2 level 3 distribcell score \\\n", + " univ cell lat univ cell \n", + " id id id x y id id \n", + "558 3 4 2 7 16 1 3 279 absorption \n", + "559 3 4 2 7 16 1 3 279 scatter \n", + "560 3 4 2 8 16 1 3 280 absorption \n", + "561 3 4 2 8 16 1 3 280 scatter \n", + "562 3 4 2 9 16 1 3 281 absorption \n", + "563 3 4 2 9 16 1 3 281 scatter \n", + "564 3 4 2 10 16 1 3 282 absorption \n", + "565 3 4 2 10 16 1 3 282 scatter \n", + "566 3 4 2 11 16 1 3 283 absorption \n", + "567 3 4 2 11 16 1 3 283 scatter \n", + "568 3 4 2 12 16 1 3 284 absorption \n", + "569 3 4 2 12 16 1 3 284 scatter \n", + "570 3 4 2 13 16 1 3 285 absorption \n", + "571 3 4 2 13 16 1 3 285 scatter \n", + "572 3 4 2 14 16 1 3 286 absorption \n", + "573 3 4 2 14 16 1 3 286 scatter \n", + "574 3 4 2 15 16 1 3 287 absorption \n", + "575 3 4 2 15 16 1 3 287 scatter \n", + "576 3 4 2 16 16 1 3 288 absorption \n", + "577 3 4 2 16 16 1 3 288 scatter \n", "\n", " mean std. dev. \n", " \n", " \n", - "558 7.77e-05 7.87e-06 \n", - "559 1.28e-02 5.09e-04 \n", - "560 8.92e-05 7.32e-06 \n", - "561 1.37e-02 4.99e-04 \n", - "562 9.50e-05 7.80e-06 \n", - "563 1.49e-02 4.74e-04 \n", - "564 1.15e-04 1.00e-05 \n", - "565 1.58e-02 6.18e-04 \n", - "566 1.13e-04 1.01e-05 \n", - "567 1.75e-02 5.66e-04 \n", - "568 1.08e-04 9.66e-06 \n", - "569 1.73e-02 5.40e-04 \n", - "570 1.16e-04 1.44e-05 \n", - "571 1.70e-02 6.90e-04 \n", - "572 1.16e-04 1.02e-05 \n", - "573 1.77e-02 6.80e-04 \n", - "574 1.20e-04 1.36e-05 \n", - "575 1.80e-02 7.80e-04 \n", + "558 6.81e-04 2.84e-05 \n", + "559 8.82e-02 1.86e-03 \n", + "560 6.65e-04 3.46e-05 \n", + "561 8.37e-02 2.02e-03 \n", + "562 5.61e-04 2.91e-05 \n", + "563 7.52e-02 1.79e-03 \n", + "564 4.77e-04 2.33e-05 \n", + "565 6.68e-02 1.14e-03 \n", + "566 4.64e-04 2.05e-05 \n", + "567 6.20e-02 1.61e-03 \n", + "568 4.44e-04 2.93e-05 \n", + "569 5.47e-02 1.53e-03 \n", + "570 3.67e-04 2.63e-05 \n", + "571 4.68e-02 1.52e-03 \n", + "572 2.76e-04 1.75e-05 \n", + "573 3.81e-02 1.28e-03 \n", + "574 2.08e-04 1.69e-05 \n", + "575 2.85e-02 1.13e-03 \n", "576 1.32e-04 1.30e-05 \n", "577 1.86e-02 7.12e-04 " ] @@ -1868,18 +1700,18 @@ "data": { "text/html": [ "
\n", - "\n", "
55810002100031000116342710000100021613279absorption7.77e-057.87e-066.81e-042.84e-05
55910002100031000116342710000100021613279scatter1.28e-025.09e-048.82e-021.86e-03
56010002100031000116342810000100021613280absorption8.92e-057.32e-066.65e-043.46e-05
56110002100031000116342810000100021613280scatter1.37e-024.99e-048.37e-022.02e-03
56210002100031000116342910000100021613281absorption9.50e-057.80e-065.61e-042.91e-05
56310002100031000116342910000100021613281scatter1.49e-024.74e-047.52e-021.79e-03
564100021000310001163421010000100021613282absorption1.15e-041.00e-054.77e-042.33e-05
565100021000310001163421010000100021613282scatter1.58e-026.18e-046.68e-021.14e-03
566100021000310001163421110000100021613283absorption1.13e-041.01e-054.64e-042.05e-05
567100021000310001163421110000100021613283scatter1.75e-025.66e-046.20e-021.61e-03
568100021000310001163421210000100021613284absorption1.08e-049.66e-064.44e-042.93e-05
569100021000310001163421210000100021613284scatter1.73e-025.40e-045.47e-021.53e-03
570100021000310001163421310000100021613285absorption1.16e-041.44e-053.67e-042.63e-05
571100021000310001163421310000100021613285scatter1.70e-026.90e-044.68e-021.52e-03
572100021000310001163421410000100021613286absorption1.16e-041.02e-052.76e-041.75e-05
573100021000310001163421410000100021613286scatter1.77e-026.80e-043.81e-021.28e-03
574100021000310001163421510000100021613287absorption1.20e-041.36e-052.08e-041.69e-05
575100021000310001163421510000100021613287scatter1.80e-027.80e-042.85e-021.13e-03
5761000210003100013421616100001000213288absorption1.32e-04
5771000210003100013421616100001000213288scatter1.86e-02
\n", " \n", @@ -2058,7 +1890,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "/home/johnny/miniconda3/lib/python3.6/site-packages/ipykernel_launcher.py:4: SettingWithCopyWarning: \n", + "/home/jan/.local/lib/python3.6/site-packages/ipykernel_launcher.py:4: SettingWithCopyWarning: \n", "A value is trying to be set on a copy of a slice from a DataFrame.\n", "Try using .loc[row_indexer,col_indexer] = value instead\n", "\n", @@ -2069,7 +1901,7 @@ { "data": { "text/plain": [ - "" + "" ] }, "execution_count": 34, @@ -2078,12 +1910,14 @@ }, { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ - "" + "
" ] }, - "metadata": {}, + "metadata": { + "needs_background": "light" + }, "output_type": "display_data" } ], @@ -2105,7 +1939,7 @@ { "data": { "text/plain": [ - "" + "" ] }, "execution_count": 35, @@ -2114,12 +1948,14 @@ }, { "data": { - "image/png": "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\n1llnccIJJ/D444/nuLpo6IhARHLvHyPgswU1u8y9usLgUTucZMuWLZxyyilMnz6dAw88cIfT9urV\ni8WLF297fd5559GgQQMAjj/+eG677bbq11xLKAhEJDYKCwvp168fDz30EHfeeecOp93+Xi353DSk\nIBCR3NvJJ/dsSSQSjB8/noEDB3Lrrbdy/fXXVzrt22+/nbf/+Len7whEJFZ22203Jk2axLhx43jo\noYcqnObpp5/mpZde4pxzzslxddHQEYGIxE6zZs144YUXOProo7edFXTHHXcwduxYNm7cyCGHHMK0\nadO2jYP//o6gRYsWTJkyJZLas0FBICKxsWHDhm3P27Vrx7JlywAYNmwYI0eOrHS+6dOnZ7myaKlp\nSEQk5nREILWGLkATiYaOCEREYk5BICIScwoCEZGYUxCIiMScviwWkUhUdnJAVWVyUkGjRo3+6xTS\nRx55hNmzZ3P33Xdz7733sttuu3HhhRdWOO/06dMpKiqiX79+NVZzbZG1IDCzdsBfgVaAA/e7+51m\n1gx4CugILAe+6+5fZ6sOEZFMXHrppTscP336dBo1alQjQVBWVkZBQe35HJ7NpqEy4Bp37wL0BS43\nsy7ACGCqu3cCpoavRUQiNXLkSG6//XYA7rrrLrp06UK3bt04++yzWb58Offeey933HEHPXr04LXX\nXmP58uUce+yxdOvWjYEDB/LRRx8B8OGHH9K3b1+6du3KjTfeuO1mONOnT+eoo45i2LBhdOnSBYBT\nTjmFQw89lIMPPpj7779/Wy2NGjXiuuuu4+CDD+a4445j1qxZ9O/fn3333ZcJEybU+LZnLZLc/VPg\n0/D5ejNbBLQBTgb6h5M9CkwHfpGtOkREttq0aRM9evTY9vqrr75i2LBh35pu1KhRLFu2jHr16rFm\nzRqaNm3KpZdeSqNGjbbdvGbo0KEMHz6c4cOHM2bMGK688kqeffZZrrrqKq666irOOecc7r333v9a\n7ty5c3n33XfZZ599ABgzZgzNmjVj06ZN9OnTh9NPP53mzZuzceNGjj32WG677TZOPfVUbrzxRl5+\n+WUWLlzI8OHDK6y5OnLyZbGZdQR6Am8CrcKQAPiMoOlIRCTrGjRowDvvvLPtcfPNN1c4Xbdu3Tjv\nvPMYO3ZspU04M2fO5NxzzwXgggsuYMaMGduGn3nmmQDbxm912GGHbQsBCI48unfvTt++fVm5ciVL\n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\n", + "image/png": "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B4sla9oOn1zu8BUjTs1xJSJfN2XKFK666ipeeOEF2rRpA8A111zDddddB8BTTz3F8ccfz4IFC2jWLOihYezYsfTo0SOymrNJewQiEiuvvvoql1xyCZMmTeLgg79xW2wAzjnnHE466SSeeOKJHFcXDe0RiEju/XM4fLKgepe5fycYOGKXk2zbto3TTz+dadOmceihh+5y2u7du7NkyZIdr88//3zq1asHwIknnsidd95Z9ZprCAWBiMRGYWEhvXv35uGHH+aee+7Z5bQ736sln5uGFAQiknu7+eaeLYlEgvHjx9O/f3/uuOMObrzxxgqnffvtt/P2g39nOkYgIrGy1157MXnyZMaOHcvDDz9c7jRPP/00L774IkOHDs1xddHQHoGIxE7jxo15/vnnOe6443acFXT33XczZswYNm/ezBFHHMHUqVN3jIOvHyNo2rQpL7/8ciS1Z4OCQERiY9OmTTuet2rViuXLlwMwePBgbrnllgrnmzZtWpYri5aahkREYk57BFJj6AI0kWhoj0BEJOYUBCIiMacgEBGJOQWBiEjM6WCxiESiopMDKiuTkwoaNGjwtVNIH330UWbNmsV9993HqFGj2GuvvbjooovKnXfatGkUFRXRu3fvaqu5pshaEJhZK+CvQHPAgQfd/R4zaww8BbQFVgDfdfcvs1WHiEgmLrvssl2OnzZtGg0aNKiWICgtLaWgoOZ8D89m01ApcK27dwR6AVeYWUdgODDF3dsDU8LXIiKRuuWWW7jrrrsAuPfee+nYsSOdO3fm3HPPZcWKFYwaNYq7776brl278tprr7FixQqOP/54OnfuTP/+/fnggw8AeP/99+nVqxedOnXi5ptv3nEznGnTptGnTx8GDx5Mx44dATj99NM58sgjOfzww3nwwQd31NKgQQOuv/56Dj/8cE444QRmzpxJ3759adeuHRMnTqz2dc9aJLn7x8DH4fONZrYYOBA4DegbTvYYMA34RbbqEBHZbsuWLXTt2nXH6y+++ILBgwd/Y7oRI0awfPly6tSpw7p169hnn3247LLLaNCgwY6b1wwaNIhhw4YxbNgwRo8ezVVXXcUzzzzD1VdfzdVXX83QoUMZNWrU15Y7Z84cFi5cyEEHHQTA6NGjady4MVu2bKFnz56cddZZNGnShM2bN3P88cdz5513csYZZ3DzzTfz0ksvsWjRIoYN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\n", "text/plain": [ - "" + "
" ] }, - "metadata": {}, + "metadata": { + "needs_background": "light" + }, "output_type": "display_data" } ], @@ -2150,7 +1986,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.0" + "version": "3.6.6" } }, "nbformat": 4, diff --git a/include/openmc/capi.h b/include/openmc/capi.h index c48b212bd..05512b379 100644 --- a/include/openmc/capi.h +++ b/include/openmc/capi.h @@ -3,6 +3,7 @@ #include #include +#include #ifdef __cplusplus extern "C" { @@ -48,9 +49,9 @@ extern "C" { int64_t openmc_get_seed(); int openmc_get_tally_index(int32_t id, int32_t* index); void openmc_get_tally_next_id(int32_t* id); + int openmc_global_tallies(double** ptr); int openmc_hard_reset(); int openmc_init(int argc, char* argv[], const void* intracomm); - int openmc_init_f(const int* intracomm); int openmc_legendre_filter_get_order(int32_t index, int* order); int openmc_legendre_filter_set_order(int32_t index, int order); int openmc_load_nuclide(const char name[]); @@ -105,7 +106,7 @@ extern "C" { int openmc_tally_get_scores(int32_t index, int** scores, int* n); int openmc_tally_get_type(int32_t index, int32_t* type); int openmc_tally_reset(int32_t index); - int openmc_tally_results(int32_t index, double** ptr, int shape_[3]); + int openmc_tally_results(int32_t index, double** ptr, size_t shape_[3]); int openmc_tally_set_active(int32_t index, bool active); int openmc_tally_set_estimator(int32_t index, const char* estimator); int openmc_tally_set_filters(int32_t index, int n, const int32_t* indices); @@ -134,8 +135,6 @@ extern "C" { // Global variables extern char openmc_err_msg[256]; - extern double openmc_keff; - extern double openmc_keff_std; extern int32_t n_cells; extern int32_t n_filters; extern int32_t n_lattices; @@ -148,15 +147,12 @@ extern "C" { extern int32_t n_surfaces; extern int32_t n_tallies; extern int32_t n_universes; - extern bool openmc_simulation_initialized; // Variables that are shared by necessity (can be removed from public header // later) extern bool openmc_master; extern int openmc_n_procs; - extern int openmc_n_threads; extern int openmc_rank; - extern int64_t openmc_work; #ifdef __cplusplus } diff --git a/include/openmc/constants.h b/include/openmc/constants.h index 042dd9668..d249e4cb5 100644 --- a/include/openmc/constants.h +++ b/include/openmc/constants.h @@ -315,9 +315,9 @@ constexpr int MG_GET_XS_CHI_DELAYED {14}; // TALLY-RELATED CONSTANTS // Tally result entries -constexpr int RESULT_VALUE {1}; -constexpr int RESULT_SUM {2}; -constexpr int RESULT_SUM_SQ {3}; +constexpr int RESULT_VALUE {0}; +constexpr int RESULT_SUM {1}; +constexpr int RESULT_SUM_SQ {2}; // Tally type // TODO: Convert to enum @@ -408,10 +408,11 @@ constexpr int RELATIVE_ERROR {2}; constexpr int STANDARD_DEVIATION {3}; // Global tally parameters -constexpr int K_COLLISION {1}; -constexpr int K_ABSORPTION {2}; -constexpr int K_TRACKLENGTH {3}; -constexpr int LEAKAGE {4}; +constexpr int N_GLOBAL_TALLIES {4}; +constexpr int K_COLLISION {0}; +constexpr int K_ABSORPTION {1}; +constexpr int K_TRACKLENGTH {2}; +constexpr int LEAKAGE {3}; // Differential tally independent variables constexpr int DIFF_DENSITY {1}; diff --git a/include/openmc/eigenvalue.h b/include/openmc/eigenvalue.h index cc879461e..cbf0baa2d 100644 --- a/include/openmc/eigenvalue.h +++ b/include/openmc/eigenvalue.h @@ -14,6 +14,7 @@ namespace openmc { // Global variables //============================================================================== +extern double keff_generation; //!< Single-generation k on each processor extern std::vector entropy; //!< Shannon entropy at each generation extern xt::xtensor source_frac; //!< Source fraction for UFS @@ -24,6 +25,12 @@ extern "C" int64_t n_bank; // Non-member functions //============================================================================== +//! Collect/normalize the tracklength keff from each process +extern "C" void calculate_generation_keff(); + +//! Sample/redistribute source sites from accumulated fission sites +extern "C" void synchronize_bank(); + //! Calculates the Shannon entropy of the fission source distribution to assess //! source convergence extern "C" void shannon_entropy(); diff --git a/include/openmc/error.h b/include/openmc/error.h index 17b2f392e..b02c0a9f8 100644 --- a/include/openmc/error.h +++ b/include/openmc/error.h @@ -9,7 +9,6 @@ namespace openmc { - extern "C" void fatal_error_from_c(const char* message, int message_len); extern "C" void warning_from_c(const char* message, int message_len); extern "C" void write_message_from_c(const char* message, int message_len, @@ -81,5 +80,9 @@ void write_message(const std::stringstream& message, int level) write_message(message.str(), level); } +#ifdef OPENMC_MPI +extern "C" void abort_mpi(int code); +#endif + } // namespace openmc #endif // OPENMC_ERROR_H diff --git a/include/openmc/finalize.h b/include/openmc/finalize.h index 58cf75606..5a8238841 100644 --- a/include/openmc/finalize.h +++ b/include/openmc/finalize.h @@ -1,6 +1,10 @@ #ifndef OPENMC_FINALIZE_H #define OPENMC_FINALIZE_H +namespace openmc { + extern "C" void openmc_free_bank(); +} // namespace openmc + #endif // OPENMC_FINALIZE_H diff --git a/include/openmc/hdf5_interface.h b/include/openmc/hdf5_interface.h index d3cc558ba..7fa5370c7 100644 --- a/include/openmc/hdf5_interface.h +++ b/include/openmc/hdf5_interface.h @@ -8,6 +8,7 @@ #include // for strlen #include #include +#include #include #include "hdf5.h" @@ -333,7 +334,9 @@ write_attribute(hid_t obj_id, const char* name, const std::vector& buffer) // Templates/overloads for write_dataset //============================================================================== -template inline void +// Template for scalars (ensured by SFINAE) +template inline +std::enable_if_t>::value> write_dataset(hid_t obj_id, const char* name, T buffer) { write_dataset(obj_id, 0, nullptr, name, H5TypeMap::type_id, &buffer, false); @@ -359,9 +362,11 @@ write_dataset(hid_t obj_id, const char* name, const std::vector& buffer) write_dataset(obj_id, 1, dims, name, H5TypeMap::type_id, buffer.data(), false); } -template inline void -write_dataset(hid_t obj_id, const char* name, const xt::xarray& arr) +// Template for xarray, xtensor, etc. +template inline void +write_dataset(hid_t obj_id, const char* name, const xt::xcontainer& arr) { + using T = typename D::value_type; auto s = arr.shape(); std::vector dims {s.cbegin(), s.cend()}; write_dataset(obj_id, dims.size(), dims.data(), name, H5TypeMap::type_id, diff --git a/include/openmc/material.h b/include/openmc/material.h index 69647dec3..b6d7e19cd 100644 --- a/include/openmc/material.h +++ b/include/openmc/material.h @@ -26,6 +26,7 @@ class Material public: int32_t id_; //!< Unique ID double volume_ {-1.0}; //!< Volume in [cm^3] + bool fissionable {false}; //!< Does this material contain fissionable nuclides //! \brief Default temperature for cells containing this material. //! diff --git a/include/openmc/math_functions.h b/include/openmc/math_functions.h index a3c9c07cf..8bfa11899 100644 --- a/include/openmc/math_functions.h +++ b/include/openmc/math_functions.h @@ -22,7 +22,7 @@ namespace openmc { //! @return The requested percentile //============================================================================== -extern "C" double normal_percentile_c(double p); +extern "C" double normal_percentile(double p); //============================================================================== //! Calculate the percentile of the Student's t distribution with a specified @@ -58,7 +58,7 @@ extern "C" void calc_pn_c(int n, double x, double pnx[]); //! evaluated at x //============================================================================== -extern "C" double evaluate_legendre_c(int n, const double data[], double x); +extern "C" double evaluate_legendre(int n, const double data[], double x); //============================================================================== //! Calculate the n-th order real spherical harmonics for a given angle (in @@ -144,7 +144,7 @@ Direction rotate_angle(Direction u, double mu, double* phi); //! @result The sampled outgoing energy //============================================================================== -extern "C" double maxwell_spectrum_c(double T); +extern "C" double maxwell_spectrum(double T); //============================================================================== //! Samples an energy from a Watt energy-dependent fission distribution. @@ -159,7 +159,7 @@ extern "C" double maxwell_spectrum_c(double T); //! @result The sampled outgoing energy //============================================================================== -extern "C" double watt_spectrum_c(double a, double b); +extern "C" double watt_spectrum(double a, double b); //============================================================================== //! Doppler broadens the windowed multipole curvefit. diff --git a/include/openmc/message_passing.h b/include/openmc/message_passing.h index c910210db..c730674ab 100644 --- a/include/openmc/message_passing.h +++ b/include/openmc/message_passing.h @@ -2,7 +2,7 @@ #define OPENMC_MESSAGE_PASSING_H #ifdef OPENMC_MPI -#include "mpi.h" +#include #endif namespace openmc { diff --git a/include/openmc/mgxs_interface.h b/include/openmc/mgxs_interface.h index 02a43fece..e0addf5bc 100644 --- a/include/openmc/mgxs_interface.h +++ b/include/openmc/mgxs_interface.h @@ -18,6 +18,9 @@ extern std::vector nuclides_MG; extern std::vector macro_xs; extern "C" int num_energy_groups; +//TODO: When more of the Fortran is converted (input_xml, tallies, etc, also +// bring over energy_bin_avg, energy_bins, etc, as vectors) + //============================================================================== // Mgxs data loading interface methods //============================================================================== @@ -44,13 +47,6 @@ extern "C" void calculate_xs_c(int i_mat, int gin, double sqrtkT, const double uvw[3], double& total_xs, double& abs_xs, double& nu_fiss_xs); -extern "C" void -sample_scatter_c(int i_mat, int gin, int& gout, double& mu, double& wgt, - double uvw[3]); - -extern "C" void -sample_fission_energy_c(int i_mat, int gin, int& dg, int& gout); - extern "C" double get_nuclide_xs_c(int index, int xstype, int gin, int* gout, double* mu, int* dg); diff --git a/include/openmc/physics_common.h b/include/openmc/physics_common.h new file mode 100644 index 000000000..33fb8ed61 --- /dev/null +++ b/include/openmc/physics_common.h @@ -0,0 +1,16 @@ +//! \file physics_common.h +//! A collection of physics methods common to MG, CE, photon, etc. + +#ifndef OPENMC_PHYSICS_COMMON_H +#define OPENMC_PHYSICS_COMMON_H + +#include "openmc/particle.h" + +namespace openmc { + +//! \brief Performs the russian roulette operation for a particle +extern "C" void +russian_roulette(Particle* p); + +} // namespace openmc +#endif // OPENMC_PHYSICS_COMMON_H diff --git a/include/openmc/physics_mg.h b/include/openmc/physics_mg.h new file mode 100644 index 000000000..14b7e6cbd --- /dev/null +++ b/include/openmc/physics_mg.h @@ -0,0 +1,60 @@ +//! \file physics_mg.h +//! Methods needed to perform the collision physics for multi-group mode + +#ifndef OPENMC_PHYSICS_MG_H +#define OPENMC_PHYSICS_MG_H + +#include "openmc/capi.h" +#include "openmc/particle.h" +#include "openmc/nuclide.h" + +namespace openmc { + +//TODO: Remove energy_bin_avg and material_xs parameters when they reside on +// the C-side this should happen after materials, physics, input, and tallies +// are brought over + +//! \brief samples particle behavior after a collision event. +//! \param p Particle to operate on +//! \param energy_bin_avg Average energy within each energy bin +//! \param material_xs The cross section cache for the current material +extern "C" void +collision_mg(Particle* p, const double* energy_bin_avg, + const MaterialMacroXS* material_xs); + +//! \brief samples a reaction type. +//! +//! Note that there is special logic when suvival biasing is turned on since +//! fission and disappearance are treated implicitly. +//! \param p Particle to operate on +//! \param energy_bin_avg Average energy within each energy bin +//! \param material_xs The cross section cache for the current material +void +sample_reaction(Particle* p, const double* energy_bin_avg, + const MaterialMacroXS* material_xs); + +//! \brief Samples the scattering event +//! \param p Particle to operate on +//! \param energy_bin_avg Average energy within each energy bin +void +scatter(Particle* p, const double* energy_bin_avg); + +//! \brief Determines the average total, prompt and delayed neutrons produced +//! from fission and creates the appropriate bank sites. +//! \param p Particle to operate on +//! \param bank_array The particle bank to populate +//! \param size_bank Number of particles currently in the bank +//! \param bank_array_size Allocated size of the bank +//! \param material_xs The cross section cache for the current material +void +create_fission_sites(Particle* p, Bank* bank_array, int64_t* size_bank, + int64_t bank_array_size, const MaterialMacroXS* material_xs); + +//! \brief Handles an absorption event +//! \param p Particle to operate on +//! \param material_xs The cross section cache for the current material +void +absorption(Particle* p, const MaterialMacroXS* material_xs); + +} // namespace openmc +#endif // OPENMC_PHYSICS_MG_H diff --git a/include/openmc/search.h b/include/openmc/search.h index c91ad18fe..446e2916a 100644 --- a/include/openmc/search.h +++ b/include/openmc/search.h @@ -4,7 +4,7 @@ #ifndef OPENMC_SEARCH_H #define OPENMC_SEARCH_H -#include // for lower_bound +#include // for lower_bound, upper_bound namespace openmc { @@ -19,6 +19,14 @@ lower_bound_index(It first, It last, const T& value) return (index == last) ? -1 : index - first; } +template +typename std::iterator_traits::difference_type +upper_bound_index(It first, It last, const T& value) +{ + It index = std::upper_bound(first, last, value) - 1; + return (index == last) ? -1 : index - first; +} + } // namespace openmc #endif // OPENMC_SEARCH_H diff --git a/include/openmc/simulation.h b/include/openmc/simulation.h index 9c0779072..e21ea05d5 100644 --- a/include/openmc/simulation.h +++ b/include/openmc/simulation.h @@ -10,29 +10,62 @@ namespace openmc { //============================================================================== -// Global variables +// Global variable declarations //============================================================================== -extern "C" int openmc_current_batch; -extern "C" int openmc_current_gen; -extern "C" int64_t openmc_current_work; -extern "C" int openmc_n_lost_particles; -extern "C" int openmc_total_gen; -extern "C" bool openmc_trace; +namespace simulation { +extern "C" int current_batch; //!< current batch +extern "C" int current_gen; //!< current fission generation +extern "C" int64_t current_work; //!< index in source back of current particle +extern "C" double keff; //!< average k over batches +extern "C" double keff_std; //!< standard deviation of average k +extern "C" double k_col_abs; //!< sum over batches of k_collision * k_absorption +extern "C" double k_col_tra; //!< sum over batches of k_collision * k_tracklength +extern "C" double k_abs_tra; //!< sum over batches of k_absorption * k_tracklength +extern "C" double log_spacing; //!< lethargy spacing for energy grid searches +extern "C" int n_lost_particles; //!< cumulative number of lost particles +extern "C" bool need_depletion_rx; //!< need to calculate depletion rx? +extern "C" int restart_batch; //!< batch at which a restart job resumed +extern "C" bool satisfy_triggers; //!< have tally triggers been satisfied? +extern "C" bool simulation_initialized; //!< has simulation been initialized? +extern "C" int total_gen; //!< total number of generations simulated +extern "C" int64_t work; //!< number of particles per process + +extern std::vector k_generation; extern std::vector work_index; -#pragma omp threadprivate(openmc_current_work, openmc_trace) +// Threadprivate variables +extern "C" bool trace; //!< flag to show debug information +#ifdef _OPENMP +extern "C" int n_threads; //!< number of OpenMP threads +extern "C" int thread_id; //!< ID of a given thread +#endif + +#pragma omp threadprivate(current_work, thread_id, trace) + +} // namespace simulation //============================================================================== // Functions //============================================================================== +//! Determine number of particles to transport per process +void calculate_work(); + //! Initialize simulation extern "C" void openmc_simulation_init_c(); -//! Determine number of particles to transport per process -void calculate_work(); +//! Initialize a fission generation +extern "C" void initialize_generation(); + +//! Determine overall generation number +extern "C" int overall_generation(); + +#ifdef OPENMC_MPI +extern "C" void broadcast_results(); +extern "C" void broadcast_triggers(); +#endif } // namespace openmc diff --git a/include/openmc/state_point.h b/include/openmc/state_point.h index 64713ae96..058edc09c 100644 --- a/include/openmc/state_point.h +++ b/include/openmc/state_point.h @@ -9,10 +9,10 @@ namespace openmc { -extern "C" void write_source_bank(hid_t group_id, int64_t* work_index, - Bank* source_bank); -extern "C" void read_source_bank(hid_t group_id, int64_t* work_index, - Bank* source_bank); +extern "C" void write_source_bank(hid_t group_id, Bank* source_bank); +extern "C" void read_source_bank(hid_t group_id, Bank* source_bank); + +extern "C" void write_tally_results_nr(hid_t file_id); } // namespace openmc #endif // OPENMC_STATE_POINT_H diff --git a/include/openmc/tallies/tally.h b/include/openmc/tallies/tally.h new file mode 100644 index 000000000..c323d3bac --- /dev/null +++ b/include/openmc/tallies/tally.h @@ -0,0 +1,46 @@ +#ifndef OPENMC_TALLIES_TALLY_H +#define OPENMC_TALLIES_TALLY_H + +#include "openmc/constants.h" + +#include "xtensor/xtensor.hpp" + +namespace openmc { + +//============================================================================== +// Global variable declarations +//============================================================================== + +extern "C" double total_weight; + +// Threadprivate variables + +extern "C" double global_tally_absorption; +#pragma omp threadprivate(global_tally_absorption) + +//============================================================================== +// Non-member functions +//============================================================================== + +// Alias for the type returned by xt::adapt(...). N is the dimension of the +// multidimensional array +template +using adaptor_type = xt::xtensor_adaptor, N>; + +//! Get the global tallies as a multidimensional array +//! \return Global tallies array +adaptor_type<2> global_tallies(); + +//! Get tally results as a multidimensional array +//! \param idx Index in tallies array +//! \return Tally results array +adaptor_type<3> tally_results(int idx); + +#ifdef OPENMC_MPI +//! Collect all tally results onto master process +extern "C" void reduce_tally_results(); +#endif + +} // namespace openmc + +#endif // OPENMC_TALLIES_TALLY_H diff --git a/include/openmc/timer.h b/include/openmc/timer.h new file mode 100644 index 000000000..11dfd1adf --- /dev/null +++ b/include/openmc/timer.h @@ -0,0 +1,47 @@ +#ifndef OPENMC_TIMER_H +#define OPENMC_TIMER_H + +#include + +namespace openmc { + +//============================================================================== +//! Class for measuring time elapsed +//============================================================================== + +class Timer { +public: + using clock = std::chrono::high_resolution_clock; + + Timer() {}; + + //! Start running the timer + void start (); + + //! Get total elapsed time in seconds + //! \return Elapsed time in [s] + double elapsed(); + + //! Stop running the timer + void stop(); + + //! Stop the timer and reset its elapsed time + void reset(); + +private: + bool running_ {false}; //!< is timer running? + std::chrono::time_point start_; //!< starting point for clock + double elapsed_; //!< elasped time in [s] +}; + +//============================================================================== +// Global variables +//============================================================================== + +extern Timer time_bank; +extern Timer time_bank_sample; +extern Timer time_bank_sendrecv; + +} // namespace openmc + +#endif // OPENMC_TIMER_H diff --git a/openmc/capi/core.py b/openmc/capi/core.py index fa6517488..5be31ce86 100644 --- a/openmc/capi/core.py +++ b/openmc/capi/core.py @@ -71,7 +71,7 @@ def current_batch(): Current batch of the simulation """ - return c_int.in_dll(_dll, 'openmc_current_batch').value + return c_int.in_dll(_dll, 'current_batch').value def finalize(): @@ -220,8 +220,8 @@ def keff(): return tuple(k) else: # Otherwise, return the tracklength estimator - mean = c_double.in_dll(_dll, 'openmc_keff').value - std_dev = c_double.in_dll(_dll, 'openmc_keff_std').value \ + mean = c_double.in_dll(_dll, 'keff').value + std_dev = c_double.in_dll(_dll, 'keff_std').value \ if n > 1 else np.inf return (mean, std_dev) diff --git a/openmc/capi/math.py b/openmc/capi/math.py index 390ee466f..a8ea849b3 100644 --- a/openmc/capi/math.py +++ b/openmc/capi/math.py @@ -12,8 +12,8 @@ _dll.t_percentile_c.argtypes = [c_double, c_int] _dll.calc_pn_c.restype = None _dll.calc_pn_c.argtypes = [c_int, c_double, ndpointer(c_double)] -_dll.evaluate_legendre_c.restype = c_double -_dll.evaluate_legendre_c.argtypes = [c_int, POINTER(c_double), c_double] +_dll.evaluate_legendre.restype = c_double +_dll.evaluate_legendre.argtypes = [c_int, POINTER(c_double), c_double] _dll.calc_rn_c.restype = None _dll.calc_rn_c.argtypes = [c_int, ndpointer(c_double), ndpointer(c_double)] @@ -27,11 +27,11 @@ _dll.calc_zn_rad_c.argtypes = [c_int, c_double, ndpointer(c_double)] _dll.rotate_angle_c.restype = None _dll.rotate_angle_c.argtypes = [ndpointer(c_double), c_double, POINTER(c_double)] -_dll.maxwell_spectrum_c.restype = c_double -_dll.maxwell_spectrum_c.argtypes = [c_double] +_dll.maxwell_spectrum.restype = c_double +_dll.maxwell_spectrum.argtypes = [c_double] -_dll.watt_spectrum_c.restype = c_double -_dll.watt_spectrum_c.argtypes = [c_double, c_double] +_dll.watt_spectrum.restype = c_double +_dll.watt_spectrum.argtypes = [c_double, c_double] _dll.broaden_wmp_polynomials_c.restype = None _dll.broaden_wmp_polynomials_c.argtypes = [c_double, c_double, c_int, @@ -100,9 +100,8 @@ def evaluate_legendre(data, x): """ data_arr = np.array(data, dtype=np.float64) - return _dll.evaluate_legendre_c(len(data), - data_arr.ctypes.data_as(POINTER(c_double)), - x) + return _dll.evaluate_legendre(len(data), + data_arr.ctypes.data_as(POINTER(c_double)), x) def calc_rn(n, uvw): @@ -182,7 +181,7 @@ def calc_zn_rad(n, rho): zn_rad = np.zeros(num_bins, dtype=np.float64) _dll.calc_zn_rad_c(n, rho, zn_rad) return zn_rad - + def rotate_angle(uvw0, mu, phi=None): """ Rotates direction cosines through a polar angle whose cosine is @@ -231,7 +230,7 @@ def maxwell_spectrum(T): """ - return _dll.maxwell_spectrum_c(T) + return _dll.maxwell_spectrum(T) def watt_spectrum(a, b): @@ -251,7 +250,7 @@ def watt_spectrum(a, b): """ - return _dll.watt_spectrum_c(a, b) + return _dll.watt_spectrum(a, b) def broaden_wmp_polynomials(E, dopp, n): diff --git a/openmc/capi/tally.py b/openmc/capi/tally.py index 44bf89415..a414ead45 100644 --- a/openmc/capi/tally.py +++ b/openmc/capi/tally.py @@ -1,5 +1,5 @@ from collections.abc import Mapping -from ctypes import c_int, c_int32, c_double, c_char_p, c_bool, POINTER +from ctypes import c_int, c_int32, c_size_t, c_double, c_char_p, c_bool, POINTER from weakref import WeakValueDictionary import numpy as np @@ -60,7 +60,7 @@ _dll.openmc_tally_reset.argtypes = [c_int32] _dll.openmc_tally_reset.restype = c_int _dll.openmc_tally_reset.errcheck = _error_handler _dll.openmc_tally_results.argtypes = [ - c_int32, POINTER(POINTER(c_double)), POINTER(c_int*3)] + c_int32, POINTER(POINTER(c_double)), POINTER(c_size_t*3)] _dll.openmc_tally_results.restype = c_int _dll.openmc_tally_results.errcheck = _error_handler _dll.openmc_tally_set_active.argtypes = [c_int32, c_bool] @@ -296,9 +296,9 @@ class Tally(_FortranObjectWithID): @property def results(self): data = POINTER(c_double)() - shape = (c_int*3)() + shape = (c_size_t*3)() _dll.openmc_tally_results(self._index, data, shape) - return as_array(data, tuple(shape[::-1])) + return as_array(data, tuple(shape)) @property def scores(self): diff --git a/openmc/mgxs/mgxs.py b/openmc/mgxs/mgxs.py index 172d7ddb3..b077d04d1 100644 --- a/openmc/mgxs/mgxs.py +++ b/openmc/mgxs/mgxs.py @@ -2774,7 +2774,7 @@ class TransportXS(MGXS): p1_tally = p1_tally.get_slice(filters=[openmc.LegendreFilter], filter_bins=[('P1',)], squeeze=True) - p1_tally.scores = ['scatter-1'] + p1_tally._scores = ['scatter-1'] # Compute total cross section total_xs = self.tallies['total'] / self.tallies['flux (tracklength)'] diff --git a/openmc/mgxs_library.py b/openmc/mgxs_library.py index e315f3314..51c91ef17 100644 --- a/openmc/mgxs_library.py +++ b/openmc/mgxs_library.py @@ -1840,10 +1840,10 @@ class XSdata(object): mu = np.linspace(-1, 1, xsdata.num_orders) # Evaluate the legendre on the mu grid for imu in range(len(mu)): - new_data[..., imu] = \ - np.sum((l + 0.5) * eval_legendre(l, mu[imu]) * - orig_data[..., l] - for l in range(self.num_orders)) + for l in range(self.num_orders): + new_data[..., imu] += ( + (l + 0.5) * eval_legendre(l, mu[imu]) * + orig_data[..., l]) elif target_format == 'histogram': # This code uses the vectorized integration capabilities @@ -1857,11 +1857,10 @@ class XSdata(object): mu_fine = np.linspace(mu[h_bin], mu[h_bin + 1], _NMU) table_fine = np.zeros(new_data.shape[:-1] + (_NMU,)) for imu in range(len(mu_fine)): - table_fine[..., imu] = \ - np.sum((l + 0.5) * - eval_legendre(l, mu_fine[imu]) * - orig_data[..., l] - for l in range(self.num_orders)) + for l in range(self.num_orders): + table_fine[..., imu] += ((l + 0.5) + * eval_legendre(l, mu_fine[imu]) * + orig_data[..., l]) new_data[..., h_bin] = simps(table_fine, mu_fine) elif self.scatter_format == 'tabular': diff --git a/openmc/tallies.py b/openmc/tallies.py index 1e4b142fe..5bc6494bb 100644 --- a/openmc/tallies.py +++ b/openmc/tallies.py @@ -376,7 +376,7 @@ class Tally(IDManagerMixin): def scores(self, scores): cv.check_type('tally scores', scores, MutableSequence) - for i, score in enumerate(scores[:-1]): + for i, score in enumerate(scores): # If the score is already in the Tally, raise an error if score in scores[i+1:]: msg = 'Unable to add a duplicate score "{0}" to Tally ID="{1}" ' \ @@ -390,7 +390,7 @@ class Tally(IDManagerMixin): for deprecated in ['scatter-', 'nu-scatter-', 'scatter-p', 'nu-scatter-p', 'scatter-y', 'nu-scatter-y', 'flux-y', 'total-y']: - if score.startswith(deprecated): + if score.strip().startswith(deprecated): msg = score.strip() + ' is no longer supported.' raise ValueError(msg) scores[i] = score.strip() diff --git a/src/api.F90 b/src/api.F90 index 38329ad80..18162e600 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -181,7 +181,6 @@ contains err = 0 #ifdef OPENMC_MPI ! Free all MPI types - call MPI_TYPE_FREE(MPI_BANK, err) call openmc_free_bank() #endif @@ -277,14 +276,12 @@ contains call time_initialize % reset() call time_read_xs % reset() call time_unionize % reset() - call time_bank % reset() - call time_bank_sample % reset() - call time_bank_sendrecv % reset() call time_tallies % reset() call time_inactive % reset() call time_active % reset() call time_transport % reset() call time_finalize % reset() + call reset_timers() err = 0 end function openmc_reset diff --git a/src/cmfd_execute.F90 b/src/cmfd_execute.F90 index 18f5ebec1..bcd7e9f5c 100644 --- a/src/cmfd_execute.F90 +++ b/src/cmfd_execute.F90 @@ -13,6 +13,16 @@ module cmfd_execute private public :: execute_cmfd, cmfd_init_batch, cmfd_tally_init +#ifdef OPENMC_MPI + interface + subroutine cmfd_broadcast(n, buffer) bind(C) + import C_DOUBLE, C_INT + integer(C_INT), value :: n + real(C_DOUBLE), intent(out) :: buffer + end subroutine + end interface +#endif + contains !============================================================================== @@ -105,9 +115,6 @@ contains real(8) :: hxyz(3) ! cell dimensions of current ijk cell real(8) :: vol ! volume of cell real(8),allocatable :: source(:,:,:,:) ! tmp source array for entropy -#ifdef OPENMC_MPI - integer :: mpi_err ! MPI error code -#endif ! Get maximum of spatial and group indices nx = cmfd % indices(1) @@ -199,7 +206,7 @@ contains #ifdef OPENMC_MPI ! Broadcast full source to all procs - call MPI_BCAST(cmfd % cmfd_src, n, MPI_REAL8, 0, mpi_intracomm, mpi_err) + call cmfd_broadcast(n, cmfd % cmfd_src(1,1,1,1)) #endif end subroutine calc_fission_source @@ -232,9 +239,6 @@ contains real(8) :: norm ! normalization factor logical(C_BOOL) :: outside ! any source sites outside mesh logical :: in_mesh ! source site is inside mesh -#ifdef OPENMC_MPI - integer :: mpi_err -#endif interface subroutine cmfd_populate_sourcecounts(ng, energies, source_counts, outside) bind(C) @@ -300,8 +304,7 @@ contains ! Broadcast weight factors to all procs #ifdef OPENMC_MPI - call MPI_BCAST(cmfd % weightfactors, ng*nx*ny*nz, MPI_REAL8, 0, & - mpi_intracomm, mpi_err) + call cmfd_broadcast(ng*nx*ny*nz, cmfd % weightfactors(1,1,1,1)) #endif end if diff --git a/src/cmfd_execute.cpp b/src/cmfd_execute.cpp index 740c1f51f..aa9907a5b 100644 --- a/src/cmfd_execute.cpp +++ b/src/cmfd_execute.cpp @@ -7,6 +7,8 @@ #include "openmc/capi.h" #include "openmc/mesh.h" +#include "openmc/message_passing.h" +#include "openmc/simulation.h" namespace openmc { @@ -23,10 +25,19 @@ cmfd_populate_sourcecounts(int n_energy, const double* energies, // Get source counts in each mesh bin / energy bin auto& m = meshes.at(index_cmfd_mesh); - xt::xarray counts = m->count_sites(openmc_work, source_bank, n_energy, energies, outside); + xt::xarray counts = m->count_sites(simulation::work, source_bank, n_energy, energies, outside); // Copy data from the xarray into the source counts array std::copy(counts.begin(), counts.end(), source_counts); } +#ifdef OPENMC_MPI +extern "C" void +cmfd_broadcast(int n, double* buffer) +{ + MPI_Bcast(buffer, n, MPI_DOUBLE, 0, mpi::intracomm); +} +#endif + + } // namespace openmc diff --git a/src/cmfd_header.F90 b/src/cmfd_header.F90 index e5e5cc423..036e193b0 100644 --- a/src/cmfd_header.F90 +++ b/src/cmfd_header.F90 @@ -51,8 +51,8 @@ module cmfd_header real(8), allocatable :: hxyz(:,:,:,:) ! Source distributions - real(8), allocatable :: cmfd_src(:,:,:,:) - real(8), allocatable :: openmc_src(:,:,:,:) + real(C_DOUBLE), allocatable :: cmfd_src(:,:,:,:) + real(C_DOUBLE), allocatable :: openmc_src(:,:,:,:) ! Source sites in each mesh box real(C_DOUBLE), allocatable :: sourcecounts(:,:) diff --git a/src/distribution.cpp b/src/distribution.cpp index 01b09e65d..e546b11dd 100644 --- a/src/distribution.cpp +++ b/src/distribution.cpp @@ -90,7 +90,7 @@ Maxwell::Maxwell(pugi::xml_node node) double Maxwell::sample() const { - return maxwell_spectrum_c(theta_); + return maxwell_spectrum(theta_); } //============================================================================== @@ -110,7 +110,7 @@ Watt::Watt(pugi::xml_node node) double Watt::sample() const { - return watt_spectrum_c(a_, b_); + return watt_spectrum(a_, b_); } //============================================================================== diff --git a/src/distribution_energy.cpp b/src/distribution_energy.cpp index 863ffd8b0..e311e322d 100644 --- a/src/distribution_energy.cpp +++ b/src/distribution_energy.cpp @@ -279,7 +279,7 @@ double MaxwellEnergy::sample(double E) const while (true) { // Sample maxwell fission spectrum - double E_out = maxwell_spectrum_c(theta); + double E_out = maxwell_spectrum(theta); // Accept energy based on restriction energy if (E_out <= E - u_) return E_out; @@ -343,7 +343,7 @@ double WattEnergy::sample(double E) const while (true) { // Sample energy-dependent Watt fission spectrum - double E_out = watt_spectrum_c(a, b); + double E_out = watt_spectrum(a, b); // Accept energy based on restriction energy if (E_out <= E - u_) return E_out; diff --git a/src/eigenvalue.F90 b/src/eigenvalue.F90 index bd2a20b41..92c731eae 100644 --- a/src/eigenvalue.F90 +++ b/src/eigenvalue.F90 @@ -16,313 +16,15 @@ module eigenvalue implicit none - real(8) :: keff_generation ! Single-generation k on each processor real(8) :: k_sum(2) = ZERO ! Used to reduce sum and sum_sq + interface + subroutine calculate_generation_keff() bind(C) + end subroutine + end interface + contains -!=============================================================================== -! SYNCHRONIZE_BANK samples source sites from the fission sites that were -! accumulated during the generation. This routine is what allows this Monte -! Carlo to scale to large numbers of processors where other codes cannot. -!=============================================================================== - - subroutine synchronize_bank() - - integer :: i ! loop indices - integer :: j ! loop indices - integer(8) :: start ! starting index in global bank - integer(8) :: finish ! ending index in global bank - integer(8) :: total ! total sites in global fission bank - integer(8) :: index_temp ! index in temporary source bank - integer(8) :: sites_needed ! # of sites to be sampled - real(8) :: p_sample ! probability of sampling a site - type(Bank), save, allocatable :: & - & temp_sites(:) ! local array of extra sites on each node - -#ifdef OPENMC_MPI - integer :: mpi_err ! MPI error code - integer(8) :: n ! number of sites to send/recv - integer :: neighbor ! processor to send/recv data from -#ifdef OPENMC_MPIF08 - type(MPI_Request) :: request(20) -#else - integer :: request(20) ! communication request for send/recving sites -#endif - integer :: n_request ! number of communication requests - integer(8) :: index_local ! index in local source bank - integer(8), save, allocatable :: & - & bank_position(:) ! starting positions in global source bank -#endif - - ! In order to properly understand the fission bank algorithm, you need to - ! think of the fission and source bank as being one global array divided - ! over multiple processors. At the start, each processor has a random amount - ! of fission bank sites -- each processor needs to know the total number of - ! sites in order to figure out the probability for selecting - ! sites. Furthermore, each proc also needs to know where in the 'global' - ! fission bank its own sites starts in order to ensure reproducibility by - ! skipping ahead to the proper seed. - -#ifdef OPENMC_MPI - start = 0_8 - call MPI_EXSCAN(n_bank, start, 1, MPI_INTEGER8, MPI_SUM, & - mpi_intracomm, mpi_err) - - ! While we would expect the value of start on rank 0 to be 0, the MPI - ! standard says that the receive buffer on rank 0 is undefined and not - ! significant - if (rank == 0) start = 0_8 - - finish = start + n_bank - total = finish - call MPI_BCAST(total, 1, MPI_INTEGER8, n_procs - 1, & - mpi_intracomm, mpi_err) - -#else - start = 0_8 - finish = n_bank - total = n_bank -#endif - - ! If there are not that many particles per generation, it's possible that no - ! fission sites were created at all on a single processor. Rather than add - ! extra logic to treat this circumstance, we really want to ensure the user - ! runs enough particles to avoid this in the first place. - - if (n_bank == 0) then - call fatal_error("No fission sites banked on processor " // to_str(rank)) - end if - - ! Make sure all processors start at the same point for random sampling. Then - ! skip ahead in the sequence using the starting index in the 'global' - ! fission bank for each processor. - - call set_particle_seed(int(total_gen + overall_generation(), 8)) - call advance_prn_seed(start) - - ! Determine how many fission sites we need to sample from the source bank - ! and the probability for selecting a site. - - if (total < n_particles) then - sites_needed = mod(n_particles,total) - else - sites_needed = n_particles - end if - p_sample = real(sites_needed,8)/real(total,8) - - call time_bank_sample % start() - - ! ========================================================================== - ! SAMPLE N_PARTICLES FROM FISSION BANK AND PLACE IN TEMP_SITES - - ! Allocate temporary source bank - index_temp = 0_8 - if (.not. allocated(temp_sites)) allocate(temp_sites(3*work)) - - do i = 1, int(n_bank,4) - - ! If there are less than n_particles particles banked, automatically add - ! int(n_particles/total) sites to temp_sites. For example, if you need - ! 1000 and 300 were banked, this would add 3 source sites per banked site - ! and the remaining 100 would be randomly sampled. - if (total < n_particles) then - do j = 1, int(n_particles/total) - index_temp = index_temp + 1 - temp_sites(index_temp) = fission_bank(i) - end do - end if - - ! Randomly sample sites needed - if (prn() < p_sample) then - index_temp = index_temp + 1 - temp_sites(index_temp) = fission_bank(i) - end if - end do - - ! At this point, the sampling of source sites is done and now we need to - ! figure out where to send source sites. Since it is possible that one - ! processor's share of the source bank spans more than just the immediate - ! neighboring processors, we have to perform an ALLGATHER to determine the - ! indices for all processors - -#ifdef OPENMC_MPI - ! First do an exclusive scan to get the starting indices for - start = 0_8 - call MPI_EXSCAN(index_temp, start, 1, MPI_INTEGER8, MPI_SUM, & - mpi_intracomm, mpi_err) - finish = start + index_temp - - ! Allocate space for bank_position if this hasn't been done yet - if (.not. allocated(bank_position)) allocate(bank_position(n_procs)) - call MPI_ALLGATHER(start, 1, MPI_INTEGER8, bank_position, 1, & - MPI_INTEGER8, mpi_intracomm, mpi_err) -#else - start = 0_8 - finish = index_temp -#endif - - ! Now that the sampling is complete, we need to ensure that we have exactly - ! n_particles source sites. The way this is done in a reproducible manner is - ! to adjust only the source sites on the last processor. - - if (rank == n_procs - 1) then - if (finish > n_particles) then - ! If we have extra sites sampled, we will simply discard the extra - ! ones on the last processor - index_temp = n_particles - start - - elseif (finish < n_particles) then - ! If we have too few sites, repeat sites from the very end of the - ! fission bank - sites_needed = n_particles - finish - do i = 1, int(sites_needed,4) - index_temp = index_temp + 1 - temp_sites(index_temp) = fission_bank(n_bank - sites_needed + i) - end do - end if - - ! the last processor should not be sending sites to right - finish = work_index(rank + 1) - end if - - call time_bank_sample % stop() - call time_bank_sendrecv % start() - -#ifdef OPENMC_MPI - ! ========================================================================== - ! SEND BANK SITES TO NEIGHBORS - - index_local = 1 - n_request = 0 - - if (start < n_particles) then - ! Determine the index of the processor which has the first part of the - ! source_bank for the local processor - neighbor = binary_search(work_index, n_procs + 1, start) - 1 - - SEND_SITES: do while (start < finish) - ! Determine the number of sites to send - n = min(work_index(neighbor + 1), finish) - start - - ! Initiate an asynchronous send of source sites to the neighboring - ! process - if (neighbor /= rank) then - n_request = n_request + 1 - call MPI_ISEND(temp_sites(index_local), int(n), MPI_BANK, neighbor, & - rank, mpi_intracomm, request(n_request), mpi_err) - end if - - ! Increment all indices - start = start + n - index_local = index_local + n - neighbor = neighbor + 1 - - ! Check for sites out of bounds -- this only happens in the rare - ! circumstance that a processor close to the end has so many sites that - ! it would exceed the bank on the last processor - if (neighbor > n_procs - 1) exit - end do SEND_SITES - end if - - ! ========================================================================== - ! RECEIVE BANK SITES FROM NEIGHBORS OR TEMPORARY BANK - - start = work_index(rank) - index_local = 1 - - ! Determine what process has the source sites that will need to be stored at - ! the beginning of this processor's source bank. - - if (start >= bank_position(n_procs)) then - neighbor = n_procs - 1 - else - neighbor = binary_search(bank_position, n_procs, start) - 1 - end if - - RECV_SITES: do while (start < work_index(rank + 1)) - ! Determine how many sites need to be received - if (neighbor == n_procs - 1) then - n = work_index(rank + 1) - start - else - n = min(bank_position(neighbor + 2), work_index(rank + 1)) - start - end if - - if (neighbor /= rank) then - ! If the source sites are not on this processor, initiate an - ! asynchronous receive for the source sites - - n_request = n_request + 1 - call MPI_IRECV(source_bank(index_local), int(n), MPI_BANK, & - neighbor, neighbor, mpi_intracomm, request(n_request), mpi_err) - - else - ! If the source sites are on this procesor, we can simply copy them - ! from the temp_sites bank - - index_temp = start - bank_position(rank+1) + 1 - source_bank(index_local:index_local+n-1) = & - temp_sites(index_temp:index_temp+n-1) - end if - - ! Increment all indices - start = start + n - index_local = index_local + n - neighbor = neighbor + 1 - end do RECV_SITES - - ! Since we initiated a series of asynchronous ISENDs and IRECVs, now we have - ! to ensure that the data has actually been communicated before moving on to - ! the next generation - - call MPI_WAITALL(n_request, request, MPI_STATUSES_IGNORE, mpi_err) - - ! Deallocate space for bank_position on the very last generation - if (current_batch == n_max_batches .and. current_gen == gen_per_batch) & - deallocate(bank_position) -#else - source_bank = temp_sites(1:n_particles) -#endif - - call time_bank_sendrecv % stop() - - ! Deallocate space for the temporary source bank on the last generation - if (current_batch == n_max_batches .and. current_gen == gen_per_batch) & - deallocate(temp_sites) - - end subroutine synchronize_bank - -!=============================================================================== -! CALCULATE_GENERATION_KEFF collects the single-processor tracklength k's onto -! the master processor and normalizes them. This should work whether or not the -! no-reduce method is being used. -!=============================================================================== - - subroutine calculate_generation_keff() - - real(8) :: keff_reduced -#ifdef OPENMC_MPI - integer :: mpi_err ! MPI error code -#endif - - ! Get keff for this generation by subtracting off the starting value - keff_generation = global_tallies(RESULT_VALUE, K_TRACKLENGTH) - keff_generation - -#ifdef OPENMC_MPI - ! Combine values across all processors - call MPI_ALLREDUCE(keff_generation, keff_reduced, 1, MPI_REAL8, & - MPI_SUM, mpi_intracomm, mpi_err) -#else - keff_reduced = keff_generation -#endif - - ! Normalize single batch estimate of k - ! TODO: This should be normalized by total_weight, not by n_particles - keff_reduced = keff_reduced / n_particles - call k_generation % push_back(keff_reduced) - - end subroutine calculate_generation_keff - !=============================================================================== ! CALCULATE_AVERAGE_KEFF calculates the mean and standard deviation of the mean ! of k-effective during active generations and broadcasts the mean to all @@ -347,12 +49,12 @@ contains if (n <= 0) then ! For inactive generations, use current generation k as estimate for next ! generation - keff = k_generation % data(i) + keff = k_generation(i) else ! Sample mean of keff - k_sum(1) = k_sum(1) + k_generation % data(i) - k_sum(2) = k_sum(2) + k_generation % data(i)**2 + k_sum(1) = k_sum(1) + k_generation(i) + k_sum(2) = k_sum(2) + k_generation(i)**2 ! Determine mean keff = k_sum(1) / n diff --git a/src/eigenvalue.cpp b/src/eigenvalue.cpp index 454fd57e6..a9f99a946 100644 --- a/src/eigenvalue.cpp +++ b/src/eigenvalue.cpp @@ -5,12 +5,20 @@ #include "xtensor/xview.hpp" #include "openmc/capi.h" +#include "openmc/constants.h" #include "openmc/error.h" #include "openmc/hdf5_interface.h" #include "openmc/mesh.h" #include "openmc/message_passing.h" +#include "openmc/random_lcg.h" +#include "openmc/search.h" #include "openmc/settings.h" #include "openmc/simulation.h" +#include "openmc/timer.h" +#include "openmc/tallies/tally.h" + +#include // for min +#include namespace openmc { @@ -18,6 +26,7 @@ namespace openmc { // Global variables //============================================================================== +double keff_generation; std::vector entropy; xt::xtensor source_frac; @@ -25,6 +34,270 @@ xt::xtensor source_frac; // Non-member functions //============================================================================== +void calculate_generation_keff() +{ + auto gt = global_tallies(); + + // Get keff for this generation by subtracting off the starting value + keff_generation = gt(K_TRACKLENGTH, RESULT_VALUE) - keff_generation; + + double keff_reduced; +#ifdef OPENMC_MPI + // Combine values across all processors + MPI_Allreduce(&keff_generation, &keff_reduced, 1, MPI_DOUBLE, + MPI_SUM, mpi::intracomm); +#else + keff_reduced = keff_generation; +#endif + + // Normalize single batch estimate of k + // TODO: This should be normalized by total_weight, not by n_particles + keff_reduced /= settings::n_particles; + simulation::k_generation.push_back(keff_reduced); +} + +void synchronize_bank() +{ + time_bank.start(); + + // Get pointers to source/fission bank + Bank* source_bank; + Bank* fission_bank; + int64_t n; + openmc_source_bank(&source_bank, &n); + openmc_fission_bank(&fission_bank, &n); + + // In order to properly understand the fission bank algorithm, you need to + // think of the fission and source bank as being one global array divided + // over multiple processors. At the start, each processor has a random amount + // of fission bank sites -- each processor needs to know the total number of + // sites in order to figure out the probability for selecting + // sites. Furthermore, each proc also needs to know where in the 'global' + // fission bank its own sites starts in order to ensure reproducibility by + // skipping ahead to the proper seed. + +#ifdef OPENMC_MPI + int64_t start = 0; + MPI_Exscan(&n_bank, &start, 1, MPI_INT64_T, MPI_SUM, mpi::intracomm); + + // While we would expect the value of start on rank 0 to be 0, the MPI + // standard says that the receive buffer on rank 0 is undefined and not + // significant + if (mpi::rank == 0) start = 0; + + int64_t finish = start + n_bank; + int64_t total = finish; + MPI_Bcast(&total, 1, MPI_INT64_T, mpi::n_procs - 1, mpi::intracomm); + +#else + int64_t start = 0; + int64_t finish = n_bank; + int64_t total = n_bank; +#endif + + // If there are not that many particles per generation, it's possible that no + // fission sites were created at all on a single processor. Rather than add + // extra logic to treat this circumstance, we really want to ensure the user + // runs enough particles to avoid this in the first place. + + if (n_bank == 0) { + fatal_error("No fission sites banked on MPI rank " + std::to_string(mpi::rank)); + } + + // Make sure all processors start at the same point for random sampling. Then + // skip ahead in the sequence using the starting index in the 'global' + // fission bank for each processor. + + set_particle_seed(simulation::total_gen + overall_generation()); + advance_prn_seed(start); + + // Determine how many fission sites we need to sample from the source bank + // and the probability for selecting a site. + + int64_t sites_needed; + if (total < settings::n_particles) { + sites_needed = settings::n_particles % total; + } else { + sites_needed = settings::n_particles; + } + double p_sample = static_cast(sites_needed) / total; + + time_bank_sample.start(); + + // ========================================================================== + // SAMPLE N_PARTICLES FROM FISSION BANK AND PLACE IN TEMP_SITES + + // Allocate temporary source bank + int64_t index_temp = 0; + Bank temp_sites[3*simulation::work]; + + for (int64_t i = 0; i < n_bank; ++i) { + // If there are less than n_particles particles banked, automatically add + // int(n_particles/total) sites to temp_sites. For example, if you need + // 1000 and 300 were banked, this would add 3 source sites per banked site + // and the remaining 100 would be randomly sampled. + if (total < settings::n_particles) { + for (int64_t j = 1; j <= settings::n_particles / total; ++j) { + temp_sites[index_temp] = fission_bank[i]; + ++index_temp; + } + } + + // Randomly sample sites needed + if (prn() < p_sample) { + temp_sites[index_temp] = fission_bank[i]; + ++index_temp; + } + } + + // At this point, the sampling of source sites is done and now we need to + // figure out where to send source sites. Since it is possible that one + // processor's share of the source bank spans more than just the immediate + // neighboring processors, we have to perform an ALLGATHER to determine the + // indices for all processors + +#ifdef OPENMC_MPI + // First do an exclusive scan to get the starting indices for + start = 0; + MPI_Exscan(&index_temp, &start, 1, MPI_INT64_T, MPI_SUM, mpi::intracomm); + finish = start + index_temp; + + // Allocate space for bank_position if this hasn't been done yet + int64_t bank_position[mpi::n_procs]; + MPI_Allgather(&start, 1, MPI_INT64_T, bank_position, 1, + MPI_INT64_T, mpi::intracomm); +#else + start = 0; + finish = index_temp; +#endif + + // Now that the sampling is complete, we need to ensure that we have exactly + // n_particles source sites. The way this is done in a reproducible manner is + // to adjust only the source sites on the last processor. + + if (mpi::rank == mpi::n_procs - 1) { + if (finish > settings::n_particles) { + // If we have extra sites sampled, we will simply discard the extra + // ones on the last processor + index_temp = settings::n_particles - start; + + } else if (finish < settings::n_particles) { + // If we have too few sites, repeat sites from the very end of the + // fission bank + sites_needed = settings::n_particles - finish; + for (int i = 0; i < sites_needed; ++i) { + temp_sites[index_temp] = fission_bank[n_bank - sites_needed + i]; + ++index_temp; + } + } + + // the last processor should not be sending sites to right + finish = simulation::work_index[mpi::rank + 1]; + } + + time_bank_sample.stop(); + time_bank_sendrecv.start(); + +#ifdef OPENMC_MPI + // ========================================================================== + // SEND BANK SITES TO NEIGHBORS + + int64_t index_local = 0; + std::vector requests; + + if (start < settings::n_particles) { + // Determine the index of the processor which has the first part of the + // source_bank for the local processor + int neighbor = upper_bound_index(simulation::work_index.begin(), + simulation::work_index.end(), start); + + while (start < finish) { + // Determine the number of sites to send + int64_t n = std::min(simulation::work_index[neighbor + 1], finish) - start; + + // Initiate an asynchronous send of source sites to the neighboring + // process + if (neighbor != mpi::rank) { + requests.emplace_back(); + MPI_Isend(&temp_sites[index_local], static_cast(n), mpi::bank, + neighbor, mpi::rank, mpi::intracomm, &requests.back()); + } + + // Increment all indices + start += n; + index_local += n; + ++neighbor; + + // Check for sites out of bounds -- this only happens in the rare + // circumstance that a processor close to the end has so many sites that + // it would exceed the bank on the last processor + if (neighbor > mpi::n_procs - 1) break; + } + } + + // ========================================================================== + // RECEIVE BANK SITES FROM NEIGHBORS OR TEMPORARY BANK + + start = simulation::work_index[mpi::rank]; + index_local = 0; + + // Determine what process has the source sites that will need to be stored at + // the beginning of this processor's source bank. + + int neighbor; + if (start >= bank_position[mpi::n_procs - 1]) { + neighbor = mpi::n_procs - 1; + } else { + neighbor = upper_bound_index(bank_position, bank_position + mpi::n_procs, start); + } + + while (start < simulation::work_index[mpi::rank + 1]) { + // Determine how many sites need to be received + int64_t n; + if (neighbor == mpi::n_procs - 1) { + n = simulation::work_index[mpi::rank + 1] - start; + } else { + n = std::min(bank_position[neighbor + 1], simulation::work_index[mpi::rank + 1]) - start; + } + + if (neighbor != mpi::rank) { + // If the source sites are not on this processor, initiate an + // asynchronous receive for the source sites + + requests.emplace_back(); + MPI_Irecv(&source_bank[index_local], static_cast(n), mpi::bank, + neighbor, neighbor, mpi::intracomm, &requests.back()); + + } else { + // If the source sites are on this procesor, we can simply copy them + // from the temp_sites bank + + index_temp = start - bank_position[mpi::rank]; + std::copy(&temp_sites[index_temp], &temp_sites[index_temp + n], + &source_bank[index_local]); + } + + // Increment all indices + start += n; + index_local += n; + ++neighbor; + } + + // Since we initiated a series of asynchronous ISENDs and IRECVs, now we have + // to ensure that the data has actually been communicated before moving on to + // the next generation + + int n_request = requests.size(); + MPI_Waitall(n_request, requests.data(), MPI_STATUSES_IGNORE); + +#else + std::copy(temp_sites, temp_sites + settings::n_particles, source_bank); +#endif + + time_bank_sendrecv.stop(); + time_bank.stop(); +} + void shannon_entropy() { // Get pointer to entropy mesh @@ -66,7 +339,7 @@ void ufs_count_sites() { auto &m = meshes[settings::index_ufs_mesh]; - if (openmc_current_batch == 1 && openmc_current_gen == 1) { + if (simulation::current_batch == 1 && simulation::current_gen == 1) { // On the first generation, just assume that the source is already evenly // distributed so that effectively the production of fission sites is not // biased @@ -82,7 +355,7 @@ void ufs_count_sites() // count number of source sites in each ufs mesh cell bool sites_outside; - source_frac = m->count_sites(openmc_work, source_bank, 0, nullptr, + source_frac = m->count_sites(simulation::work, source_bank, 0, nullptr, &sites_outside); // Check for sites outside of the mesh @@ -102,7 +375,7 @@ void ufs_count_sites() // Since the total starting weight is not equal to n_particles, we need to // renormalize the weight of the source sites - for (int i = 0; i < openmc_work; ++i) { + for (int i = 0; i < simulation::work; ++i) { source_bank[i].wgt *= settings::n_particles / total; } } @@ -127,18 +400,34 @@ double ufs_get_weight(const Particle* p) } } -extern "C" void entropy_to_hdf5(hid_t group) +extern "C" void write_eigenvalue_hdf5(hid_t group) { + write_dataset(group, "n_inactive", settings::n_inactive); + write_dataset(group, "generations_per_batch", settings::gen_per_batch); + write_dataset(group, "k_generation", simulation::k_generation); if (settings::entropy_on) { write_dataset(group, "entropy", entropy); } + write_dataset(group, "k_col_abs", simulation::k_col_abs); + write_dataset(group, "k_col_tra", simulation::k_col_tra); + write_dataset(group, "k_abs_tra", simulation::k_abs_tra); + std::array k_combined; + openmc_get_keff(k_combined.data()); + write_dataset(group, "k_combined", k_combined); } -extern "C" void entropy_from_hdf5(hid_t group) +extern "C" void read_eigenvalue_hdf5(hid_t group) { + read_dataset(group, "generations_per_batch", settings::gen_per_batch); + int n = simulation::restart_batch*settings::gen_per_batch; + simulation::k_generation.resize(n); + read_dataset(group, "k_generation", simulation::k_generation); if (settings::entropy_on) { read_dataset(group, "entropy", entropy); } + read_dataset(group, "k_col_abs", simulation::k_col_abs); + read_dataset(group, "k_col_tra", simulation::k_col_tra); + read_dataset(group, "k_abs_tra", simulation::k_abs_tra); } extern "C" double entropy_c(int i) diff --git a/src/error.F90 b/src/error.F90 index c00006c8d..ad07987eb 100644 --- a/src/error.F90 +++ b/src/error.F90 @@ -130,14 +130,20 @@ contains character(*) :: message integer, optional :: error_code ! error code - integer :: code ! error code + integer(C_INT) :: code ! error code integer :: i_start ! starting position integer :: i_end ! ending position integer :: line_wrap ! length of line integer :: length ! length of message integer :: indent ! length of indentation + #ifdef OPENMC_MPI - integer :: mpi_err + interface + subroutine abort_mpi(code) bind(C) + import C_INT + integer(C_INT), value :: code + end subroutine + end interface #endif @@ -190,7 +196,7 @@ contains #ifdef OPENMC_MPI ! Abort MPI - call MPI_ABORT(mpi_intracomm, code, mpi_err) + call abort_mpi(code) #endif ! Abort program diff --git a/src/error.cpp b/src/error.cpp new file mode 100644 index 000000000..d17219f31 --- /dev/null +++ b/src/error.cpp @@ -0,0 +1,14 @@ +#include "openmc/error.h" + +#include "openmc/message_passing.h" + +namespace openmc { + +#ifdef OPENMC_MPI +void abort_mpi(int code) +{ + MPI_Abort(mpi::intracomm, code); +} +#endif + +} // namespace openmc diff --git a/src/finalize.cpp b/src/finalize.cpp index 3b4f26764..7f07ddac0 100644 --- a/src/finalize.cpp +++ b/src/finalize.cpp @@ -2,9 +2,13 @@ #include "openmc/message_passing.h" +namespace openmc { + void openmc_free_bank() { #ifdef OPENMC_MPI - MPI_Type_free(&openmc::mpi::bank); + MPI_Type_free(&mpi::bank); #endif } + +} // namespace openmc diff --git a/src/geometry.cpp b/src/geometry.cpp index 857dbd32e..28885a5db 100644 --- a/src/geometry.cpp +++ b/src/geometry.cpp @@ -91,7 +91,7 @@ find_cell(Particle* p, int search_surf) { if (cells[i_cell]->contains(r, u, surf)) { p->coord[p->n_coord-1].cell = i_cell; - if (settings::verbosity >= 10 || openmc_trace) { + if (settings::verbosity >= 10 || simulation::trace) { std::stringstream msg; msg << " Entering cell " << cells[i_cell]->id_; write_message(msg, 1); @@ -255,7 +255,7 @@ cross_lattice(Particle* p, int lattice_translation[3]) { Lattice& lat {*lattices[p->coord[p->n_coord-1].lattice-1]}; - if (settings::verbosity >= 10 || openmc_trace) { + if (settings::verbosity >= 10 || simulation::trace) { std::stringstream msg; msg << " Crossing lattice " << lat.id_ << ". Current position (" << p->coord[p->n_coord-1].lattice_x << "," diff --git a/src/hdf5_interface.F90 b/src/hdf5_interface.F90 index 8872a84f6..7856abb50 100644 --- a/src/hdf5_interface.F90 +++ b/src/hdf5_interface.F90 @@ -13,9 +13,6 @@ module hdf5_interface use, intrinsic :: ISO_C_BINDING use error, only: fatal_error -#ifdef PHDF5 - use message_passing, only: mpi_intracomm, MPI_INFO_NULL -#endif use string, only: to_c_string, to_f_string implicit none diff --git a/src/initialize.F90 b/src/initialize.F90 index 0dbaa83bf..3d805c97d 100644 --- a/src/initialize.F90 +++ b/src/initialize.F90 @@ -49,44 +49,17 @@ contains ! setting up timers, etc. !=============================================================================== - function openmc_init_f(intracomm) result(err) bind(C) - integer, intent(in), optional :: intracomm ! MPI intracommunicator + function openmc_init_f() result(err) bind(C) integer(C_INT) :: err #ifdef _OPENMP character(MAX_WORD_LEN) :: envvar #endif - ! Copy the communicator to a new variable. This is done to avoid changing - ! the signature of this subroutine. If MPI is being used but no communicator - ! was passed, assume MPI_COMM_WORLD. -#ifdef OPENMC_MPI -#ifdef OPENMC_MPIF08 - type(MPI_Comm), intent(in) :: comm ! MPI intracommunicator - if (present(intracomm)) then - comm % MPI_VAL = intracomm - else - comm = MPI_COMM_WORLD - end if -#else - integer :: comm - if (present(intracomm)) then - comm = intracomm - else - comm = MPI_COMM_WORLD - end if -#endif -#endif - ! Start total and initialization timer call time_total%start() call time_initialize%start() -#ifdef OPENMC_MPI - ! Setup MPI - call initialize_mpi(comm) -#endif - #ifdef _OPENMP ! Change schedule of main parallel-do loop if OMP_SCHEDULE is set call get_environment_variable("OMP_SCHEDULE", envvar) @@ -114,57 +87,6 @@ contains err = 0 end function openmc_init_f -#ifdef OPENMC_MPI -!=============================================================================== -! INITIALIZE_MPI starts up the Message Passing Interface (MPI) and determines -! the number of processors the problem is being run with as well as the rank of -! each processor. -!=============================================================================== - - subroutine initialize_mpi(intracomm) -#ifdef OPENMC_MPIF08 - type(MPI_Comm), intent(in) :: intracomm ! MPI intracommunicator -#else - integer, intent(in) :: intracomm ! MPI intracommunicator -#endif - - integer :: mpi_err ! MPI error code - integer :: bank_blocks(5) ! Count for each datatype -#ifdef OPENMC_MPIF08 - type(MPI_Datatype) :: bank_types(5) -#else - integer :: bank_types(5) ! Datatypes -#endif - integer(MPI_ADDRESS_KIND) :: bank_disp(5) ! Displacements - type(Bank) :: b - - ! Indicate that MPI is turned on - mpi_enabled = .true. - mpi_intracomm = intracomm - - ! ========================================================================== - ! CREATE MPI_BANK TYPE - - ! Determine displacements for MPI_BANK type - call MPI_GET_ADDRESS(b % wgt, bank_disp(1), mpi_err) - call MPI_GET_ADDRESS(b % xyz, bank_disp(2), mpi_err) - call MPI_GET_ADDRESS(b % uvw, bank_disp(3), mpi_err) - call MPI_GET_ADDRESS(b % E, bank_disp(4), mpi_err) - call MPI_GET_ADDRESS(b % delayed_group, bank_disp(5), mpi_err) - - ! Adjust displacements - bank_disp = bank_disp - bank_disp(1) - - ! Define MPI_BANK for fission sites - bank_blocks = (/ 1, 3, 3, 1, 1 /) - bank_types = (/ MPI_REAL8, MPI_REAL8, MPI_REAL8, MPI_REAL8, MPI_INTEGER /) - call MPI_TYPE_CREATE_STRUCT(5, bank_blocks, bank_disp, & - bank_types, MPI_BANK, mpi_err) - call MPI_TYPE_COMMIT(MPI_BANK, mpi_err) - - end subroutine initialize_mpi -#endif - !=============================================================================== ! READ_COMMAND_LINE reads all parameters from the command line !=============================================================================== diff --git a/src/initialize.cpp b/src/initialize.cpp index 0c26135dc..26d21bf09 100644 --- a/src/initialize.cpp +++ b/src/initialize.cpp @@ -15,9 +15,11 @@ #include "openmc/hdf5_interface.h" #include "openmc/message_passing.h" #include "openmc/settings.h" +#include "openmc/simulation.h" #include "openmc/string_utils.h" // data/functions from Fortran side +extern "C" void openmc_init_f(); extern "C" void print_usage(); extern "C" void print_version(); @@ -46,12 +48,7 @@ int openmc_init(int argc, char* argv[], const void* intracomm) if (err) return err; // Continue with rest of initialization -#ifdef OPENMC_MPI - MPI_Fint fcomm = MPI_Comm_c2f(comm); - openmc_init_f(&fcomm); -#else - openmc_init_f(nullptr); -#endif + openmc_init_f(); return 0; } @@ -79,16 +76,18 @@ void initialize_mpi(MPI_Comm intracomm) // Create bank datatype Bank b; - MPI_Aint disp[] { - offsetof(Bank, wgt), - offsetof(Bank, xyz), - offsetof(Bank, uvw), - offsetof(Bank, E), - offsetof(Bank, delayed_group) - }; - int blocks[] {1, 3, 3, 1, 1}; - MPI_Datatype types[] {MPI_DOUBLE, MPI_DOUBLE, MPI_DOUBLE, MPI_DOUBLE, MPI_INT}; - MPI_Type_create_struct(5, blocks, disp, types, &mpi::bank); + MPI_Aint disp[6]; + MPI_Get_address(&b.wgt, &disp[0]); + MPI_Get_address(&b.xyz, &disp[1]); + MPI_Get_address(&b.uvw, &disp[2]); + MPI_Get_address(&b.E, &disp[3]); + MPI_Get_address(&b.delayed_group, &disp[4]); + MPI_Get_address(&b.particle, &disp[5]); + for (int i = 5; i >= 0; --i) disp[i] -= disp[0]; + + int blocks[] {1, 3, 3, 1, 1, 1}; + MPI_Datatype types[] {MPI_DOUBLE, MPI_DOUBLE, MPI_DOUBLE, MPI_DOUBLE, MPI_INT, MPI_INT}; + MPI_Type_create_struct(6, blocks, disp, types, &mpi::bank); MPI_Type_commit(&mpi::bank); } #endif // OPENMC_MPI @@ -172,13 +171,13 @@ parse_command_line(int argc, char* argv[]) #ifdef _OPENMP // Read and set number of OpenMP threads - openmc_n_threads = std::stoi(argv[i]); - if (openmc_n_threads < 1) { + simulation::n_threads = std::stoi(argv[i]); + if (simulation::n_threads < 1) { std::string msg {"Number of threads must be positive."}; strcpy(openmc_err_msg, msg.c_str()); return OPENMC_E_INVALID_ARGUMENT; } - omp_set_num_threads(openmc_n_threads); + omp_set_num_threads(simulation::n_threads); #else if (openmc_master) warning("Ignoring number of threads specified on command line."); diff --git a/src/input_xml.F90 b/src/input_xml.F90 index d4fd5dfa4..be28cd85f 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -219,7 +219,7 @@ contains if (run_mode == MODE_EIGENVALUE) then ! Preallocate space for keff and entropy by generation - call k_generation % reserve(n_max_batches*gen_per_batch) + call k_generation_reserve(n_max_batches*gen_per_batch) end if ! Particle tracks @@ -2975,7 +2975,7 @@ contains ! Check if material is fissionable if (nuclides(materials(i) % nuclide(j)) % fissionable) then - materials(i) % fissionable = .true. + call materials(i) % set_fissionable(.true.) end if end do diff --git a/src/material.cpp b/src/material.cpp index fb52d5cd9..ecc6eed2b 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -122,6 +122,13 @@ extern "C" { material_map[id] = index - 1; } + bool material_fissionable(Material* mat) {return mat->fissionable;} + + void material_set_fissionable(Material* mat, bool fissionable) + { + mat->fissionable = fissionable; + } + void extend_materials_c(int32_t n) { materials.reserve(materials.size() + n); diff --git a/src/material_header.F90 b/src/material_header.F90 index 71e6ec5f1..b4d061725 100644 --- a/src/material_header.F90 +++ b/src/material_header.F90 @@ -51,6 +51,20 @@ module material_header integer(C_INT32_T), intent(in), value :: index end subroutine material_set_id_c + function material_fissionable_c(mat_ptr) & + bind(C, name='material_fissionable') result(fissionable) + import C_PTR, C_BOOL + type(C_PTR), intent(in), value :: mat_ptr + logical(C_BOOL) :: fissionable + end function material_fissionable_c + + subroutine material_set_fissionable_c(mat_ptr, fissionable) & + bind(C, name='material_set_fissionable') + import C_PTR, C_BOOL + type(C_PTR), intent(in), value :: mat_ptr + logical(C_BOOL), intent(in), value :: fissionable + end subroutine material_set_fissionable_c + subroutine extend_materials_c(n) bind(C) import C_INT32_T integer(C_INT32_T), intent(in), value :: n @@ -95,7 +109,6 @@ module material_header character(20), allocatable :: sab_names(:) ! name of S(a,b) table ! Does this material contain fissionable nuclides? Is it depletable? - logical :: fissionable = .false. logical :: depletable = .false. ! enforce isotropic scattering in lab for specific nuclides @@ -105,6 +118,8 @@ module material_header contains procedure :: id => material_id procedure :: set_id => material_set_id + procedure :: fissionable => material_fissionable + procedure :: set_fissionable => material_set_fissionable procedure :: set_density => material_set_density procedure :: init_nuclide_index => material_init_nuclide_index procedure :: assign_sab_tables => material_assign_sab_tables @@ -139,6 +154,18 @@ contains call material_set_id_c(this % ptr, id, index) end subroutine material_set_id + function material_fissionable(this) result(fissionable) + class(Material), intent(in) :: this + logical(C_BOOL) :: fissionable + fissionable = material_fissionable_c(this % ptr) + end function material_fissionable + + subroutine material_set_fissionable(this, fissionable) + class(Material),intent(in) :: this + logical, intent(in) :: fissionable + call material_set_fissionable_c(this % ptr, logical(fissionable, C_BOOL)) + end subroutine material_set_fissionable + function material_set_density(this, density) result(err) class(Material), intent(inout) :: this real(8), intent(in) :: density @@ -685,7 +712,7 @@ contains integer(C_INT) :: err if (index >= 1 .and. index <= size(materials)) then - fissionable = materials(index) % fissionable + fissionable = materials(index) % fissionable() err = 0 else err = E_OUT_OF_BOUNDS diff --git a/src/math.F90 b/src/math.F90 index 9964922c1..143e21e03 100644 --- a/src/math.F90 +++ b/src/math.F90 @@ -12,10 +12,7 @@ module math public :: calc_rn public :: calc_zn public :: calc_zn_rad - public :: evaluate_legendre public :: rotate_angle - public :: maxwell_spectrum - public :: watt_spectrum public :: faddeeva public :: w_derivative public :: broaden_wmp_polynomials @@ -42,16 +39,6 @@ module math real(C_DOUBLE), intent(out) :: pnx(n + 1) end subroutine calc_pn - pure function evaluate_legendre_c_intfc(n, data, x) & - bind(C, name='evaluate_legendre_c') result(val) - use ISO_C_BINDING - implicit none - integer(C_INT), value, intent(in) :: n - real(C_DOUBLE), intent(in) :: data(n) - real(C_DOUBLE), value, intent(in) :: x - real(C_DOUBLE) :: val - end function evaluate_legendre_c_intfc - pure subroutine calc_rn(n, uvw, rn) bind(C, name='calc_rn_c') use ISO_C_BINDING implicit none @@ -85,23 +72,6 @@ module math real(C_DOUBLE), optional, intent(in) :: phi end subroutine rotate_angle_c_intfc - function maxwell_spectrum(T) bind(C, name='maxwell_spectrum_c') & - result(E_out) - use ISO_C_BINDING - implicit none - real(C_DOUBLE), value, intent(in) :: T - real(C_DOUBLE) :: E_out - end function maxwell_spectrum - - function watt_spectrum(a, b) bind(C, name='watt_spectrum_c') & - result(E_out) - use ISO_C_BINDING - implicit none - real(C_DOUBLE), value, intent(in) :: a - real(C_DOUBLE), value, intent(in) :: b - real(C_DOUBLE) :: E_out - end function watt_spectrum - subroutine broaden_wmp_polynomials(E, dopp, n, factors) & bind(C, name='broaden_wmp_polynomials_c') use ISO_C_BINDING @@ -157,20 +127,6 @@ module math contains -!=============================================================================== -! EVALUATE_LEGENDRE Find the value of f(x) given a set of Legendre coefficients -! and the value of x -!=============================================================================== - - pure function evaluate_legendre(data, x) result(val) bind(C) - real(C_DOUBLE), intent(in) :: data(:) - real(C_DOUBLE), intent(in) :: x - real(C_DOUBLE) :: val - - val = evaluate_legendre_c_intfc(size(data) - 1, data, x) - - end function evaluate_legendre - !=============================================================================== ! ROTATE_ANGLE rotates direction cosines through a polar angle whose cosine is ! mu and through an azimuthal angle sampled uniformly. Note that this is done diff --git a/src/math_functions.cpp b/src/math_functions.cpp index 5c1a2f4dc..8d30ea84d 100644 --- a/src/math_functions.cpp +++ b/src/math_functions.cpp @@ -6,7 +6,7 @@ namespace openmc { // Mathematical methods //============================================================================== -double normal_percentile_c(double p) { +double normal_percentile(double p) { constexpr double p_low = 0.02425; constexpr double a[6] = {-3.969683028665376e1, 2.209460984245205e2, -2.759285104469687e2, 1.383577518672690e2, @@ -79,7 +79,7 @@ double t_percentile_c(double p, int df){ // 16 (4), pp. 1123-1132 (1987). double n = df; double k = 1. / (n - 2.); - double z = normal_percentile_c(p); + double z = normal_percentile(p); double z2 = z * z; t = std::sqrt(n * k) * (z + (z2 - 3.) * z * k / 4. + ((5. * z2 - 56.) * z2 + 75.) * z * k * k / 96. + (((z2 - 27.) * 3. * z2 + 417.) * z2 - 315.) * @@ -103,7 +103,7 @@ void calc_pn_c(int n, double x, double pnx[]) { } -double evaluate_legendre_c(int n, const double data[], double x) { +double evaluate_legendre(int n, const double data[], double x) { double pnx[n + 1]; double val = 0.0; calc_pn_c(n, x, pnx); @@ -658,7 +658,7 @@ Direction rotate_angle(Direction u, double mu, double* phi) } -double maxwell_spectrum_c(double T) { +double maxwell_spectrum(double T) { // Set the random numbers double r1 = prn(); double r2 = prn(); @@ -674,8 +674,8 @@ double maxwell_spectrum_c(double T) { } -double watt_spectrum_c(double a, double b) { - double w = maxwell_spectrum_c(a); +double watt_spectrum(double a, double b) { + double w = maxwell_spectrum(a); double E_out = w + 0.25 * a * a * b + (2. * prn() - 1.) * std::sqrt(a * a * b * w); return E_out; diff --git a/src/message_passing.F90 b/src/message_passing.F90 index 6ade0895a..37d203447 100644 --- a/src/message_passing.F90 +++ b/src/message_passing.F90 @@ -2,28 +2,12 @@ module message_passing use, intrinsic :: ISO_C_BINDING -#ifdef OPENMC_MPI -#ifdef OPENMC_MPIF08 - use mpi_f08 -#else - use mpi -#endif -#endif - - ! The defaults set here for the number of processors, rank, and master and - ! mpi_enabled flag are for when MPI is not being used at all, i.e. a serial - ! run. In this case, these variables are still used at times. + ! The defaults set here for the number of processors, rank, and master and are + ! for when MPI is not being used at all, i.e. a serial run. In this case, these + ! variables are still used at times. integer(C_INT), bind(C, name='openmc_n_procs') :: n_procs = 1 ! number of processes integer(C_INT), bind(C, name='openmc_rank') :: rank = 0 ! rank of process logical(C_BOOL), bind(C, name='openmc_master') :: master = .true. ! master process? - logical :: mpi_enabled = .false. ! is MPI in use and initialized? -#ifdef OPENMC_MPIF08 - type(MPI_Datatype) :: MPI_BANK ! MPI datatype for fission bank - type(MPI_Comm) :: mpi_intracomm ! MPI intra-communicator -#else - integer :: MPI_BANK ! MPI datatype for fission bank - integer :: mpi_intracomm ! MPI intra-communicator -#endif end module message_passing diff --git a/src/message_passing.cpp b/src/message_passing.cpp index dc287e3b3..8d8779cca 100644 --- a/src/message_passing.cpp +++ b/src/message_passing.cpp @@ -13,4 +13,23 @@ MPI_Datatype bank; #endif } // namespace mpi + +//============================================================================== +// Fortran compatibility functions +//============================================================================== + +#ifdef OPENMC_MPI +extern "C" void +send_int(void* buffer, int count, int dest, int tag) +{ + MPI_Send(buffer, count, MPI_INTEGER, dest, tag, mpi::intracomm); +} + +extern "C" void +recv_int(void* buffer, int count, int source, int tag) +{ + MPI_Recv(buffer, count, MPI_INTEGER, source, tag, mpi::intracomm, MPI_STATUS_IGNORE); +} +#endif + } // namespace openmc diff --git a/src/mgxs_data.F90 b/src/mgxs_data.F90 index 03a0e9402..90178a015 100644 --- a/src/mgxs_data.F90 +++ b/src/mgxs_data.F90 @@ -90,7 +90,8 @@ contains end if end do NUCLIDE_LOOP - mat % fissionable = query_fissionable_c(mat % n_nuclides, mat % nuclide) + call mat % set_fissionable( & + logical(query_fissionable_c(mat % n_nuclides, mat % nuclide))) end do MATERIAL_LOOP diff --git a/src/mgxs_interface.F90 b/src/mgxs_interface.F90 index 9dccc9230..77f629b80 100644 --- a/src/mgxs_interface.F90 +++ b/src/mgxs_interface.F90 @@ -62,26 +62,6 @@ module mgxs_interface real(C_DOUBLE), intent(inout) :: nu_fiss_xs end subroutine calculate_xs_c - subroutine sample_scatter_c(i_mat, gin, gout, mu, wgt, uvw) bind(C) - use ISO_C_BINDING - implicit none - integer(C_INT), value, intent(in) :: i_mat - integer(C_INT), value, intent(in) :: gin - integer(C_INT), intent(inout) :: gout - real(C_DOUBLE), intent(inout) :: mu - real(C_DOUBLE), intent(inout) :: wgt - real(C_DOUBLE), intent(inout) :: uvw(1:3) - end subroutine sample_scatter_c - - subroutine sample_fission_energy_c(i_mat, gin, dg, gout) bind(C) - use ISO_C_BINDING - implicit none - integer(C_INT), value, intent(in) :: i_mat - integer(C_INT), value, intent(in) :: gin - integer(C_INT), intent(inout) :: dg - integer(C_INT), intent(inout) :: gout - end subroutine sample_fission_energy_c - subroutine get_name_c(index, name_len, name) bind(C) use ISO_C_BINDING implicit none @@ -156,7 +136,7 @@ module mgxs_interface real(8), allocatable :: energy_bins(:) ! Midpoint of the energy group structure - real(8), allocatable :: energy_bin_avg(:) + real(C_DOUBLE), allocatable :: energy_bin_avg(:) ! Energy group structure with increasing energy real(C_DOUBLE), allocatable, target :: rev_energy_bins(:) diff --git a/src/mgxs_interface.cpp b/src/mgxs_interface.cpp index 0bb464da0..c592d28a2 100644 --- a/src/mgxs_interface.cpp +++ b/src/mgxs_interface.cpp @@ -97,36 +97,6 @@ calculate_xs_c(int i_mat, int gin, double sqrtkT, const double uvw[3], //============================================================================== -void -sample_scatter_c(int i_mat, int gin, int& gout, double& mu, double& wgt, - double uvw[3]) -{ - int gout_c = gout - 1; - macro_xs[i_mat - 1].sample_scatter(gin - 1, gout_c, mu, wgt); - - // adjust return value for fortran indexing - gout = gout_c + 1; - - // Rotate the angle - rotate_angle_c(uvw, mu, nullptr); -} - -//============================================================================== - -void -sample_fission_energy_c(int i_mat, int gin, int& dg, int& gout) -{ - int dg_c = 0; - int gout_c = 0; - macro_xs[i_mat - 1].sample_fission_energy(gin - 1, dg_c, gout_c); - - // adjust return values for fortran indexing - dg = dg_c + 1; - gout = gout_c + 1; -} - -//============================================================================== - double get_nuclide_xs_c(int index, int xstype, int gin, int* gout, double* mu, int* dg) { diff --git a/src/output.F90 b/src/output.F90 index e9eecc3fd..b1a2f4e77 100644 --- a/src/output.F90 +++ b/src/output.F90 @@ -339,7 +339,7 @@ contains ! write out information about batch and generation write(UNIT=OUTPUT_UNIT, FMT='(2X,A9)', ADVANCE='NO') & trim(to_str(current_batch)) // "/" // trim(to_str(current_gen)) - write(UNIT=OUTPUT_UNIT, FMT='(3X,F8.5)', ADVANCE='NO') k_generation % data(i) + write(UNIT=OUTPUT_UNIT, FMT='(3X,F8.5)', ADVANCE='NO') k_generation(i) ! write out entropy info if (entropy_on) write(UNIT=OUTPUT_UNIT, FMT='(3X, F8.5)', ADVANCE='NO') & @@ -373,7 +373,7 @@ contains write(UNIT=OUTPUT_UNIT, FMT='(2X,A9)', ADVANCE='NO') & trim(to_str(current_batch)) // "/" // trim(to_str(gen_per_batch)) write(UNIT=OUTPUT_UNIT, FMT='(3X,F8.5)', ADVANCE='NO') & - k_generation % data(i) + k_generation(i) ! write out entropy info if (entropy_on) write(UNIT=OUTPUT_UNIT, FMT='(3X, F8.5)', ADVANCE='NO') & @@ -511,9 +511,9 @@ contains end if write(ou,100) " Time in active batches", time_active % elapsed if (run_mode == MODE_EIGENVALUE) then - write(ou,100) " Time synchronizing fission bank", time_bank % elapsed - write(ou,100) " Sampling source sites", time_bank_sample % elapsed - write(ou,100) " SEND/RECV source sites", time_bank_sendrecv % elapsed + write(ou,100) " Time synchronizing fission bank", time_bank_elapsed() + write(ou,100) " Sampling source sites", time_bank_sample_elapsed() + write(ou,100) " SEND/RECV source sites", time_bank_sendrecv_elapsed() end if write(ou,100) " Time accumulating tallies", time_tallies % elapsed if (cmfd_run) write(ou,100) " Time in CMFD", time_cmfd % elapsed diff --git a/src/particle.cpp b/src/particle.cpp index ca57c2faa..d6dfa969c 100644 --- a/src/particle.cpp +++ b/src/particle.cpp @@ -127,15 +127,15 @@ Particle::mark_as_lost(const char* message) // Increment number of lost particles alive = false; #pragma omp atomic - openmc_n_lost_particles += 1; + simulation::n_lost_particles += 1; // Count the total number of simulated particles (on this processor) - auto n = openmc_current_batch * settings::gen_per_batch * openmc_work; + auto n = simulation::current_batch * settings::gen_per_batch * simulation::work; // Abort the simulation if the maximum number of lost particles has been // reached - if (openmc_n_lost_particles >= MAX_LOST_PARTICLES && - openmc_n_lost_particles >= REL_MAX_LOST_PARTICLES*n) { + if (simulation::n_lost_particles >= MAX_LOST_PARTICLES && + simulation::n_lost_particles >= REL_MAX_LOST_PARTICLES*n) { fatal_error("Maximum number of lost particles has been reached."); } } @@ -148,7 +148,7 @@ Particle::write_restart() const // Set up file name std::stringstream filename; - filename << settings::path_output << "particle_" << openmc_current_batch + filename << settings::path_output << "particle_" << simulation::current_batch << '_' << id << ".h5"; #pragma omp critical (WriteParticleRestart) @@ -165,9 +165,9 @@ Particle::write_restart() const #endif // Write data to file - write_dataset(file_id, "current_batch", openmc_current_batch); + write_dataset(file_id, "current_batch", simulation::current_batch); write_dataset(file_id, "generations_per_batch", settings::gen_per_batch); - write_dataset(file_id, "current_generation", openmc_current_gen); + write_dataset(file_id, "current_generation", simulation::current_gen); write_dataset(file_id, "n_particles", settings::n_particles); switch (settings::run_mode) { case RUN_MODE_FIXEDSOURCE: @@ -188,7 +188,7 @@ Particle::write_restart() const int64_t n; openmc_source_bank(&src, &n); - int64_t i = openmc_current_work; + int64_t i = simulation::current_work; write_dataset(file_id, "weight", src[i-1].wgt); write_dataset(file_id, "energy", src[i-1].E); hsize_t dims[] {3}; diff --git a/src/physics_common.F90 b/src/physics_common.F90 index 63bf62ce6..0d5b9fb32 100644 --- a/src/physics_common.F90 +++ b/src/physics_common.F90 @@ -1,33 +1,20 @@ module physics_common - use constants + use, intrinsic :: ISO_C_BINDING + use particle_header, only: Particle - use random_lcg, only: prn - use settings, only: weight_cutoff, weight_survive implicit none -contains - !=============================================================================== -! RUSSIAN_ROULETTE +! RUSSIAN_ROULETTE FROM C !=============================================================================== - subroutine russian_roulette(p) - - type(Particle), intent(inout) :: p - - if (p % wgt < weight_cutoff) then - if (prn() < p % wgt / weight_survive) then - p % wgt = weight_survive - p % last_wgt = p % wgt - else - p % wgt = ZERO - p % last_wgt = ZERO - p % alive = .false. - end if - end if - - end subroutine russian_roulette + interface + subroutine russian_roulette(p) bind(C) + import Particle + type(Particle), intent(inout) :: p + end subroutine russian_roulette + end interface end module physics_common diff --git a/src/physics_common.cpp b/src/physics_common.cpp new file mode 100644 index 000000000..02c77ae7d --- /dev/null +++ b/src/physics_common.cpp @@ -0,0 +1,26 @@ +#include "openmc/physics_common.h" + +#include "openmc/settings.h" +#include "openmc/random_lcg.h" + +namespace openmc { + +//============================================================================== +// RUSSIAN_ROULETTE +//============================================================================== + +void russian_roulette(Particle* p) +{ + if (p->wgt < settings::weight_cutoff) { + if (prn() < p->wgt / settings::weight_survive) { + p->wgt = settings::weight_survive; + p->last_wgt = p->wgt; + } else { + p->wgt = 0.; + p->last_wgt = 0.; + p->alive = false; + } + } +} + +} //namespace openmc diff --git a/src/physics_mg.F90 b/src/physics_mg.F90 deleted file mode 100644 index 1d60b371e..000000000 --- a/src/physics_mg.F90 +++ /dev/null @@ -1,275 +0,0 @@ -module physics_mg - ! This module contains the multi-group specific physics routines so as to not - ! hinder performance of the CE versions with multiple if-thens. - - use bank_header - use constants - use error, only: fatal_error, warning, write_message - use material_header, only: Material, materials - use math, only: rotate_angle - use mgxs_interface - use message_passing - use nuclide_header, only: material_xs - use particle_header - use physics_common - use random_lcg, only: prn - use settings - use simulation_header - use string, only: to_str - use tally_header - - implicit none - -contains - -!=============================================================================== -! COLLISION_MG samples a nuclide and reaction and then calls the appropriate -! routine for that reaction -!=============================================================================== - - subroutine collision_mg(p) - - type(Particle), intent(inout) :: p - - ! Add to collision counter for particle - p % n_collision = p % n_collision + 1 - - ! Sample nuclide/reaction for the material the particle is in - call sample_reaction(p) - - ! Display information about collision - if (verbosity >= 10 .or. trace) then - call write_message(" " // "Energy Group = " // trim(to_str(p % g))) - end if - - end subroutine collision_mg - -!=============================================================================== -! SAMPLE_REACTION samples a nuclide based on the macroscopic cross sections for -! each nuclide within a material and then samples a reaction for that nuclide -! and calls the appropriate routine to process the physics. Note that there is -! special logic when suvival biasing is turned on since fission and -! disappearance are treated implicitly. -!=============================================================================== - - subroutine sample_reaction(p) - - type(Particle), intent(inout) :: p - - type(Material), pointer :: mat - - mat => materials(p % material) - - ! Create fission bank sites. Note that while a fission reaction is sampled, - ! it never actually "happens", i.e. the weight of the particle does not - ! change when sampling fission sites. The following block handles all - ! absorption (including fission) - - if (mat % fissionable) then - if (run_mode == MODE_EIGENVALUE) then - call create_fission_sites(p, fission_bank, n_bank) - elseif (run_mode == MODE_FIXEDSOURCE .and. create_fission_neutrons) then - call create_fission_sites(p, p % secondary_bank, p % n_secondary) - end if - end if - - ! If survival biasing is being used, the following subroutine adjusts the - ! weight of the particle. Otherwise, it checks to see if absorption occurs - - if (material_xs % absorption > ZERO) then - call absorption(p) - else - p % absorb_wgt = ZERO - end if - if (.not. p % alive) return - - ! Sample a scattering reaction and determine the secondary energy of the - ! exiting neutron - call scatter(p) - - ! Play russian roulette if survival biasing is turned on - if (survival_biasing) then - call russian_roulette(p) - if (.not. p % alive) return - end if - - end subroutine sample_reaction - -!=============================================================================== -! ABSORPTION -!=============================================================================== - - subroutine absorption(p) - - type(Particle), intent(inout) :: p - - if (survival_biasing) then - ! Determine weight absorbed in survival biasing - p % absorb_wgt = (p % wgt * & - material_xs % absorption / material_xs % total) - - ! Adjust weight of particle by probability of absorption - p % wgt = p % wgt - p % absorb_wgt - p % last_wgt = p % wgt - - ! Score implicit absorption estimate of keff -!$omp atomic - global_tallies(RESULT_VALUE, K_ABSORPTION) = & - global_tallies(RESULT_VALUE, K_ABSORPTION) + p % absorb_wgt * & - material_xs % nu_fission / material_xs % absorption - else - ! See if disappearance reaction happens - if (material_xs % absorption > prn() * material_xs % total) then - ! Score absorption estimate of keff -!$omp atomic - global_tallies(RESULT_VALUE, K_ABSORPTION) = & - global_tallies(RESULT_VALUE, K_ABSORPTION) + p % wgt * & - material_xs % nu_fission / material_xs % absorption - - p % alive = .false. - p % event = EVENT_ABSORB - end if - end if - - end subroutine absorption - -!=============================================================================== -! SCATTER -!=============================================================================== - - subroutine scatter(p) - - type(Particle), intent(inout) :: p - - call sample_scatter_c(p % material, p % last_g, p % g, p % mu, & - p % wgt, p % coord(1) % uvw) - - ! Update energy value for downstream compatability (in tallying) - p % E = energy_bin_avg(p % g) - - ! Set event component - p % event = EVENT_SCATTER - - end subroutine scatter - -!=============================================================================== -! CREATE_FISSION_SITES determines the average total, prompt, and delayed -! neutrons produced from fission and creates appropriate bank sites. -!=============================================================================== - - subroutine create_fission_sites(p, bank_array, size_bank) - type(Particle), intent(inout) :: p - type(Bank), intent(inout) :: bank_array(:) - integer(8), intent(inout) :: size_bank - - integer :: nu_d(MAX_DELAYED_GROUPS) ! number of delayed neutrons born - integer :: i ! loop index - integer :: dg ! delayed group - integer :: gout ! group out - integer :: nu ! actual number of neutrons produced - real(8) :: nu_t ! total nu - real(8) :: mu ! fission neutron angular cosine - real(8) :: phi ! fission neutron azimuthal angle - real(8) :: weight ! weight adjustment for ufs method - - interface - function ufs_get_weight(p) result(weight) bind(C) - import Particle, C_DOUBLE - type(Particle), intent(in) :: p - real(C_DOUBLE) :: WEIGHT - end function - end interface - - ! TODO: Heat generation from fission - - ! If uniform fission source weighting is turned on, we increase of decrease - ! the expected number of fission sites produced - - if (ufs) then - weight = ufs_get_weight(p) - else - weight = ONE - end if - - ! Determine expected number of neutrons produced - nu_t = p % wgt / keff * weight * & - material_xs % nu_fission / material_xs % total - - ! Sample number of neutrons produced - if (prn() > nu_t - int(nu_t)) then - nu = int(nu_t) - else - nu = int(nu_t) + 1 - end if - - ! Check for bank size getting hit. For fixed source calculations, this is a - ! fatal error. For eigenvalue calculations, it just means that k-effective - ! was too high for a single batch. - if (size_bank + nu > size(bank_array)) then - if (run_mode == MODE_FIXEDSOURCE) then - call fatal_error("Secondary particle bank size limit reached. If you & - &are running a subcritical multiplication problem, k-effective & - &may be too close to one.") - else - if (master) call warning("Maximum number of sites in fission bank & - &reached. This can result in irreproducible results using different & - &numbers of processes/threads.") - end if - end if - - ! Bank source neutrons - if (nu == 0 .or. size_bank == size(bank_array)) return - - ! Initialize counter of delayed neutrons encountered for each delayed group - ! to zero. - nu_d(:) = 0 - - p % fission = .true. ! Fission neutrons will be banked - do i = int(size_bank,4) + 1, int(min(size_bank + nu, int(size(bank_array),8)),4) - ! Bank source neutrons by copying particle data - bank_array(i) % xyz = p % coord(1) % xyz - - ! Set particle as neutron - bank_array(i) % particle = NEUTRON - - ! Set weight of fission bank site - bank_array(i) % wgt = ONE/weight - - ! Sample cosine of angle -- fission neutrons are treated as being emitted - ! isotropically. - mu = TWO * prn() - ONE - - ! Sample azimuthal angle uniformly in [0,2*pi) - phi = TWO * PI * prn() - bank_array(i) % uvw(1) = mu - bank_array(i) % uvw(2) = sqrt(ONE - mu*mu) * cos(phi) - bank_array(i) % uvw(3) = sqrt(ONE - mu*mu) * sin(phi) - - ! Sample secondary energy distribution for fission reaction and set energy - ! in fission bank - call sample_fission_energy_c(p % material, p % g, dg, gout) - - bank_array(i) % E = real(gout, 8) - bank_array(i) % delayed_group = dg - - ! Set delayed group on particle too - p % delayed_group = dg - - ! Increment the number of neutrons born delayed - if (p % delayed_group > 0) then - nu_d(p % delayed_group) = nu_d(p % delayed_group) + 1 - end if - - end do - - ! increment number of bank sites - size_bank = min(size_bank + nu, int(size(bank_array),8)) - - ! Store total weight banked for analog fission tallies - p % n_bank = nu - p % wgt_bank = nu/weight - p % n_delayed_bank(:) = nu_d(:) - - end subroutine create_fission_sites - -end module physics_mg diff --git a/src/physics_mg.cpp b/src/physics_mg.cpp new file mode 100644 index 000000000..40e5ba1ae --- /dev/null +++ b/src/physics_mg.cpp @@ -0,0 +1,234 @@ +#include "openmc/physics_mg.h" + +#include +#include + +#include "xtensor/xarray.hpp" + +#include "openmc/constants.h" +#include "openmc/eigenvalue.h" +#include "openmc/error.h" +#include "openmc/material.h" +#include "openmc/math_functions.h" +#include "openmc/message_passing.h" +#include "openmc/mgxs_interface.h" +#include "openmc/physics_common.h" +#include "openmc/random_lcg.h" +#include "openmc/settings.h" +#include "openmc/simulation.h" +#include "openmc/tallies/tally.h" + +namespace openmc { + +void +collision_mg(Particle* p, const double* energy_bin_avg, + const MaterialMacroXS* material_xs) +{ + // Add to the collision counter for the particle + p->n_collision++; + + // Sample the reaction type + sample_reaction(p, energy_bin_avg, material_xs); + + // Display information about collision + if ((settings::verbosity >= 10) || (simulation::trace)) { + std::stringstream msg; + msg << " Energy Group = " << p->g; + write_message(msg, 1); + } +} + +void +sample_reaction(Particle* p, const double* energy_bin_avg, + const MaterialMacroXS* material_xs) +{ + // Create fission bank sites. Note that while a fission reaction is sampled, + // it never actually "happens", i.e. the weight of the particle does not + // change when sampling fission sites. The following block handles all + // absorption (including fission) + + if (materials[p->material - 1]->fissionable) { + if (settings::run_mode == RUN_MODE_EIGENVALUE) { + Bank* result_bank; + int64_t result_bank_size; + // Get pointer to fission bank from Fortran side + openmc_fission_bank(&result_bank, &result_bank_size); + create_fission_sites(p, result_bank, &n_bank, result_bank_size, + material_xs); + } else if ((settings::run_mode == RUN_MODE_FIXEDSOURCE) && + (settings::create_fission_neutrons)) { + create_fission_sites(p, p->secondary_bank, &(p->n_secondary), + MAX_SECONDARY, material_xs); + } + } + + // If survival biasing is being used, the following subroutine adjusts the + // weight of the particle. Otherwise, it checks to see if absorption occurs. + if (material_xs->absorption > 0.) { + absorption(p, material_xs); + } else { + p->absorb_wgt = 0.; + } + if (!p->alive) return; + + // Sample a scattering event to determine the energy of the exiting neutron + scatter(p, energy_bin_avg); + + // Play Russian roulette if survival biasing is turned on + if (settings::survival_biasing) { + russian_roulette(p); + if (!p->alive) return; + } +} + +void +scatter(Particle* p, const double* energy_bin_avg) +{ + // Adjust indices for Fortran to C++ indexing + // TODO: Remove when no longer needed + int gin = p->last_g - 1; + int gout = p->g - 1; + int i_mat = p->material - 1; + macro_xs[i_mat].sample_scatter(gin, gout, p->mu, p->wgt); + + // Adjust return value for fortran indexing + // TODO: Remove when no longer needed + p->g = gout + 1; + + // Rotate the angle + rotate_angle_c(p->coord[0].uvw, p->mu, nullptr); + + // Update energy value for downstream compatability (in tallying) + p->E = energy_bin_avg[gout]; + + // Set event component + p->event = EVENT_SCATTER; +} + +void +create_fission_sites(Particle* p, Bank* bank_array, int64_t* size_bank, + int64_t bank_array_size, const MaterialMacroXS* material_xs) +{ + // TODO: Heat generation from fission + + // If uniform fission source weighting is turned on, we increase or decrease + // the expected number of fission sites produced + double weight = settings::ufs_on ? ufs_get_weight(p) : 1.0; + + // Determine the expected number of neutrons produced + double nu_t = p->wgt / simulation::keff * weight * material_xs->nu_fission / + material_xs->total; + + // Sample the number of neutrons produced + int nu = static_cast(nu_t); + if (prn() <= (nu_t - int(nu_t))) { + nu++; + } + + // Check for the bank size getting hit. For fixed source calculations, this + // is a fatal error; for eigenvalue calculations, it just means that k-eff + // was too high for a single batch. + if (*size_bank + nu > bank_array_size) { + if (settings::run_mode == RUN_MODE_FIXEDSOURCE) { + throw std::runtime_error{"Secondary particle bank size limit reached." + " If you are running a subcritical multiplication problem," + " k-effective may be too close to one."}; + } else { + if (mpi::master) { + std::stringstream msg; + msg << "Maximum number of sites in fission bank reached. This can" + " result in irreproducible results using different numbers of" + " processes/threads."; + warning(msg); + } + } + } + + // Begin banking the source neutrons + // First, if our bank is full then don't continue + if ((nu == 0) || (*size_bank == bank_array_size)) return; + + // Initialize the counter of delayed neutrons encountered for each delayed + // group. + double nu_d[MAX_DELAYED_GROUPS] = {0.}; + + p->fission = true; + for (size_t i = static_cast(*size_bank); + i < static_cast(std::min(*size_bank + nu, bank_array_size)); i++) { + // Bank source neutrons by copying the particle data + bank_array[i].xyz[0] = p->coord[0].xyz[0]; + bank_array[i].xyz[1] = p->coord[0].xyz[1]; + bank_array[i].xyz[2] = p->coord[0].xyz[2]; + + // Set that the bank particle is a neutron + bank_array[i].particle = static_cast(ParticleType::neutron); + + // Set the weight of the fission bank site + bank_array[i].wgt = 1. / weight; + + // Sample the cosine of the angle, assuming fission neutrons are emitted + // isotropically + double mu = 2. * prn() - 1.; + + // Sample the azimuthal angle uniformly in [0, 2.pi) + double phi = 2. * PI * prn(); + bank_array[i].uvw[0] = mu; + bank_array[i].uvw[1] = std::sqrt(1. - mu * mu) * std::cos(phi); + bank_array[i].uvw[2] = std::sqrt(1. - mu * mu) * std::sin(phi); + + // Sample secondary energy distribution for the fission reaction and set + // the energy in the fission bank + int dg; + int gout; + macro_xs[p->material - 1].sample_fission_energy(p->g - 1, dg, gout); + bank_array[i].E = static_cast(gout + 1); + bank_array[i].delayed_group = dg + 1; + + // Set the delayed group on the particle as well + p->delayed_group = dg + 1; + + // Increment the number of neutrons born delayed + if (p->delayed_group > 0) { + nu_d[dg]++; + } + } + + // Increment number of bank sites + *size_bank = std::min(*size_bank + nu, bank_array_size); + + // Store the total weight banked for analog fission tallies + p->n_bank = nu; + p->wgt_bank = nu / weight; + for (size_t d = 0; d < MAX_DELAYED_GROUPS; d++) { + p->n_delayed_bank[d] = nu_d[d]; + } +} + +void +absorption(Particle* p, const MaterialMacroXS* material_xs) +{ + if (settings::survival_biasing) { + // Determine weight absorbed in survival biasing + p->absorb_wgt = p->wgt * material_xs->absorption / material_xs->total; + + // Adjust weight of particle by the probability of absorption + p->wgt -= p->absorb_wgt; + p->last_wgt = p->wgt; + + // Score implicit absorpion estimate of keff +#pragma omp atomic + global_tally_absorption += p->absorb_wgt * material_xs->nu_fission / + material_xs->absorption; + } else { + if (material_xs->absorption > prn() * material_xs->total) { +#pragma omp atomic + global_tally_absorption += p->wgt * material_xs->nu_fission / + material_xs->absorption; + p->alive = false; + p->event = EVENT_ABSORB; + } + + } +} + +} //namespace openmc diff --git a/src/scattdata.cpp b/src/scattdata.cpp index 8d7138624..21e7f9df7 100644 --- a/src/scattdata.cpp +++ b/src/scattdata.cpp @@ -316,8 +316,8 @@ ScattDataLegendre::update_max_val() } // Calculate probability - double f = evaluate_legendre_c(dist[gin][i_gout].size() - 1, - dist[gin][i_gout].data(), mu); + double f = evaluate_legendre(dist[gin][i_gout].size() - 1, + dist[gin][i_gout].data(), mu); // if this is a new maximum, store it if (f > max_val[gin][i_gout]) max_val[gin][i_gout] = f; @@ -339,8 +339,8 @@ ScattDataLegendre::calc_f(int gin, int gout, double mu) f = 0.; } else { int i_gout = gout - gmin[gin]; - f = evaluate_legendre_c(dist[gin][i_gout].size() - 1, - dist[gin][i_gout].data(), mu); + f = evaluate_legendre(dist[gin][i_gout].size() - 1, + dist[gin][i_gout].data(), mu); } return f; } @@ -881,8 +881,8 @@ convert_legendre_to_tabular(ScattDataLegendre& leg, ScattDataTabular& tab, tab.fmu[gin][i_gout].resize(n_mu); for (int imu = 0; imu < n_mu; imu++) { tab.fmu[gin][i_gout][imu] = - evaluate_legendre_c(leg.dist[gin][i_gout].size() - 1, - leg.dist[gin][i_gout].data(), tab.mu[imu]); + evaluate_legendre(leg.dist[gin][i_gout].size() - 1, + leg.dist[gin][i_gout].data(), tab.mu[imu]); } // Ensure positivity diff --git a/src/secondary_nbody.cpp b/src/secondary_nbody.cpp index 45f6d3cb8..dabd9de3a 100644 --- a/src/secondary_nbody.cpp +++ b/src/secondary_nbody.cpp @@ -32,13 +32,13 @@ void NBodyPhaseSpace::sample(double E_in, double& E_out, double& mu) const double E_max = (Ap - 1.0)/Ap * (A_/(A_ + 1.0)*E_in + Q_); // x is essentially a Maxwellian distribution - double x = maxwell_spectrum_c(1.0); + double x = maxwell_spectrum(1.0); double y; double r1, r2, r3, r4, r5, r6; switch (n_bodies_) { case 3: - y = maxwell_spectrum_c(1.0); + y = maxwell_spectrum(1.0); break; case 4: r1 = prn(); diff --git a/src/settings.cpp b/src/settings.cpp index b5f2bf5c6..bf97e44b8 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -17,6 +17,7 @@ #include "openmc/mesh.h" #include "openmc/output.h" #include "openmc/random_lcg.h" +#include "openmc/simulation.h" #include "openmc/source.h" #include "openmc/string_utils.h" #include "openmc/xml_interface.h" @@ -57,7 +58,7 @@ bool urr_ptables_on {true}; bool write_all_tracks {false}; bool write_initial_source {false}; bool dagmc {false}; - + std::string path_cross_sections; std::string path_input; std::string path_multipole; @@ -218,7 +219,7 @@ void read_settings_xml() fatal_error("DAGMC mode unsupported for this build of OpenMC"); } #endif - + // To this point, we haven't displayed any output since we didn't know what // the verbosity is. Now that we checked for it, show the title if necessary if (openmc_master) { @@ -393,14 +394,14 @@ void read_settings_xml() // Number of OpenMP threads if (check_for_node(root, "threads")) { #ifdef _OPENMP - if (openmc_n_threads == 0) { - openmc_n_threads = std::stoi(get_node_value(root, "threads")); - if (openmc_n_threads < 1) { + if (simulation::n_threads == 0) { + simulation::n_threads = std::stoi(get_node_value(root, "threads")); + if (simulation::n_threads < 1) { std::stringstream msg; - msg << "Invalid number of threads: " << openmc_n_threads; + msg << "Invalid number of threads: " << simulation::n_threads; fatal_error(msg); } - omp_set_num_threads(openmc_n_threads); + omp_set_num_threads(simulation::n_threads); } #else if (openmc_master) warning("OpenMC was not compiled with OpenMP support; " @@ -414,7 +415,7 @@ void read_settings_xml() omp_set_num_threads(1); } #endif - + // ========================================================================== // EXTERNAL SOURCE diff --git a/src/simulation.F90 b/src/simulation.F90 index 356cca1a8..e744f1ea8 100644 --- a/src/simulation.F90 +++ b/src/simulation.F90 @@ -11,7 +11,7 @@ module simulation use cmfd_header, only: cmfd_on use constants, only: ZERO use eigenvalue, only: calculate_average_keff, calculate_generation_keff, & - synchronize_bank, keff_generation, k_sum + k_sum #ifdef _OPENMP use eigenvalue, only: join_bank_from_threads #endif @@ -57,6 +57,9 @@ module simulation subroutine initialize_source() bind(C) end subroutine + subroutine initialize_generation() bind(C) + end subroutine + function sample_external_source() result(site) bind(C) import Bank type(Bank) :: site @@ -222,30 +225,6 @@ contains end subroutine initialize_batch -!=============================================================================== -! INITIALIZE_GENERATION -!=============================================================================== - - subroutine initialize_generation() - - interface - subroutine ufs_count_sites() bind(C) - end subroutine - end interface - - if (run_mode == MODE_EIGENVALUE) then - ! Reset number of fission bank sites - n_bank = 0 - - ! Count source sites if using uniform fission source weighting - if (ufs) call ufs_count_sites() - - ! Store current value of tracklength k - keff_generation = global_tallies(RESULT_VALUE, K_TRACKLENGTH) - end if - - end subroutine initialize_generation - !=============================================================================== ! FINALIZE_GENERATION !=============================================================================== @@ -258,6 +237,9 @@ contains subroutine shannon_entropy() bind(C) end subroutine + + subroutine synchronize_bank() bind(C) + end subroutine end interface ! Update global tallies with the omp private accumulation variables @@ -291,9 +273,7 @@ contains #endif ! Distribute fission bank across processors evenly - call time_bank % start() call synchronize_bank() - call time_bank % stop() ! Calculate shannon entropy if (entropy_on) call shannon_entropy() @@ -325,11 +305,13 @@ contains subroutine finalize_batch() integer(C_INT) :: err -#ifdef OPENMC_MPI - integer :: mpi_err ! MPI error code -#endif character(MAX_FILE_LEN) :: filename + interface + subroutine broadcast_triggers() bind(C) + end subroutine broadcast_triggers + end interface + ! Reduce tallies onto master process and accumulate call time_tallies % start() call accumulate_tallies() @@ -351,8 +333,7 @@ contains ! Check_triggers if (master) call check_triggers() #ifdef OPENMC_MPI - call MPI_BCAST(satisfy_triggers, 1, MPI_LOGICAL, 0, & - mpi_intracomm, mpi_err) + call broadcast_triggers() #endif if (satisfy_triggers .or. & (trigger_on .and. current_batch == n_max_batches)) then @@ -402,9 +383,6 @@ contains ! Set up tally procedure pointers call init_tally_routines() - ! Determine how much work each processor should do - call calculate_work() - ! Allocate source bank, and for eigenvalue simulations also allocate the ! fission bank call allocate_banks() @@ -435,7 +413,7 @@ contains ! Reset global variables -- this is done before loading state point (as that ! will potentially populate k_generation and entropy) current_batch = 0 - call k_generation % clear() + call k_generation_clear() call entropy_clear() need_depletion_rx = .false. @@ -472,17 +450,6 @@ contains integer(C_INT) :: err integer :: i ! loop index -#ifdef OPENMC_MPI - integer :: n ! size of arrays - integer :: mpi_err ! MPI error code - integer :: count_per_filter ! number of result values for one filter bin - real(8) :: tempr(3) ! temporary array for communication -#ifdef OPENMC_MPIF08 - type(MPI_Datatype) :: result_block -#else - integer :: result_block -#endif -#endif interface subroutine openmc_simulation_finalize_c() bind(C) @@ -490,6 +457,9 @@ contains subroutine print_overlap_check() bind(C) end subroutine print_overlap_check + + subroutine broadcast_results() bind(C) + end subroutine broadcast_results end interface err = 0 @@ -513,37 +483,7 @@ contains total_gen = total_gen + current_batch*gen_per_batch #ifdef OPENMC_MPI - ! Broadcast tally results so that each process has access to results - if (allocated(tallies)) then - do i = 1, size(tallies) - associate (results => tallies(i) % obj % results) - ! Create a new datatype that consists of all values for a given filter - ! bin and then use that to broadcast. This is done to minimize the - ! chance of the 'count' argument of MPI_BCAST exceeding 2**31 - n = size(results, 3) - count_per_filter = size(results, 1) * size(results, 2) - call MPI_TYPE_CONTIGUOUS(count_per_filter, MPI_DOUBLE, & - result_block, mpi_err) - call MPI_TYPE_COMMIT(result_block, mpi_err) - call MPI_BCAST(results, n, result_block, 0, mpi_intracomm, mpi_err) - call MPI_TYPE_FREE(result_block, mpi_err) - end associate - end do - end if - - ! Also broadcast global tally results - n = size(global_tallies) - call MPI_BCAST(global_tallies, n, MPI_DOUBLE, 0, mpi_intracomm, mpi_err) - - ! These guys are needed so that non-master processes can calculate the - ! combined estimate of k-effective - tempr(1) = k_col_abs - tempr(2) = k_col_tra - tempr(3) = k_abs_tra - call MPI_BCAST(tempr, 3, MPI_REAL8, 0, mpi_intracomm, mpi_err) - k_col_abs = tempr(1) - k_col_tra = tempr(2) - k_abs_tra = tempr(3) + call broadcast_results() #endif ! Write tally results to tallies.out @@ -573,46 +513,6 @@ contains end function openmc_simulation_finalize -!=============================================================================== -! CALCULATE_WORK determines how many particles each processor should simulate -!=============================================================================== - - subroutine calculate_work() - - integer :: i ! loop index - integer :: remainder ! Number of processors with one extra particle - integer(8) :: i_bank ! Running count of number of particles - integer(8) :: min_work ! Minimum number of particles on each proc - integer(8) :: work_i ! Number of particles on rank i - - if (.not. allocated(work_index)) allocate(work_index(0:n_procs)) - - ! Determine minimum amount of particles to simulate on each processor - min_work = n_particles/n_procs - - ! Determine number of processors that have one extra particle - remainder = int(mod(n_particles, int(n_procs,8)), 4) - - i_bank = 0 - work_index(0) = 0 - do i = 0, n_procs - 1 - ! Number of particles for rank i - if (i < remainder) then - work_i = min_work + 1 - else - work_i = min_work - end if - - ! Set number of particles - if (rank == i) work = work_i - - ! Set index into source bank for rank i - i_bank = i_bank + work_i - work_index(i+1) = i_bank - end do - - end subroutine calculate_work - !=============================================================================== ! ALLOCATE_BANKS allocates memory for the fission and source banks !=============================================================================== diff --git a/src/simulation.cpp b/src/simulation.cpp index ed2b52fb3..01fd2ebf7 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -1,10 +1,14 @@ #include "openmc/simulation.h" #include "openmc/capi.h" +#include "openmc/eigenvalue.h" #include "openmc/message_passing.h" #include "openmc/settings.h" +#include "openmc/tallies/tally.h" #include "openmc/tallies/tally_filter.h" +#include + // OPENMC_RUN encompasses all the main logic where iterations are performed // over the batches, generations, and histories in a fixed source or k-eigenvalue // calculation. @@ -29,10 +33,39 @@ namespace openmc { // Global variables //============================================================================== +namespace simulation { + +int current_batch; +int current_gen; +int64_t current_work; +double keff {1.0}; +double keff_std; +double k_col_abs {0.0}; +double k_col_tra {0.0}; +double k_abs_tra {0.0}; +double log_spacing; +int n_lost_particles {0}; +bool need_depletion_rx {false}; +int restart_batch; +bool satisfy_triggers {false}; +bool simulation_initialized {false}; +int total_gen {0}; +int64_t work; + +std::vector k_generation; std::vector work_index; +// Threadprivate variables +bool trace; //!< flag to show debug information +#ifdef _OPENMP +int n_threads {-1}; //!< number of OpenMP threads +int thread_id; //!< ID of a given thread +#endif + +} // namespace simulation + //============================================================================== -// Functions +// Non-member functions //============================================================================== void openmc_simulation_init_c() @@ -47,27 +80,17 @@ void openmc_simulation_init_c() } } -void calculate_work() +void initialize_generation() { - // Determine minimum amount of particles to simulate on each processor - int64_t min_work = settings::n_particles / mpi::n_procs; + if (settings::run_mode == RUN_MODE_EIGENVALUE) { + // Reset number of fission bank sites + n_bank = 0; - // Determine number of processors that have one extra particle - int64_t remainder = settings::n_particles % mpi::n_procs; + // Count source sites if using uniform fission source weighting + if (settings::ufs_on) ufs_count_sites(); - int64_t i_bank = 0; - work_index.reserve(mpi::n_procs); - work_index.push_back(0); - for (int i = 0; i < mpi::n_procs; ++i) { - // Number of particles for rank i - int64_t work_i = i < remainder ? min_work + 1 : min_work; - - // Set number of particles - if (mpi::rank == i) openmc_work = work_i; - - // Set index into source bank for rank i - i_bank += work_i; - work_index.push_back(i_bank); + // Store current value of tracklength k + keff_generation = global_tallies()(K_TRACKLENGTH, RESULT_VALUE); } } @@ -80,4 +103,82 @@ openmc_simulation_finalize_c() } } +int overall_generation() +{ + using namespace simulation; + return settings::gen_per_batch*(current_batch - 1) + current_gen; +} + +void calculate_work() +{ + // Determine minimum amount of particles to simulate on each processor + int64_t min_work = settings::n_particles / mpi::n_procs; + + // Determine number of processors that have one extra particle + int64_t remainder = settings::n_particles % mpi::n_procs; + + int64_t i_bank = 0; + simulation::work_index.resize(mpi::n_procs + 1); + simulation::work_index[0] = 0; + for (int i = 0; i < mpi::n_procs; ++i) { + // Number of particles for rank i + int64_t work_i = i < remainder ? min_work + 1 : min_work; + + // Set number of particles + if (mpi::rank == i) simulation::work = work_i; + + // Set index into source bank for rank i + i_bank += work_i; + simulation::work_index[i + 1] = i_bank; + } +} + +#ifdef OPENMC_MPI +void broadcast_results() { + // Broadcast tally results so that each process has access to results + for (int i = 1; i <= n_tallies; ++i) { + // Create a new datatype that consists of all values for a given filter + // bin and then use that to broadcast. This is done to minimize the + // chance of the 'count' argument of MPI_BCAST exceeding 2**31 + auto results = tally_results(i); + + auto shape = results.shape(); + int count_per_filter = shape[1] * shape[2]; + MPI_Datatype result_block; + MPI_Type_contiguous(count_per_filter, MPI_DOUBLE, &result_block); + MPI_Type_commit(&result_block); + MPI_Bcast(results.data(), shape[0], result_block, 0, mpi::intracomm); + MPI_Type_free(&result_block); + } + + // Also broadcast global tally results + auto gt = global_tallies(); + MPI_Bcast(gt.data(), gt.size(), MPI_DOUBLE, 0, mpi::intracomm); + + // These guys are needed so that non-master processes can calculate the + // combined estimate of k-effective + double temp[] {simulation::k_col_abs, simulation::k_col_tra, + simulation::k_abs_tra}; + MPI_Bcast(temp, 3, MPI_DOUBLE, 0, mpi::intracomm); + simulation::k_col_abs = temp[0]; + simulation::k_col_tra = temp[1]; + simulation::k_abs_tra = temp[2]; +} + +void broadcast_triggers() +{ + MPI_Bcast(&simulation::satisfy_triggers, 1, MPI_C_BOOL, 0, mpi::intracomm); +} +#endif + +//============================================================================== +// Fortran compatibility +//============================================================================== + +extern "C" double k_generation(int i) { return simulation::k_generation.at(i - 1); } +extern "C" int k_generation_size() { return simulation::k_generation.size(); } +extern "C" void k_generation_clear() { simulation::k_generation.clear(); } +extern "C" void k_generation_reserve(int i) { simulation::k_generation.reserve(i); } +extern "C" int64_t work_index(int rank) { return simulation::work_index[rank]; } + } // namespace openmc diff --git a/src/simulation_header.F90 b/src/simulation_header.F90 index ad5513c61..1844c8da6 100644 --- a/src/simulation_header.F90 +++ b/src/simulation_header.F90 @@ -13,84 +13,98 @@ module simulation_header ! GEOMETRY-RELATED VARIABLES ! Number of lost particles - integer(C_INT), bind(C, name='openmc_n_lost_particles') :: n_lost_particles = 0 + integer(C_INT), bind(C) :: n_lost_particles - real(8) :: log_spacing ! spacing on logarithmic grid + real(C_DOUBLE), bind(C) :: log_spacing ! spacing on logarithmic grid ! ============================================================================ ! SIMULATION VARIABLES - integer(C_INT), bind(C, name='openmc_current_batch') :: current_batch ! current batch - integer(C_INT), bind(C, name='openmc_current_gen') :: current_gen ! current generation within a batch - integer(C_INT), bind(C, name='openmc_total_gen') :: total_gen = 0 ! total number of generations simulated - logical(C_BOOL), bind(C, name='openmc_simulation_initialized') :: & - simulation_initialized = .false. - logical :: need_depletion_rx ! need to calculate depletion reaction rx? + integer(C_INT), bind(C) :: current_batch ! current batch + integer(C_INT), bind(C) :: current_gen ! current generation within a batch + integer(C_INT), bind(C) :: total_gen ! total number of generations simulated + logical(C_BOOL), bind(C) :: simulation_initialized + logical(C_BOOL), bind(C) :: need_depletion_rx ! need to calculate depletion reaction rx? ! ============================================================================ ! TALLY PRECISION TRIGGER VARIABLES - logical :: satisfy_triggers = .false. ! whether triggers are satisfied + logical(C_BOOL), bind(C) :: satisfy_triggers ! whether triggers are satisfied - integer(C_INT64_T), bind(C, name='openmc_work') :: work ! number of particles per processor - integer(C_INT64_T), allocatable :: work_index(:) ! starting index in source bank for each process - integer(C_INT64_T), bind(C, name='openmc_current_work') :: current_work ! index in source bank of current history simulated + integer(C_INT64_T), bind(C) :: work ! number of particles per processor + integer(C_INT64_T), bind(C) :: current_work ! index in source bank of current history simulated ! ============================================================================ ! K-EIGENVALUE SIMULATION VARIABLES ! Temporary k-effective values - type(VectorReal) :: k_generation ! single-generation estimates of k - real(C_DOUBLE), bind(C, name='openmc_keff') :: keff = ONE ! average k over active batches - real(C_DOUBLE), bind(C, name='openmc_keff_std') :: keff_std ! standard deviation of average k - real(8) :: k_col_abs = ZERO ! sum over batches of k_collision * k_absorption - real(8) :: k_col_tra = ZERO ! sum over batches of k_collision * k_tracklength - real(8) :: k_abs_tra = ZERO ! sum over batches of k_absorption * k_tracklength + real(C_DOUBLE), bind(C) :: keff ! average k over active batches + real(C_DOUBLE), bind(C) :: keff_std ! standard deviation of average k + real(C_DOUBLE), bind(C) :: k_col_abs ! sum over batches of k_collision * k_absorption + real(C_DOUBLE), bind(C) :: k_col_tra ! sum over batches of k_collision * k_tracklength + real(C_DOUBLE), bind(C) :: k_abs_tra ! sum over batches of k_absorption * k_tracklength ! ============================================================================ ! PARALLEL PROCESSING VARIABLES #ifdef _OPENMP - integer(C_INT), bind(C, name='openmc_n_threads') :: n_threads = NONE ! number of OpenMP threads - integer :: thread_id ! ID of a given thread + integer(C_INT), bind(C) :: n_threads ! number of OpenMP threads + integer(C_INT), bind(C) :: thread_id ! ID of a given thread #endif ! ============================================================================ ! MISCELLANEOUS VARIABLES - integer :: restart_batch + integer(C_INT), bind(C) :: restart_batch - logical(C_BOOL), bind(C, name='openmc_trace') :: trace + logical(C_BOOL), bind(C) :: trace !$omp threadprivate(trace, thread_id, current_work) interface subroutine entropy_clear() bind(C) end subroutine + + pure function overall_generation() result(gen) bind(C) + import C_INT + integer(C_INT) :: gen + end function overall_generation + + function k_generation(i) result(k) bind(C) + import C_DOUBLE, C_INT + integer(C_INT), value :: i + real(C_DOUBLE) :: k + end function + + function k_generation_size() result(sz) bind(C) + import C_INT + integer(C_INT) :: sz + end function + + subroutine k_generation_clear() bind(C) + end subroutine + + subroutine k_generation_reserve(i) bind(C) + import C_INT + integer(C_INT), value :: i + end subroutine + + function work_index(rank) result(i) bind(C) + import C_INT, C_INT64_T + integer(C_INT), value :: rank + integer(C_INT64_T) :: i + end function end interface - contains -!=============================================================================== -! OVERALL_GENERATION determines the overall generation number -!=============================================================================== - - pure function overall_generation() result(gen) bind(C) - integer(C_INT) :: gen - gen = gen_per_batch*(current_batch - 1) + current_gen - end function overall_generation - !=============================================================================== ! FREE_MEMORY_SIMULATION deallocates global arrays defined in this module !=============================================================================== subroutine free_memory_simulation() - if (allocated(work_index)) deallocate(work_index) - - call k_generation % clear() - call k_generation % shrink_to_fit() + call k_generation_clear() call entropy_clear() end subroutine free_memory_simulation diff --git a/src/source.cpp b/src/source.cpp index 666bbb783..3427579ee 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -264,17 +264,17 @@ void initialize_source() } // Read in the source bank - read_source_bank(file_id, work_index.data(), source_bank); + read_source_bank(file_id, source_bank); // Close file file_close(file_id); } else { // Generation source sites from specified distribution in user input - for (int64_t i = 0; i < openmc_work; ++i) { + for (int64_t i = 0; i < simulation::work; ++i) { // initialize random number seed - int64_t id = openmc_total_gen*settings::n_particles + - work_index[openmc::mpi::rank] + i + 1; + int64_t id = simulation::total_gen*settings::n_particles + + simulation::work_index[mpi::rank] + i + 1; set_particle_seed(id); // sample external source distribution @@ -287,7 +287,7 @@ void initialize_source() write_message("Writing out initial source...", 5); std::string filename = settings::path_output + "initial_source.h5"; hid_t file_id = file_open(filename, 'w', true); - write_source_bank(file_id, work_index.data(), source_bank); + write_source_bank(file_id, source_bank); file_close(file_id); } } @@ -364,10 +364,10 @@ extern "C" void fill_source_bank_fixedsource() int64_t n; openmc_source_bank(&source_bank, &n); - for (int64_t i = 0; i < openmc_work; ++i) { + for (int64_t i = 0; i < simulation::work; ++i) { // initialize random number seed - int64_t id = (openmc_total_gen + overall_generation()) * - settings::n_particles + work_index[openmc::mpi::rank] + i + 1; + int64_t id = (simulation::total_gen + overall_generation()) * + settings::n_particles + simulation::work_index[mpi::rank] + i + 1; set_particle_seed(id); // sample external source distribution diff --git a/src/state_point.F90 b/src/state_point.F90 index 91d0f1090..52b01557d 100644 --- a/src/state_point.F90 +++ b/src/state_point.F90 @@ -36,17 +36,15 @@ module state_point implicit none interface - subroutine write_source_bank(group_id, work_index, bank_) bind(C) + subroutine write_source_bank(group_id, bank_) bind(C) import HID_T, C_INT64_T, Bank integer(HID_T), value :: group_id - integer(C_INT64_T), intent(in) :: work_index(*) type(Bank), intent(in) :: bank_(*) end subroutine write_source_bank - subroutine read_source_bank(group_id, work_index, bank_) bind(C) + subroutine read_source_bank(group_id, bank_) bind(C) import HID_T, C_INT64_T, Bank integer(HID_T), value :: group_id - integer(C_INT64_T), intent(in) :: work_index(*) type(Bank), intent(out) :: bank_(*) end subroutine read_source_bank end interface @@ -63,15 +61,13 @@ contains integer(C_INT) :: err logical :: write_source_ - integer :: i, j, k + integer :: i, j integer :: i_xs integer, allocatable :: id_array(:) integer(HID_T) :: file_id integer(HID_T) :: cmfd_group, tallies_group, tally_group, & filters_group, filter_group, derivs_group, & deriv_group, runtime_group - integer(C_INT) :: ignored_err - real(C_DOUBLE) :: k_combined(2) character(MAX_WORD_LEN), allocatable :: str_array(:) character(C_CHAR), pointer :: string(:) character(len=:, kind=C_CHAR), allocatable :: filename_ @@ -83,10 +79,14 @@ contains import HID_T integer(HID_T), value :: group end subroutine - subroutine entropy_to_hdf5(group) bind(C) + subroutine write_eigenvalue_hdf5(group) bind(C) import HID_T integer(HID_T), value :: group end subroutine + subroutine write_tally_results_nr(file_id) bind(C) + import HID_T + integer(HID_T), value :: file_id + end subroutine end interface err = 0 @@ -169,16 +169,7 @@ contains ! Write out information for eigenvalue run if (run_mode == MODE_EIGENVALUE) then - call write_dataset(file_id, "n_inactive", n_inactive) - call write_dataset(file_id, "generations_per_batch", gen_per_batch) - k = k_generation % size() - call write_dataset(file_id, "k_generation", k_generation % data(1:k)) - call entropy_to_hdf5(file_id) - call write_dataset(file_id, "k_col_abs", k_col_abs) - call write_dataset(file_id, "k_col_tra", k_col_tra) - call write_dataset(file_id, "k_abs_tra", k_abs_tra) - ignored_err = openmc_get_keff(k_combined) - call write_dataset(file_id, "k_combined", k_combined) + call write_eigenvalue_hdf5(file_id) ! Write out CMFD info if (cmfd_on) then @@ -415,11 +406,11 @@ contains time_active % get_value()) if (run_mode == MODE_EIGENVALUE) then call write_dataset(runtime_group, "synchronizing fission bank", & - time_bank % get_value()) + time_bank_elapsed()) call write_dataset(runtime_group, "sampling source sites", & - time_bank_sample % get_value()) + time_bank_sample_elapsed()) call write_dataset(runtime_group, "SEND-RECV source sites", & - time_bank_sendrecv % get_value()) + time_bank_sendrecv_elapsed()) end if call write_dataset(runtime_group, "accumulating tallies", & time_tallies % get_value()) @@ -447,7 +438,7 @@ contains if (master .or. parallel) then file_id = file_open(filename_, 'a', parallel=.true.) end if - call write_source_bank(file_id, work_index, source_bank) + call write_source_bank(file_id, source_bank) if (master .or. parallel) call file_close(file_id) end if end function openmc_statepoint_write @@ -485,134 +476,11 @@ contains file_id = file_open(filename_, 'w', parallel=.true.) call write_attribute(file_id, "filetype", 'source') end if - call write_source_bank(file_id, work_index, source_bank) + call write_source_bank(file_id, source_bank) if (master .or. parallel) call file_close(file_id) end subroutine write_source_point -!=============================================================================== -! WRITE_TALLY_RESULTS_NR -!=============================================================================== - - subroutine write_tally_results_nr(file_id) - integer(HID_T), intent(in) :: file_id - - integer :: i ! loop index - integer :: n ! number of filter bins - integer :: m ! number of score bins - integer :: n_bins ! total number of bins - integer(HID_T) :: tallies_group, tally_group - real(8), allocatable :: tally_temp(:,:,:) ! contiguous array of results - real(8), target :: global_temp(3,N_GLOBAL_TALLIES) -#ifdef OPENMC_MPI - integer :: mpi_err ! MPI error code - real(8) :: dummy ! temporary receive buffer for non-root reduces -#endif - type(TallyObject) :: dummy_tally - - ! ========================================================================== - ! COLLECT AND WRITE GLOBAL TALLIES - - if (master) then - ! Write number of realizations - call write_dataset(file_id, "n_realizations", n_realizations) - - ! Write number of global tallies - call write_dataset(file_id, "n_global_tallies", N_GLOBAL_TALLIES) - - tallies_group = open_group(file_id, "tallies") - end if - - -#ifdef OPENMC_MPI - ! Reduce global tallies - n_bins = size(global_tallies) - call MPI_REDUCE(global_tallies, global_temp, n_bins, MPI_REAL8, MPI_SUM, & - 0, mpi_intracomm, mpi_err) -#endif - - if (master) then - ! Transfer values to value on master - if (current_batch == n_max_batches .or. satisfy_triggers) then - global_tallies(:,:) = global_temp(:,:) - end if - - ! Write out global tallies sum and sum_sq - call write_dataset(file_id, "global_tallies", global_temp) - end if - - if (active_tallies % size() > 0) then - ! Indicate that tallies are on - if (master) then - call write_attribute(file_id, "tallies_present", 1) - end if - - ! Write all tally results - TALLY_RESULTS: do i = 1, n_tallies - associate (t => tallies(i) % obj) - ! Determine size of tally results array - m = size(t % results, 2) - n = size(t % results, 3) - n_bins = m*n*2 - - ! Allocate array for storing sums and sums of squares, but - ! contiguously in memory for each - allocate(tally_temp(2,m,n)) - tally_temp(1,:,:) = t % results(RESULT_SUM,:,:) - tally_temp(2,:,:) = t % results(RESULT_SUM_SQ,:,:) - - if (master) then - tally_group = open_group(tallies_group, "tally " // & - trim(to_str(t % id))) - - ! The MPI_IN_PLACE specifier allows the master to copy values into - ! a receive buffer without having a temporary variable -#ifdef OPENMC_MPI - call MPI_REDUCE(MPI_IN_PLACE, tally_temp, n_bins, MPI_REAL8, & - MPI_SUM, 0, mpi_intracomm, mpi_err) -#endif - - ! At the end of the simulation, store the results back in the - ! regular TallyResults array - if (current_batch == n_max_batches .or. satisfy_triggers) then - t % results(RESULT_SUM,:,:) = tally_temp(1,:,:) - t % results(RESULT_SUM_SQ,:,:) = tally_temp(2,:,:) - end if - - ! Put in temporary tally result - allocate(dummy_tally % results(3,m,n)) - dummy_tally % results(RESULT_SUM,:,:) = tally_temp(1,:,:) - dummy_tally % results(RESULT_SUM_SQ,:,:) = tally_temp(2,:,:) - - ! Write reduced tally results to file - call dummy_tally % write_results_hdf5(tally_group) - - ! Deallocate temporary tally result - deallocate(dummy_tally % results) - else - ! Receive buffer not significant at other processors -#ifdef OPENMC_MPI - call MPI_REDUCE(tally_temp, dummy, n_bins, MPI_REAL8, MPI_SUM, & - 0, mpi_intracomm, mpi_err) -#endif - end if - - ! Deallocate temporary copy of tally results - deallocate(tally_temp) - - if (master) call close_group(tally_group) - end associate - end do TALLY_RESULTS - - if (master) call close_group(tallies_group) - else - ! Indicate that tallies are off - if (master) call write_dataset(file_id, "tallies_present", 0) - end if - - - end subroutine write_tally_results_nr - !=============================================================================== ! LOAD_STATE_POINT !=============================================================================== @@ -632,7 +500,9 @@ contains character(MAX_WORD_LEN) :: word interface - subroutine entropy_from_hdf5() bind(C) + subroutine read_eigenvalue_hdf5(group) bind(C) + import HID_T + integer(HID_T), value :: group end subroutine end interface @@ -711,16 +581,7 @@ contains ! Read information specific to eigenvalue run if (run_mode == MODE_EIGENVALUE) then call read_dataset(int_array(1), file_id, "n_inactive") - call read_dataset(gen_per_batch, file_id, "generations_per_batch") - - n = restart_batch*gen_per_batch - call k_generation % resize(n) - call read_dataset(k_generation % data(1:n), file_id, "k_generation") - - call entropy_from_hdf5() - call read_dataset(k_col_abs, file_id, "k_col_abs") - call read_dataset(k_col_tra, file_id, "k_col_tra") - call read_dataset(k_abs_tra, file_id, "k_abs_tra") + call read_eigenvalue_hdf5(file_id) ! Take maximum of statepoint n_inactive and input n_inactive n_inactive = max(n_inactive, int_array(1)) @@ -753,13 +614,14 @@ contains ! of active cycle or inactive cycle if (restart_batch > n_inactive) then do i = n_inactive + 1, restart_batch - k_sum(1) = k_sum(1) + k_generation % data(i) - k_sum(2) = k_sum(2) + k_generation % data(i)**2 + k_sum(1) = k_sum(1) + k_generation(i) + k_sum(2) = k_sum(2) + k_generation(i)**2 end do n = gen_per_batch*n_realizations keff = k_sum(1) / n else - keff = k_generation % data(n) + n = k_generation_size() + keff = k_generation(n) end if current_batch = restart_batch @@ -820,7 +682,7 @@ contains end if ! Write out source - call read_source_bank(file_id, work_index, source_bank) + call read_source_bank(file_id, source_bank) end if diff --git a/src/state_point.cpp b/src/state_point.cpp index dba0fe1b8..63c8264a3 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -1,16 +1,20 @@ #include "openmc/state_point.h" #include +#include #include -#ifdef OPENMC_MPI -#include "mpi.h" -#endif +#include "xtensor/xbuilder.hpp" // for empty_like +#include "xtensor/xview.hpp" #include "openmc/capi.h" +#include "openmc/constants.h" #include "openmc/error.h" +#include "openmc/hdf5_interface.h" #include "openmc/message_passing.h" #include "openmc/settings.h" +#include "openmc/simulation.h" +#include "openmc/tallies/tally.h" namespace openmc { @@ -34,7 +38,7 @@ hid_t h5banktype() { void -write_source_bank(hid_t group_id, int64_t* work_index, Bank* source_bank) +write_source_bank(hid_t group_id, Bank* source_bank) { hid_t banktype = h5banktype(); @@ -46,11 +50,11 @@ write_source_bank(hid_t group_id, int64_t* work_index, Bank* source_bank) H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); // Create another data space but for each proc individually - hsize_t count[] {static_cast(openmc_work)}; + hsize_t count[] {static_cast(simulation::work)}; hid_t memspace = H5Screate_simple(1, count, nullptr); // Select hyperslab for this dataspace - hsize_t start[] {static_cast(work_index[openmc::mpi::rank])}; + hsize_t start[] {static_cast(simulation::work_index[mpi::rank])}; H5Sselect_hyperslab(dspace, H5S_SELECT_SET, start, nullptr, count, nullptr); // Set up the property list for parallel writing @@ -77,24 +81,25 @@ write_source_bank(hid_t group_id, int64_t* work_index, Bank* source_bank) // Save source bank sites since the souce_bank array is overwritten below #ifdef OPENMC_MPI - std::vector temp_source {source_bank, source_bank + openmc_work}; + std::vector temp_source {source_bank, source_bank + simulation::work}; #endif - for (int i = 0; i < openmc::mpi::n_procs; ++i) { + for (int i = 0; i < mpi::n_procs; ++i) { // Create memory space - hsize_t count[] {static_cast(work_index[i+1] - work_index[i])}; + hsize_t count[] {static_cast(simulation::work_index[i+1] - + simulation::work_index[i])}; hid_t memspace = H5Screate_simple(1, count, nullptr); #ifdef OPENMC_MPI // Receive source sites from other processes if (i > 0) - MPI_Recv(source_bank, count[0], openmc::mpi::bank, i, i, - openmc::mpi::intracomm, MPI_STATUS_IGNORE); + MPI_Recv(source_bank, count[0], mpi::bank, i, i, + mpi::intracomm, MPI_STATUS_IGNORE); #endif // Select hyperslab for this dataspace dspace = H5Dget_space(dset); - hsize_t start[] {static_cast(work_index[i])}; + hsize_t start[] {static_cast(simulation::work_index[i])}; H5Sselect_hyperslab(dspace, H5S_SELECT_SET, start, nullptr, count, nullptr); // Write data to hyperslab @@ -113,8 +118,8 @@ write_source_bank(hid_t group_id, int64_t* work_index, Bank* source_bank) #endif } else { #ifdef OPENMC_MPI - MPI_Send(source_bank, openmc_work, openmc::mpi::bank, 0, openmc::mpi::rank, - openmc::mpi::intracomm); + MPI_Send(source_bank, simulation::work, mpi::bank, 0, mpi::rank, + mpi::intracomm); #endif } #endif @@ -123,7 +128,7 @@ write_source_bank(hid_t group_id, int64_t* work_index, Bank* source_bank) } -void read_source_bank(hid_t group_id, int64_t* work_index, Bank* source_bank) +void read_source_bank(hid_t group_id, Bank* source_bank) { hid_t banktype = h5banktype(); @@ -131,20 +136,20 @@ void read_source_bank(hid_t group_id, int64_t* work_index, Bank* source_bank) hid_t dset = H5Dopen(group_id, "source_bank", H5P_DEFAULT); // Create another data space but for each proc individually - hsize_t dims[] {static_cast(openmc_work)}; + hsize_t dims[] {static_cast(simulation::work)}; hid_t memspace = H5Screate_simple(1, dims, nullptr); // Make sure source bank is big enough hid_t dspace = H5Dget_space(dset); hsize_t dims_all[1]; H5Sget_simple_extent_dims(dspace, dims_all, nullptr); - if (work_index[openmc::mpi::n_procs] > dims_all[0]) { + if (simulation::work_index[mpi::n_procs] > dims_all[0]) { fatal_error("Number of source sites in source file is less " "than number of source particles per generation."); } // Select hyperslab for each process - hsize_t start[] {static_cast(work_index[openmc::mpi::rank])}; + hsize_t start[] {static_cast(simulation::work_index[mpi::rank])}; H5Sselect_hyperslab(dspace, H5S_SELECT_SET, start, nullptr, dims, nullptr); #ifdef PHDF5 @@ -164,4 +169,110 @@ void read_source_bank(hid_t group_id, int64_t* work_index, Bank* source_bank) H5Tclose(banktype); } +void write_tally_results_nr(hid_t file_id) +{ + // ========================================================================== + // COLLECT AND WRITE GLOBAL TALLIES + + hid_t tallies_group; + if (mpi::master) { + // Write number of realizations + write_dataset(file_id, "n_realizations", n_realizations); + + // Write number of global tallies + write_dataset(file_id, "n_global_tallies", N_GLOBAL_TALLIES); + + tallies_group = open_group(file_id, "tallies"); + } + + // Get pointer to global tallies + auto gt = global_tallies(); + +#ifdef OPENMC_MPI + // Reduce global tallies + xt::xtensor gt_reduced = xt::empty_like(gt); + MPI_Reduce(gt.data(), gt_reduced.data(), gt.size(), MPI_DOUBLE, + MPI_SUM, 0, mpi::intracomm); + + // Transfer values to value on master + if (mpi::master) { + if (simulation::current_batch == settings::n_max_batches || + simulation::satisfy_triggers) { + std::copy(gt_reduced.begin(), gt_reduced.end(), gt.begin()); + } + } +#endif + + // Write out global tallies sum and sum_sq + if (mpi::master) { + write_dataset(file_id, "global_tallies", gt); + } + + for (int i = 1; i <= n_tallies; ++i) { + // Skip any tallies that are not active + bool active; + openmc_tally_get_active(i, &active); + if (!active) continue; + + if (mpi::master && !object_exists(file_id, "tallies_present")) { + write_attribute(file_id, "tallies_present", 1); + } + + // Get view of accumulated tally values + auto results = tally_results(i); + auto values_view = xt::view(results, xt::all(), xt::all(), + xt::range(RESULT_SUM, RESULT_SUM_SQ + 1)); + + // Make copy of tally values in contiguous array + xt::xtensor values = values_view; + + if (mpi::master) { + // Open group for tally + int id; + openmc_tally_get_id(i, &id); + std::string groupname {"tally " + std::to_string(id)}; + hid_t tally_group = open_group(tallies_group, groupname.c_str()); + + // The MPI_IN_PLACE specifier allows the master to copy values into + // a receive buffer without having a temporary variable +#ifdef OPENMC_MPI + MPI_Reduce(MPI_IN_PLACE, values.data(), values.size(), MPI_DOUBLE, + MPI_SUM, 0, mpi::intracomm); +#endif + + // At the end of the simulation, store the results back in the + // regular TallyResults array + if (simulation::current_batch == settings::n_max_batches || + simulation::satisfy_triggers) { + values_view = values; + } + + // Put in temporary tally result + xt::xtensor results_copy = xt::zeros_like(results); + auto copy_view = xt::view(results_copy, xt::all(), xt::all(), + xt::range(RESULT_SUM, RESULT_SUM_SQ + 1)); + copy_view = values; + + // Write reduced tally results to file + auto shape = results_copy.shape(); + write_tally_results(tally_group, shape[0], shape[1], results_copy.data()); + + close_group(tally_group); + } else { + // Receive buffer not significant at other processors +#ifdef OPENMC_MPI + MPI_Reduce(values.data(), nullptr, values.size(), MPI_REAL8, MPI_SUM, + 0, mpi::intracomm); +#endif + } + } + + if (mpi::master) { + if (!object_exists(file_id, "tallies_present")) { + // Indicate that tallies are off + write_dataset(file_id, "tallies_present", 0); + } + } +} + } // namespace openmc diff --git a/src/tallies/tally.F90 b/src/tallies/tally.F90 index a586d2150..c15325418 100644 --- a/src/tallies/tally.F90 +++ b/src/tallies/tally.F90 @@ -3778,6 +3778,11 @@ contains real(C_DOUBLE) :: val #ifdef OPENMC_MPI + interface + subroutine reduce_tally_results() bind(C) + end subroutine + end interface + ! Combine tally results onto master process if (reduce_tallies) call reduce_tally_results() #endif @@ -3821,67 +3826,6 @@ contains end subroutine accumulate_tallies -!=============================================================================== -! REDUCE_TALLY_RESULTS collects all the results from tallies onto one processor -!=============================================================================== - -#ifdef OPENMC_MPI - subroutine reduce_tally_results() - - integer :: i - integer :: n ! number of filter bins - integer :: m ! number of score bins - integer :: n_bins ! total number of bins - integer :: mpi_err ! MPI error code - real(C_DOUBLE), allocatable :: tally_temp(:,:) ! contiguous array of results - real(C_DOUBLE), allocatable :: tally_temp2(:,:) ! reduced contiguous results - real(C_DOUBLE) :: temp(N_GLOBAL_TALLIES), temp2(N_GLOBAL_TALLIES) - - do i = 1, active_tallies % size() - associate (t => tallies(active_tallies % data(i)) % obj) - - m = size(t % results, 2) - n = size(t % results, 3) - n_bins = m*n - - allocate(tally_temp(m,n), tally_temp2(m,n)) - - ! Reduce contiguous set of tally results - tally_temp = t % results(RESULT_VALUE,:,:) - call MPI_REDUCE(tally_temp, tally_temp2, n_bins, MPI_DOUBLE, & - MPI_SUM, 0, mpi_intracomm, mpi_err) - - if (master) then - ! Transfer values to value on master - t % results(RESULT_VALUE,:,:) = tally_temp2 - else - ! Reset value on other processors - t % results(RESULT_VALUE,:,:) = ZERO - end if - - deallocate(tally_temp, tally_temp2) - end associate - end do - - ! Reduce global tallies onto master - temp = global_tallies(RESULT_VALUE, :) - call MPI_REDUCE(temp, temp2, N_GLOBAL_TALLIES, MPI_DOUBLE, MPI_SUM, & - 0, mpi_intracomm, mpi_err) - if (master) then - global_tallies(RESULT_VALUE, :) = temp2 - else - global_tallies(RESULT_VALUE, :) = ZERO - end if - - ! We also need to determine the total starting weight of particles from the - ! last realization - temp(1) = total_weight - call MPI_REDUCE(temp, total_weight, 1, MPI_REAL8, MPI_SUM, & - 0, mpi_intracomm, mpi_err) - - end subroutine reduce_tally_results -#endif - !=============================================================================== ! SETUP_ACTIVE_TALLIES !=============================================================================== diff --git a/src/tallies/tally.cpp b/src/tallies/tally.cpp new file mode 100644 index 000000000..481f05edc --- /dev/null +++ b/src/tallies/tally.cpp @@ -0,0 +1,102 @@ +#include "openmc/tallies/tally.h" + +#include "openmc/capi.h" +#include "openmc/constants.h" +#include "openmc/message_passing.h" + +#include "xtensor/xadapt.hpp" +#include "xtensor/xbuilder.hpp" // for empty_like +#include "xtensor/xview.hpp" + +#include +#include + +namespace openmc { + +//============================================================================== +// Non-member functions +//============================================================================== + +adaptor_type<2> global_tallies() +{ + // Get pointer to global tallies + double* buffer; + openmc_global_tallies(&buffer); + + // Adapt into xtensor + std::array shape = {N_GLOBAL_TALLIES, 3}; + std::size_t size {3*N_GLOBAL_TALLIES}; + + return xt::adapt(buffer, size, xt::no_ownership(), shape); +} + +adaptor_type<3> tally_results(int idx) +{ + // Get pointer to tally results + double* results; + std::array shape; + openmc_tally_results(idx, &results, shape.data()); + + // Adapt array into xtensor with no ownership + std::size_t size {shape[0] * shape[1] * shape[2]}; + return xt::adapt(results, size, xt::no_ownership(), shape); +} + +#ifdef OPENMC_MPI +void reduce_tally_results() +{ + for (int i = 1; i <= n_tallies; ++i) { + // Skip any tallies that are not active + bool active; + openmc_tally_get_active(i, &active); + if (!active) continue; + + // Get view of accumulated tally values + auto results = tally_results(i); + auto values_view = xt::view(results, xt::all(), xt::all(), RESULT_VALUE); + + // Make copy of tally values in contiguous array + xt::xtensor values = values_view; + xt::xtensor values_reduced = xt::empty_like(values); + + // Reduce contiguous set of tally results + MPI_Reduce(values.data(), values_reduced.data(), values.size(), + MPI_DOUBLE, MPI_SUM, 0, mpi::intracomm); + + // Transfer values on master and reset on other ranks + if (mpi::master) { + values_view = values_reduced; + } else { + values_view = 0.0; + } + } + + // Get view of global tally values + auto gt = global_tallies(); + auto gt_values_view = xt::view(gt, xt::all(), RESULT_VALUE); + + // Make copy of values in contiguous array + xt::xtensor gt_values = gt_values_view; + xt::xtensor gt_values_reduced = xt::empty_like(gt_values); + + // Reduce contiguous data + MPI_Reduce(gt_values.data(), gt_values_reduced.data(), N_GLOBAL_TALLIES, + MPI_DOUBLE, MPI_SUM, 0, mpi::intracomm); + + // Transfer values on master and reset on other ranks + if (mpi::master) { + gt_values_view = gt_values_reduced; + } else { + gt_values_view = 0.0; + } + + // We also need to determine the total starting weight of particles from the + // last realization + double weight_reduced; + MPI_Reduce(&total_weight, &weight_reduced, 1, MPI_DOUBLE, MPI_SUM, + 0, mpi::intracomm); + if (mpi::master) total_weight = weight_reduced; +} +#endif + +} // namespace openmc diff --git a/src/tallies/tally_header.F90 b/src/tallies/tally_header.F90 index 4bb504b8c..bc9a9bf9b 100644 --- a/src/tallies/tally_header.F90 +++ b/src/tallies/tally_header.F90 @@ -137,7 +137,7 @@ module tally_header ! invalidation. Thus, we use threadprivate variables to accumulate global ! tallies and then reduce at the end of a generation. real(C_DOUBLE), public :: global_tally_collision = ZERO - real(C_DOUBLE), public :: global_tally_absorption = ZERO + real(C_DOUBLE), public, bind(C) :: global_tally_absorption = ZERO real(C_DOUBLE), public :: global_tally_tracklength = ZERO real(C_DOUBLE), public :: global_tally_leakage = ZERO !$omp threadprivate(global_tally_collision, global_tally_absorption, & @@ -153,7 +153,7 @@ module tally_header ! Normalization for statistics integer(C_INT32_T), public, bind(C) :: n_realizations = 0 ! # of independent realizations - real(8), public :: total_weight ! total starting particle weight in realization + real(C_DOUBLE), public, bind(C) :: total_weight ! total starting particle weight in realization contains @@ -688,14 +688,19 @@ contains ! allows a user to obtain in-memory tally results from Python directly. integer(C_INT32_T), intent(in), value :: index type(C_PTR), intent(out) :: ptr - integer(C_INT), intent(out) :: shape_(3) + integer(C_SIZE_T), intent(out) :: shape_(3) integer(C_INT) :: err if (index >= 1 .and. index <= size(tallies)) then associate (t => tallies(index) % obj) if (allocated(t % results)) then ptr = C_LOC(t % results(1,1,1)) - shape_(:) = shape(t % results) + + ! Note that shape is reversed since it is assumed to be used from + ! C/C++ code + shape_(1) = size(t % results, 3) + shape_(2) = size(t % results, 2) + shape_(3) = size(t % results, 1) err = 0 else err = E_ALLOCATE diff --git a/src/timer.cpp b/src/timer.cpp new file mode 100644 index 000000000..85b1eb771 --- /dev/null +++ b/src/timer.cpp @@ -0,0 +1,60 @@ +#include "openmc/timer.h" + +namespace openmc { + +//============================================================================== +// Timer implementation +//============================================================================== + +void Timer::start () +{ + running_ = true; + start_ = clock::now(); +} + +void Timer::stop() +{ + elapsed_ = elapsed(); + running_ = false; +} + +void Timer::reset() +{ + running_ = false; + elapsed_ = 0.0; +} + +double Timer::elapsed() +{ + if (running_) { + std::chrono::duration diff = clock::now() - start_; + return elapsed_ += diff.count(); + } else { + return elapsed_; + } +} + +//============================================================================== +// Global variables +//============================================================================== + +Timer time_bank; +Timer time_bank_sample; +Timer time_bank_sendrecv; + +//============================================================================== +// Fortran compatibility +//============================================================================== + +extern "C" double time_bank_elapsed() { return time_bank.elapsed(); } +extern "C" double time_bank_sample_elapsed() { return time_bank_sample.elapsed(); } +extern "C" double time_bank_sendrecv_elapsed() { return time_bank_sendrecv.elapsed(); } + +extern "C" void reset_timers() +{ + time_bank.reset(); + time_bank_sample.reset(); + time_bank_sendrecv.reset(); +} + +} // namespace openmc diff --git a/src/timer_header.F90 b/src/timer_header.F90 index a147a551d..169154696 100644 --- a/src/timer_header.F90 +++ b/src/timer_header.F90 @@ -1,9 +1,28 @@ module timer_header + use, intrinsic :: ISO_C_BINDING + use constants, only: ZERO implicit none + interface + function time_bank_elapsed() result(t) bind(C) + import C_DOUBLE + real(C_DOUBLE) :: t + end function + function time_bank_sample_elapsed() result(t) bind(C) + import C_DOUBLE + real(C_DOUBLE) :: t + end function + function time_bank_sendrecv_elapsed() result(t) bind(C) + import C_DOUBLE + real(C_DOUBLE) :: t + end function + subroutine reset_timers() bind(C) + end subroutine + end interface + !=============================================================================== ! TIMER represents a timer that can be started and stopped to measure how long ! different routines run. The intrinsic routine system_clock is used to measure @@ -29,9 +48,6 @@ module timer_header type(Timer) :: time_initialize ! timer for initialization type(Timer) :: time_read_xs ! timer for reading cross sections type(Timer) :: time_unionize ! timer for material xs-energy grid union - type(Timer) :: time_bank ! timer for fission bank synchronization - type(Timer) :: time_bank_sample ! timer for fission bank sampling - type(Timer) :: time_bank_sendrecv ! timer for fission bank SEND/RECV type(Timer) :: time_tallies ! timer for accumulate tallies type(Timer) :: time_inactive ! timer for inactive batches type(Timer) :: time_active ! timer for active batches diff --git a/src/tracking.F90 b/src/tracking.F90 index 8cf2fc05c..dc0c008d6 100644 --- a/src/tracking.F90 +++ b/src/tracking.F90 @@ -17,7 +17,6 @@ module tracking use nuclide_header use particle_header use physics, only: collision - use physics_mg, only: collision_mg use random_lcg, only: prn, prn_set_stream use settings use simulation_header @@ -33,6 +32,16 @@ module tracking implicit none + interface + subroutine collision_mg(p, energy_bin_avg, material_xs) bind(C) + import Particle, C_DOUBLE, MaterialMacroXS + type(Particle), intent(inout) :: p + real(C_DOUBLE), intent(in) :: energy_bin_avg(*) + type(MaterialMacroXS), intent(in) :: material_xs + end subroutine collision_mg + + end interface + contains !=============================================================================== @@ -226,7 +235,7 @@ contains if (run_CE) then call collision(p) else - call collision_mg(p) + call collision_mg(p, energy_bin_avg, material_xs) end if ! Score collision estimator tallies -- this is done after a collision @@ -313,7 +322,9 @@ contains real(8) :: norm ! "norm" of surface normal real(8) :: xyz(3) ! Saved global coordinate integer :: i_surface ! index in surfaces +#ifdef DAGMC integer :: i_cell ! index of new cell +#endif logical :: rotational ! if rotational periodic BC applied logical :: found ! particle found in universe? class(Surface), pointer :: surf diff --git a/src/volume_calc.F90 b/src/volume_calc.F90 index f7ded7361..5379a3b2e 100644 --- a/src/volume_calc.F90 +++ b/src/volume_calc.F90 @@ -138,16 +138,35 @@ contains integer :: min_samples ! minimum number of samples per process integer :: remainder ! leftover samples from uneven divide #ifdef OPENMC_MPI - integer :: mpi_err ! MPI error code integer :: m ! index over materials - integer :: n ! number of materials - integer, allocatable :: data(:) ! array used to send number of hits + integer(C_INT) :: n ! number of materials + integer(C_INT), allocatable :: data(:) ! array used to send number of hits #endif real(8) :: f ! fraction of hits real(8) :: var_f ! variance of fraction of hits real(8) :: volume_sample ! total volume of sampled region real(8) :: atoms(2, size(nuclides)) +#ifdef OPENMC_MPI + interface + subroutine send_int(buffer, count, dest, tag) bind(C) + import C_INT + integer(C_INT), intent(in) :: buffer + integer(C_INT), value :: count + integer(C_INT), value :: dest + integer(C_INT), value :: tag + end subroutine + + subroutine recv_int(buffer, count, source, tag) bind(C) + import C_INT + integer(C_INT), intent(out) :: buffer + integer(C_INT), value :: count + integer(C_INT), value :: source + integer(C_INT), value :: tag + end subroutine + end interface +#endif + ! Divide work over MPI processes min_samples = this % samples / n_procs remainder = mod(this % samples, n_procs) @@ -285,12 +304,10 @@ contains if (master) then #ifdef OPENMC_MPI do j = 1, n_procs - 1 - call MPI_RECV(n, 1, MPI_INTEGER, j, 0, mpi_intracomm, & - MPI_STATUS_IGNORE, mpi_err) + call recv_int(n, 1, j, 0) allocate(data(2*n)) - call MPI_RECV(data, 2*n, MPI_INTEGER, j, 1, mpi_intracomm, & - MPI_STATUS_IGNORE, mpi_err) + call recv_int(data(1), 2*n, j, 1) do k = 0, n - 1 do m = 1, master_indices(i_domain) % size() if (data(2*k + 1) == master_indices(i_domain) % data(m)) then @@ -353,8 +370,8 @@ contains data(2*k + 2) = master_hits(i_domain) % data(k + 1) end do - call MPI_SEND(n, 1, MPI_INTEGER, 0, 0, mpi_intracomm, mpi_err) - call MPI_SEND(data, 2*n, MPI_INTEGER, 0, 1, mpi_intracomm, mpi_err) + call send_int(n, 1, 0, 0) + call send_int(data(1), 2*n, 0, 1) deallocate(data) #endif end if