diff --git a/docs/source/pythonapi/examples/mdgxs-part-i.ipynb b/docs/source/pythonapi/examples/mdgxs-part-i.ipynb index 03bf15897..a7a959841 100644 --- a/docs/source/pythonapi/examples/mdgxs-part-i.ipynb +++ b/docs/source/pythonapi/examples/mdgxs-part-i.ipynb @@ -581,9 +581,9 @@ " Copyright: 2011-2016 Massachusetts Institute of Technology\n", " License: http://openmc.readthedocs.io/en/latest/license.html\n", " Version: 0.8.0\n", - " Git SHA1: e8819e6a77f2e998dcce937e80fbdd8dd430b667\n", - " Date/Time: 2016-08-02 18:51:52\n", - " MPI Processes: 4\n", + " Git SHA1: c23c1cabfbb8c726ee0b4bd01bb7e74e679edcd0\n", + " Date/Time: 2016-08-03 16:02:28\n", + " MPI Processes: 1\n", "\n", " ===========================================================================\n", " ========================> INITIALIZATION <=========================\n", @@ -669,20 +669,20 @@ "\n", " =======================> TIMING STATISTICS <=======================\n", "\n", - " Total time for initialization = 9.9200E-01 seconds\n", - " Reading cross sections = 3.8400E-01 seconds\n", - " Total time in simulation = 2.8675E+01 seconds\n", - " Time in transport only = 2.8041E+01 seconds\n", - " Time in inactive batches = 1.3230E+00 seconds\n", - " Time in active batches = 2.7352E+01 seconds\n", - " Time synchronizing fission bank = 5.6200E-01 seconds\n", - " Sampling source sites = 3.0000E-03 seconds\n", - " SEND/RECV source sites = 3.0000E-03 seconds\n", - " Time accumulating tallies = 1.0000E-02 seconds\n", - " Total time for finalization = 9.7000E-02 seconds\n", - " Total time elapsed = 2.9773E+01 seconds\n", - " Calculation Rate (inactive) = 37792.9 neutrons/second\n", - " Calculation Rate (active) = 7312.08 neutrons/second\n", + " Total time for initialization = 4.3800E-01 seconds\n", + " Reading cross sections = 2.5200E-01 seconds\n", + " Total time in simulation = 8.4618E+01 seconds\n", + " Time in transport only = 8.4594E+01 seconds\n", + " Time in inactive batches = 5.0040E+00 seconds\n", + " Time in active batches = 7.9614E+01 seconds\n", + " Time synchronizing fission bank = 6.0000E-03 seconds\n", + " Sampling source sites = 4.0000E-03 seconds\n", + " SEND/RECV source sites = 2.0000E-03 seconds\n", + " Time accumulating tallies = 4.0000E-03 seconds\n", + " Total time for finalization = 7.5000E-02 seconds\n", + " Total time elapsed = 8.5157E+01 seconds\n", + " Calculation Rate (inactive) = 9992.01 neutrons/second\n", + " Calculation Rate (active) = 2512.12 neutrons/second\n", "\n", " ============================> RESULTS <============================\n", "\n", @@ -707,7 +707,7 @@ ], "source": [ "# Run OpenMC\n", - "openmc.run(mpi_procs=4)" + "openmc.run()" ] }, { @@ -1310,7 +1310,7 @@ "data": { "image/png": "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\n771XLz/wwAO55JJLWLBgAfPmzVt9R8qOtPXkVe/EUikFl7+jrW3T5r6SGshKOGdGxNK0+HLggxGx\nK1mp5sftjtisizQ3w9KlVTfr1pYuza7D1rjjjjtYuHAhr7zyCj/72c/o379/m9ufcsopq29XfOGF\nF6617tvf/jZTp07lpptuqrhvv379GDNmDBdccAEvvvgiAP/1X//Fbrvtxq677so+++zD4YcfzoYb\nbsgWW2xRmwusoN5VYbOBYSWvtwHmlm0zC9gWmCupLzAwIt6UNDstX2dfSRuQJZVrI+KOlg0i4l8l\n2/8auKu1wJpLPv1NTU00NTXlviizeih6UmnRna6jM1VXNYuhQulk0003XavEMG/evNXPr7jiCq64\n4op19hk3bhz33Xcfjz76KA0NDW2ec/ny5bz88svsvPPO9O/fn0svvZRLL70UgF/96lfsvvvuuare\nJk2axKRJk6put448dwPr6APoC/w3MBzoBzwH7Fi2zanA5en5WLJ2FYCdgGfTfiPTcVpuTHYNWSN9\n+fm2Knl+FnBDK3FVv4Wa2XqW3aE9exRRV8Xfnf+eR4wYEQ8++OA6y48++ug4+uijY/ny5TF58uQY\nMmRIHHvssa0e5wc/+EGMGjUq5s2bt866J598Mh577LF47733YtmyZXHhhRfGgAED4vXXX4+IiDlz\n5sTcuXMjIuKJJ56IbbfdNh544IE2427tPSXnHSTXx22ADwD+DswAzknLxgOfS883Am5O658ERpTs\ne25KKNOA0WnZ3sDKlKSeBaYAB8SahPNCWvf/gC1bianNN9WsKzixdPS83fcNGzlyZMXE8vLLL8de\ne+0VjY2N8bnPfS7OPPPMNhOLpOjfv380Njauvl3xBRdcEBERjzzySOyyyy4xYMCAGDx4cDQ1NcVj\njz22et9HH300RowYEZtuumnssMMOceONN1aNu7OJxbcmNusmSmsmivjx7Kr4fWvi2vOtic3MrFup\n2ngv6ePAMcAnga2BZcBfgd8D10XEorpGaGZmhdJmVZikP5D1xLoD+AvwT6A/sD2wL3AwWSP6nfUP\ntXZcFWbdkavCOnpeV4XVWmerwqolliERsaBKAFW36W6cWKw7cmLp6HmdWGqtrm0sLQlD0qZp3i8k\nbS/pEEkblm5jZmYG+RvvHwX6SxoK3A8cC/y2XkGZmVlx5R15r4h4R9IJZIMZL5L0bD0DMzPLY/jw\n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\n", "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1373,7 +1373,7 @@ "data": { "image/png": "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\n9erFmWeeyRtvvAFAkyZNgmWqSk5ODjt37mTHjh2AnT5zwoQJFBQU0KZNGyZMmBDTNPTCCy9w3HHH\n0bp1a8aPH1+ts3/wwQc5/vjjadOmDQMHDmTdunUR21m6dCmnnHIKBQUFFBcXh/xIBw8ezF/+8peQ\n+ieddBJPP23/dNasWUP//v1p06YNxx13HE888USw3o4dOzj//PMpKCigd+/eCaU3bciULi8NboaG\nR1IVg6oeAK4Dngc+Bhao6iciUioigwFE5Mcish64CLhPRAKJfYcCfYCxIvK+iLwnIifWpXyW30JK\npdoWzS4bqX5dRJ9cv359sKOMhldzyPfff8/KlSurpfw86aSTaNq0KRdccAFXXHFFMEdDIMxugIMH\nD7JhwwZ2795dre3t27dz0UUXcdttt7Ft2zaOPvpoXnvttWD5U089xe9//3ueeuoptm7dyplnnsmI\nESMiypmXl8fcuXPZtWsXS5Ys4b777gt2/IEcDQFWrVrFpk2bGDRoEHv37qV///6MHDmSbdu2MX/+\nfK699lo++eQTAK699lqaN2/Oli1bmD17tmf/h8FgqCLpsZJU9TlVPUZVu6rq751jJar6rPP5HVXt\nqKotVfVQZ3oqqvqYqjZR1VNU9WTn73+SLW99csEFF1BYWMhZZ51Fv379IialqSlXX301J598Mv37\n9w85vmrVKnbv3s28efM444wzgscHDhzI3XffzbZt2/jqq6+CWeH27t1bre2lS5dywgkn8LOf/YxG\njRpx/fXX065du2D5Aw88wJQpU+jWrRs5OTnceOONfPDBByFJfwKcddZZQeX1ox/9iOHDh7N8+XIA\nhgwZwmeffRZ823/00UcZNmwYjRo14tlnn6Vz586MHj0aEaFHjx5ceOGFLFy4kIMHD/Lkk09yyy23\n0LRpU0444QTGjBmT4BM1GBoeJoheClm8eDE7duzgyy+/5M9//jNNmjSJe84111wTzMXw+9//PqRs\n8uTJrF69Omou58aNGzNs2DBmzJjBhx/aA7OpU6dy8skn06NHD/r06cPPfvYzcnNzOeyww6qdH57y\nEwjZLy8vZ+LEicF0nG3atEFEqmVbA3jrrbc4++yzOeyww2jVqhX3339/MOVn48aNGTp0KI8++iiq\nyvz584O5oMvLy3nzzTdDUn7OmzePLVu2sHXrViorKznyyCOD12mwwfn8JVVbBALO6Ax0z0Uk/F6y\n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\n", "text/plain": [ - "" + "" ] }, "metadata": {}, diff --git a/docs/source/pythonapi/examples/mdgxs-part-ii.ipynb b/docs/source/pythonapi/examples/mdgxs-part-ii.ipynb index bb740b7bc..7df67ce4d 100644 --- a/docs/source/pythonapi/examples/mdgxs-part-ii.ipynb +++ b/docs/source/pythonapi/examples/mdgxs-part-ii.ipynb @@ -456,7 +456,7 @@ "outputs": [ { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "" ] @@ -608,9 +608,9 @@ " Copyright: 2011-2016 Massachusetts Institute of Technology\n", " License: http://openmc.readthedocs.io/en/latest/license.html\n", " Version: 0.8.0\n", - " Git SHA1: e8819e6a77f2e998dcce937e80fbdd8dd430b667\n", - " Date/Time: 2016-08-03 00:11:15\n", - " MPI Processes: 4\n", + " Git SHA1: c23c1cabfbb8c726ee0b4bd01bb7e74e679edcd0\n", + " Date/Time: 2016-08-03 15:59:52\n", + " MPI Processes: 1\n", "\n", " ===========================================================================\n", " ========================> INITIALIZATION <=========================\n", @@ -646,61 +646,7 @@ " consistent delayed group structure across all isotopes while the JEFF\n", " 3.1.1 library has the same delayed group structure across all\n", " isotopes. Use with caution!\n", - " WARNING: A delayedgroup filter was used on a total nuclide tally. Cross section\n", - " libraries are not guaranteed to have the same delayed group structure\n", - " across all isotopes. In particular, ENDF/B-VII.1 does not have a\n", - " consistent delayed group structure across all isotopes while the JEFF\n", - " 3.1.1 library has the same delayed group structure across all\n", - " isotopes. Use with caution!\n", - " WARNING: A delayedgroup filter was used on a total nuclide tally. Cross section\n", - " libraries are not guaranteed to have the same delayed group structure\n", - " across all isotopes. In particular, ENDF/B-VII.1 does not have a\n", - " consistent delayed group structure across all isotopes while the JEFF\n", - " 3.1.1 library has the same delayed group structure across all\n", - " isotopes. Use with caution!\n", - " WARNING: A delayedgroup filter was used on a total nuclide tally. Cross section\n", - " libraries are not guaranteed to have the same delayed group structure\n", - " across all isotopes. In particular, ENDF/B-VII.1 does not have a\n", - " consistent delayed group structure across all isotopes while the JEFF\n", - " 3.1.1 library has the same delayed group structure across all\n", - " isotopes. Use with caution!\n", - " WARNING: A delayedgroup filter was used on a total nuclide tally. Cross section\n", - " libraries are not guaranteed to have the same delayed group structure\n", - " across all isotopes. In particular, ENDF/B-VII.1 does not have a\n", - " consistent delayed group structure across all isotopes while the JEFF\n", - " 3.1.1 library has the same delayed group structure across all\n", - " isotopes. Use with caution!\n", " Building neighboring cells lists for each surface...\n", - " WARNING: A delayedgroup filter was used on a total nuclide tally. Cross section\n", - " libraries are not guaranteed to have the same delayed group structure\n", - " across all isotopes. In particular, ENDF/B-VII.1 does not have a\n", - " consistent delayed group structure across all isotopes while the JEFF\n", - " 3.1.1 library has the same delayed group structure across all\n", - " isotopes. Use with caution!\n", - " WARNING: A delayedgroup filter was used on a total nuclide tally. Cross section\n", - " libraries are not guaranteed to have the same delayed group structure\n", - " across all isotopes. In particular, ENDF/B-VII.1 does not have a\n", - " consistent delayed group structure across all isotopes while the JEFF\n", - " 3.1.1 library has the same delayed group structure across all\n", - " isotopes. Use with caution!\n", - " WARNING: A delayedgroup filter was used on a total nuclide tally. Cross section\n", - " libraries are not guaranteed to have the same delayed group structure\n", - " across all isotopes. In particular, ENDF/B-VII.1 does not have a\n", - " consistent delayed group structure across all isotopes while the JEFF\n", - " 3.1.1 library has the same delayed group structure across all\n", - " isotopes. Use with caution!\n", - " WARNING: A delayedgroup filter was used on a total nuclide tally. Cross section\n", - " libraries are not guaranteed to have the same delayed group structure\n", - " across all isotopes. In particular, ENDF/B-VII.1 does not have a\n", - " consistent delayed group structure across all isotopes while the JEFF\n", - " 3.1.1 library has the same delayed group structure across all\n", - " isotopes. Use with caution!\n", - " WARNING: A delayedgroup filter was used on a total nuclide tally. Cross section\n", - " libraries are not guaranteed to have the same delayed group structure\n", - " across all isotopes. In particular, ENDF/B-VII.1 does not have a\n", - " consistent delayed group structure across all isotopes while the JEFF\n", - " 3.1.1 library has the same delayed group structure across all\n", - " isotopes. Use with caution!\n", " Initializing source particles...\n", "\n", " ===========================================================================\n", @@ -768,20 +714,20 @@ "\n", " =======================> TIMING STATISTICS <=======================\n", "\n", - " Total time for initialization = 6.4100E-01 seconds\n", - " Reading cross sections = 3.6400E-01 seconds\n", - " Total time in simulation = 2.6284E+01 seconds\n", - " Time in transport only = 2.5289E+01 seconds\n", - " Time in inactive batches = 1.3870E+00 seconds\n", - " Time in active batches = 2.4897E+01 seconds\n", - " Time synchronizing fission bank = 3.9900E-01 seconds\n", - " Sampling source sites = 1.0000E-03 seconds\n", + " Total time for initialization = 4.1300E-01 seconds\n", + " Reading cross sections = 2.3300E-01 seconds\n", + " Total time in simulation = 7.3036E+01 seconds\n", + " Time in transport only = 7.2812E+01 seconds\n", + " Time in inactive batches = 4.9340E+00 seconds\n", + " Time in active batches = 6.8102E+01 seconds\n", + " Time synchronizing fission bank = 5.0000E-03 seconds\n", + " Sampling source sites = 4.0000E-03 seconds\n", " SEND/RECV source sites = 0.0000E+00 seconds\n", - " Time accumulating tallies = 5.6100E-01 seconds\n", - " Total time for finalization = 1.1000E-02 seconds\n", - " Total time elapsed = 2.6944E+01 seconds\n", - " Calculation Rate (inactive) = 18024.5 neutrons/second\n", - " Calculation Rate (active) = 4016.55 neutrons/second\n", + " Time accumulating tallies = 2.0400E-01 seconds\n", + " Total time for finalization = 7.0000E-03 seconds\n", + " Total time elapsed = 7.3477E+01 seconds\n", + " Calculation Rate (inactive) = 5066.88 neutrons/second\n", + " Calculation Rate (active) = 1468.39 neutrons/second\n", "\n", " ============================> RESULTS <============================\n", "\n", @@ -806,7 +752,7 @@ ], "source": [ "# Run OpenMC\n", - "openmc.run(mpi_procs=4)" + "openmc.run()" ] }, { @@ -1304,7 +1250,7 @@ { "data": { "text/plain": [ - "" + "" ] }, "execution_count": 24, @@ -1315,7 +1261,7 @@ "data": { "image/png": "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\nDmZmZmZmZmbW5TzgYGZmZmZmZmZdzgMOZmZmZmZmZtblPOBgZmZmZmZmZl3OAw5mZmZmZmZm1uU8\n4GBmZmZmZmZmXc4DDmZmZmZmZmbW5TzgYGZmZmZmZmZdzgMOZmZmZmZmZtblPOBgZmZmZmZmZl3O\nAw5mZmZmZmZm1uU84GBmZmZmZmZmXc4DDusJSZMlnZlx2ycl7d/dfapqcz9JT/Vkm2ZmPck5bGZW\nPmexWXPxgEMdkuZLelXSy5Kel3S9pJ0y1jpIaouyO9CI9N91ZdZfYmbWGOdwt1gnc7jqZ+FlSTeV\n3Sez3sJZ3C3WySwGkHSKpCck/UvSo5KGlt0na24ecKgvgEMiYnNgB+AfwIUZa8U6HCTNTlJrCW32\nAS4A7u7pts16Medwkyohh1f9LKSP0T3cvllv5ixuUj2dxZI+C3wGOCgiNgU+DPxfT/bB1j0ecOiY\nACLidWAqsOuqFVJfSedKWiDpGUk/krShpI2BG4EdJf0zHQ3uL+ldkv4iaamkpyVdmP4RW6xj0h6S\n7pP0kqQpwEZV6z8s6YG0vTskvb3Ofur2S9JFks6t2n6apC+mz3eQNFXSPyT9XdIXKrbbSNLPJb0g\n6RHgXZ28n1GS5qb9+KGkGZKOTdeNS9/D+ZKeB8Yr8Y101P3ZtK3N0u3XGk2vnDInabyk30iakv77\n/FXS7p185F8GbgbmdrKdmXUt57BzeNUuOllvZt3HWdzLs1iSgG8CX4qIxwAi4smIeLGj92PmAYcM\n0sD8BHBXxeJzgKHA7ul/dwS+GRGvAgcBiyNis/SbmGeBNuC/gH7A3sD+wIkF+7MBcA1wWbq/3wAf\nq1i/JzAJOD5d/xNgWlpXraN+XQaMrdjv1un6K9PQuR54gGS0+wDgFEkfSjefALwxfRwIjOvg/Wyd\nvofTgK2Bx9K+VHo38DdgW+DbJKOrnwL2A94EbAb8sGL7zkbTDwV+DWwF/Aq4VnVGiSUNTts7Ex/w\nmpXCObxqv70yh1NXSFoi6aYMgxNm1g2cxav22xuzeED6eLukhenAyoRO9m0GEeFHjQfwJPAy8AKw\nHFgE7Fax/l/AGyte7w08kT7fD1jYyf5PAa4u2Lf3AYuqlt0JnJk+vxiYWLV+LvC+ive2f5Z+AY8C\nB6TPTwJuSJ+/G5hfVXs6MCl9/nfgQxXrjq/3mQCfBO6sWrYQODZ9Pq5GW7cC/1nx+i3AMpJBtLU+\n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\neuPVxnRsyZiOLXtfXzzliVq7DchEZX2NMUvHOIJsgsgeRwPXR0Q38Likm4E3A0tqfjgzsyGkH+Zw\nMDOzJjmL81MaejdwJ5Ci68FaP07W176qu9y5StTd+sY3ljrXL+I9hWu+1H5yqXM92r1P8XPFfxWu\nmfqtEwvXAHzg5FmFa8ZFuZVVfhUdxWs+/P5S5/rsles1HDZ0/kFnFK55501zCtcAfDr+t3DNR9uu\nISKK/Q+ZSIpbYkyuff9R89c7T/rL/31kk0b+mWy82VEV3b2QdALwhog4QdJE4PCI6Jk08jLgLWRD\nKW4ARkdESPoh8ERETKpxzYcCp0XEARXbTgX2iIjjJW2ermNCRNyb/9vYsJTJYYB2rWq803rnKpff\n83b+h8I1v4x3lzrXf7SfX7hmzROvKH6erc5uvFMN519SPEfGHzez1LneyW8L18yOw0qd6zefKP7n\n5umXTm68U5WvvH9K4RqAg352beGa4+KSwjWv4U0c2HZWqSwuksNQO4utdUrdE5fI4excxbO4TA5D\nuSz+j/avFa5Z88SrCtcAfHar4vdzF15yaqlzlcniMjkM5bJ4sHIYymVxmRyGclncynvijY17OJhZ\nbkNtvFqOMWYTWHc4BcB3gGmSehoYpr6cGxvM7OXFQyrMzFrPWZyfGxzMLLcm1xzu9/FqjcaYRcR6\nM5tHxPP1zmFmNtQ1m8NmZtY8Z3F+bnAws9w8Xs3MrLWcw2Zmrecszs8NDmaWW7Prv5uZWXOcw2Zm\nrecszs/flJnl5vFqZmat5Rw2M2s9Z3F+bnAws9wcrmZmreUcNjNrPWdxfm5wMLPcPF7NzKy1nMNm\nZq3nLM7PDQ5mlpvHq5mZtZZz2Mys9ZzF+fmbMrPc3H3MzKy1nMNmZq3nLM7PDQ5mltsqrzlsZtZS\nzmEzs9ZzFufnBgczy83dx8zMWss5bGbWes7i/PxNmVlu7j5mZtZazmEzs9ZzFufnBgczy83hambW\nWs5hM7PWcxbnNygNDjfv8qZC+4+PGaXOc7WOKlzTdt0fSp1Ls6NwTXeXSp1r2zX3FK5ZOfPvC9d0\nn1S4BID2X36ocM0XDvxyqXN9QV8rXNM2rtz3fsH3Ti9c0/3L4uc5jVuLFwG/VkeJqmtKnauHw3XD\nddsu+xSu+UBcXbhmto4sXAPQNv++wjW6pHgOQ7ks3p6HCtc8celOhWsAuo8rXtP+mwmlzvXl/b9Y\n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\n+8b/K1zTdUrx/wY/ru8XrgH4Il8uXLOHlhERxS+S7P/ltuXVoxVq695hi5rnSctiXsDaZTG/Urn2\nu6RNyVaQeBNp7feIWJJqzyBbxWI1a5fFfDvwG+AesiEWAZyZllRD0jTgloi4uOo6jgbOBLqBn0bE\nGcW+jQ1LmRwG0CXFs6fr7aX+82KbrurOLo1t2fZsqXM9pNcXrml/sPj3d96unylcA/BZ/W/hmrZa\nzW05aFLx/Onq+mSpc02Pmqss9ukT515euKbrtHL/DR5b4ns/my8WrtmUA3m1ZpXK4iI5DPWz2Fqj\n1D1xiRyGcllcJoehXBYPVg5DuSwuk8NQLovL5DCUy+LBymEol8VlchjgLM4qXLOrHm/pPfHGxEMq\nzCy37q7mIqO/136PiJuh/jTBEXFsne0zgBm5L9zMbIhoNofNzKx5zuL8/E2ZWX7uGmZm1lrOYTOz\n1nMW5+YGBzPLz+FqZtZazmEzs9ZzFufmBgczy2/NRj0Ezcys9ZzDZmat5yzOzQ0OZpbfmlZfgJnZ\nRs45bGbWes7i3Lwsppnl92LOh5mZDYy8OewsNjMbOE3msKRDJS2SdL+k02q8P1zSTEmLJd0iaVTF\ne2ek7QslHdLomJKmSXpQ0l2S5knaJ23fQ9LvJb0oaVLF/ptKui3tf4+k3gney3APBzPLb3WrL8DM\nbCPnHDYza70mslhSG3AhcBDwGDBX0jURsahit+OBpyJitKQJwLnAREl7ka3oticwErhR0mhADY55\nSkRcXXUpTwL/DhxeuTEiVkk6ICJekNQO3Czp5xFxe5nP6x4OZpZfV86HmZkNjLw57Cw2Mxs4zeXw\nOGBxRDwcEauBmcD4qn3GA5em57OAA9Pzw4CZEbEmIpYAi9PxGh1zvb/3R8QTEXEnNQaIRMQL6emm\nZJ0Uou6nacANDmaW35qcDzMzGxh5c9hZbGY2cJrL4R2BpRWvl6VtNfeJiC7gGUlb16h9NG1rdMyz\nJc2XdJ6kTRp9PEltku4ClgM3RMTcRjX1uMHBzPLzTa6ZWWv1Q4PDAI0dnipphaQ/VB3r3LTvfElX\nStoybd8vjQ/ueRyeto+UdJOkBWns8EllvyozswHTXA7XWuKiugdBvX2Kbgc4PSL2BPYDtgHWy/31\nCiO6I+JNZMM23pKGcpTiORzMLD83JpiZtVaTOTwQY4cjIoBpwLeBH1adcg7ZzW63pK8AZ6THPcC+\n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\n", "text/plain": [ - "" + "" ] }, "metadata": {}, diff --git a/openmc/filter.py b/openmc/filter.py index 9e05cff63..256674af3 100644 --- a/openmc/filter.py +++ b/openmc/filter.py @@ -162,6 +162,11 @@ class Filter(object): if not isinstance(bins, Iterable): bins = [bins] + # If the bin is 0D numpy array, promote to 1D + elif isinstance(bins, np.ndarray): + if bins.shape == (): + bins.shape = (1,) + # If the bins are in a collection, convert it to a list else: bins = list(bins) diff --git a/src/cmfd_data.F90 b/src/cmfd_data.F90 index c07569ae5..bda12f27b 100644 --- a/src/cmfd_data.F90 +++ b/src/cmfd_data.F90 @@ -6,6 +6,7 @@ module cmfd_data !============================================================================== use constants + use tally_filter, only: MeshFilter implicit none private @@ -94,7 +95,10 @@ contains ! Associate tallies and mesh t => cmfd_tallies(1) - i_mesh = t % filters(t % find_filter(FILTER_MESH)) % int_bins(1) + select type(filt => t % filters(t % find_filter(FILTER_MESH)) % obj) + type is (MeshFilter) + i_mesh = filt % mesh + end select m => meshes(i_mesh) ! Set mesh widths @@ -109,7 +113,10 @@ contains ! Associate tallies and mesh t => cmfd_tallies(ital) - i_mesh = t % filters(t % find_filter(FILTER_MESH)) % int_bins(1) + select type(filt => t % filters(t % find_filter(FILTER_MESH)) % obj) + type is (MeshFilter) + i_mesh = filt % mesh + end select m => meshes(i_mesh) i_filter_mesh = t % find_filter(FILTER_MESH) @@ -138,7 +145,7 @@ contains TALLY: if (ital == 1) then ! Reset all bins to 1 - matching_bins(1:t%n_filters) = 1 + matching_bins(1:size(t % filters)) = 1 ! Set ijk as mesh indices ijk = (/ i, j, k /) @@ -152,7 +159,8 @@ contains end if ! Calculate score index from bins - score_index = sum((matching_bins(1:t%n_filters) - 1) * t%stride) + 1 + score_index = sum((matching_bins(1:size(t % filters)) - 1) & + * t%stride) + 1 ! Get flux flux = t % results(1,score_index) % sum @@ -181,7 +189,7 @@ contains INGROUP: do g = 1, ng ! Reset all bins to 1 - matching_bins(1:t%n_filters) = 1 + matching_bins(1:size(t % filters)) = 1 ! Set ijk as mesh indices ijk = (/ i, j, k /) @@ -198,7 +206,8 @@ contains end if ! Calculate score index from bins - score_index = sum((matching_bins(1:t%n_filters) - 1) * t%stride) + 1 + score_index = sum((matching_bins(1:size(t % filters)) - 1) & + * t%stride) + 1 ! Get scattering cmfd % scattxs(h,g,i,j,k) = t % results(1,score_index) % sum /& @@ -220,7 +229,7 @@ contains else if (ital == 3) then ! Initialize and filter for energy - matching_bins(1:t%n_filters) = 1 + matching_bins(1:size(t % filters)) = 1 if (i_filter_ein > 0) then matching_bins(i_filter_ein) = ng - h + 1 end if @@ -229,14 +238,16 @@ contains matching_bins(i_filter_mesh) = mesh_indices_to_bin(m, & (/ i, j, k /)) matching_bins(i_filter_surf) = OUT_LEFT - score_index = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 ! outgoing + score_index = sum((matching_bins(1:size(t % filters)) - 1) & + * t%stride) + 1 ! outgoing cmfd % current(1,h,i,j,k) = t % results(1,score_index) % sum if (i > 1) then matching_bins(i_filter_mesh) = mesh_indices_to_bin(m, & (/ i-1, j, k /)) matching_bins(i_filter_surf) = OUT_RIGHT - score_index = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 ! incoming + score_index = sum((matching_bins(1:size(t % filters)) - 1) & + * t % stride) + 1 ! incoming cmfd % current(2,h,i,j,k) = t % results(1,score_index) % sum end if @@ -245,29 +256,32 @@ contains matching_bins(i_filter_mesh) = mesh_indices_to_bin(m, & (/ i+1, j, k /) ) matching_bins(i_filter_surf) = OUT_LEFT - score_index = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 ! incoming + score_index = sum((matching_bins(1:size(t % filters)) - 1) & + * t % stride) + 1 ! incoming cmfd % current(3,h,i,j,k) = t % results(1,score_index) % sum end if matching_bins(i_filter_mesh) = mesh_indices_to_bin(m, & (/ i, j, k /) ) matching_bins(i_filter_surf) = OUT_RIGHT - score_index = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 ! outgoing + score_index = sum((matching_bins(1:size(t % filters)) - 1) & + * t % stride) + 1 ! outgoing cmfd % current(4,h,i,j,k) = t % results(1,score_index) % sum ! Back surface - matching_bins(i_filter_mesh) = mesh_indices_to_bin(m, & (/ i, j, k /)) matching_bins(i_filter_surf) = OUT_BACK - score_index = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 ! outgoing + score_index = sum((matching_bins(1:size(t % filters)) - 1) & + * t % stride) + 1 ! outgoing cmfd % current(5,h,i,j,k) = t % results(1,score_index) % sum if (j > 1) then matching_bins(i_filter_mesh) = mesh_indices_to_bin(m, & (/ i, j-1, k /)) matching_bins(i_filter_surf) = OUT_FRONT - score_index = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 ! incoming + score_index = sum((matching_bins(1:size(t % filters)) - 1) & + * t % stride) + 1 ! incoming cmfd % current(6,h,i,j,k) = t % results(1,score_index) % sum end if @@ -276,28 +290,32 @@ contains matching_bins(i_filter_mesh) = mesh_indices_to_bin(m, & (/ i, j+1, k /)) matching_bins(i_filter_surf) = OUT_BACK - score_index = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 ! incoming + score_index = sum((matching_bins(1:size(t % filters)) - 1) & + * t % stride) + 1 ! incoming cmfd % current(7,h,i,j,k) = t % results(1,score_index) % sum end if matching_bins(i_filter_mesh) = mesh_indices_to_bin(m, & (/ i, j, k /)) matching_bins(i_filter_surf) = OUT_FRONT - score_index = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 ! outgoing + score_index = sum((matching_bins(1:size(t % filters)) - 1) & + * t % stride) + 1 ! outgoing cmfd % current(8,h,i,j,k) = t % results(1,score_index) % sum ! Bottom surface matching_bins(i_filter_mesh) = mesh_indices_to_bin(m, & (/ i, j, k /)) matching_bins(i_filter_surf) = OUT_BOTTOM - score_index = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 ! outgoing + score_index = sum((matching_bins(1:size(t % filters)) - 1) & + * t % stride) + 1 ! outgoing cmfd % current(9,h,i,j,k) = t % results(1,score_index) % sum if (k > 1) then matching_bins(i_filter_mesh) = mesh_indices_to_bin(m, & (/ i, j, k-1 /)) matching_bins(i_filter_surf) = OUT_TOP - score_index = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 ! incoming + score_index = sum((matching_bins(1:size(t % filters)) - 1) & + * t % stride) + 1 ! incoming cmfd % current(10,h,i,j,k) = t % results(1,score_index) % sum end if @@ -306,14 +324,16 @@ contains matching_bins(i_filter_mesh) = mesh_indices_to_bin(m, & (/ i, j, k+1 /)) matching_bins(i_filter_surf) = OUT_BOTTOM - score_index = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 ! incoming + score_index = sum((matching_bins(1:size(t % filters)) - 1) & + * t % stride) + 1 ! incoming cmfd % current(11,h,i,j,k) = t % results(1,score_index) % sum end if matching_bins(i_filter_mesh) = mesh_indices_to_bin(m, & (/ i, j, k /)) matching_bins(i_filter_surf) = OUT_TOP - score_index = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 ! outgoing + score_index = sum((matching_bins(1:size(t % filters)) - 1) & + * t % stride) + 1 ! outgoing cmfd % current(12,h,i,j,k) = t % results(1,score_index) % sum end if TALLY diff --git a/src/cmfd_input.F90 b/src/cmfd_input.F90 index e63a1fcea..5fe44ad43 100644 --- a/src/cmfd_input.F90 +++ b/src/cmfd_input.F90 @@ -267,14 +267,16 @@ contains use mesh_header, only: RegularMesh use string use tally, only: setup_active_cmfdtallies - use tally_header, only: TallyObject, TallyFilter + use tally_header, only: TallyObject + use tally_filter_header + use tally_filter use tally_initialize, only: add_tallies use xml_interface type(Node), pointer :: doc ! pointer to XML doc info character(MAX_LINE_LEN) :: temp_str ! temp string - integer :: i ! loop counter + integer :: i, j ! loop counter integer :: n ! size of arrays in mesh specification integer :: ng ! number of energy groups (default 1) integer :: n_filters ! number of filters @@ -283,7 +285,7 @@ contains real(8) :: rarray3(3) ! temp double array type(TallyObject), pointer :: t type(RegularMesh), pointer :: m - type(TallyFilter) :: filters(N_FILTER_TYPES) ! temporary filters + type(TallyFilterContainer) :: filters(N_FILTER_TYPES) ! temporary filters type(Node), pointer :: node_mesh ! Set global variables if they are 0 (this can happen if there is no tally @@ -410,21 +412,25 @@ contains ! Set up mesh filter n_filters = 1 - filters(n_filters) % type = FILTER_MESH - filters(n_filters) % n_bins = product(m % dimension) - allocate(filters(n_filters) % int_bins(1)) - filters(n_filters) % int_bins(1) = n_user_meshes + 1 + allocate(MeshFilter :: filters(n_filters) % obj) + select type (filt => filters(n_filters) % obj) + type is (MeshFilter) + filt % n_bins = product(m % dimension) + filt % mesh = n_user_meshes + 1 + end select t % find_filter(FILTER_MESH) = n_filters ! Read and set incoming energy mesh filter if (check_for_node(node_mesh, "energy")) then n_filters = n_filters + 1 - filters(n_filters) % type = FILTER_ENERGYIN - ng = get_arraysize_double(node_mesh, "energy") - filters(n_filters) % n_bins = ng - 1 - allocate(filters(n_filters) % real_bins(ng)) - call get_node_array(node_mesh, "energy", & - filters(n_filters) % real_bins) + allocate(EnergyFilter :: filters(n_filters) % obj) + select type (filt => filters(n_filters) % obj) + type is (EnergyFilter) + ng = get_arraysize_double(node_mesh, "energy") + filt % n_bins = ng - 1 + allocate(filt % bins(ng)) + call get_node_array(node_mesh, "energy", filt % bins) + end select t % find_filter(FILTER_ENERGYIN) = n_filters end if @@ -448,9 +454,10 @@ contains t % type = TALLY_VOLUME ! Allocate and set filters - t % n_filters = n_filters allocate(t % filters(n_filters)) - t % filters = filters(1:n_filters) + do j = 1, n_filters + call move_alloc(filters(j) % obj, t % filters(j) % obj) + end do ! Allocate scoring bins allocate(t % score_bins(3)) @@ -481,23 +488,22 @@ contains ! read and set outgoing energy mesh filter if (check_for_node(node_mesh, "energy")) then n_filters = n_filters + 1 - filters(n_filters) % type = FILTER_ENERGYOUT - ng = get_arraysize_double(node_mesh, "energy") - filters(n_filters) % n_bins = ng - 1 - allocate(filters(n_filters) % real_bins(ng)) - call get_node_array(node_mesh, "energy", & - filters(n_filters) % real_bins) + allocate(EnergyoutFilter :: filters(n_filters) % obj) + select type (filt => filters(n_filters) % obj) + type is (EnergyoutFilter) + ng = get_arraysize_double(node_mesh, "energy") + filt % n_bins = ng - 1 + allocate(filt % bins(ng)) + call get_node_array(node_mesh, "energy", filt % bins) + end select t % find_filter(FILTER_ENERGYOUT) = n_filters end if ! Allocate and set filters - t % n_filters = n_filters allocate(t % filters(n_filters)) - t % filters = filters(1:n_filters) - - ! deallocate filters bins array - if (check_for_node(node_mesh, "energy")) & - deallocate(filters(n_filters) % real_bins) + do j = 1, n_filters + call move_alloc(filters(j) % obj, t % filters(j) % obj) + end do ! Allocate macro reactions allocate(t % score_bins(2)) @@ -522,25 +528,25 @@ contains ! Add extra filter for surface n_filters = n_filters + 1 - filters(n_filters) % type = FILTER_SURFACE - filters(n_filters) % n_bins = 2 * m % n_dimension - allocate(filters(n_filters) % int_bins(2 * m % n_dimension)) - if (m % n_dimension == 2) then - filters(n_filters) % int_bins = (/ OUT_LEFT, OUT_RIGHT, OUT_BACK, & - OUT_FRONT /) - elseif (m % n_dimension == 3) then - filters(n_filters) % int_bins = (/ OUT_LEFT, OUT_RIGHT, OUT_BACK, & - OUT_FRONT, OUT_BOTTOM, OUT_TOP /) - end if + allocate(SurfaceFilter :: filters(n_filters) % obj) + select type(filt => filters(n_filters) % obj) + type is(SurfaceFilter) + filt % n_bins = 2 * m % n_dimension + allocate(filt % surfaces(2 * m % n_dimension)) + if (m % n_dimension == 2) then + filt % surfaces = (/ OUT_LEFT, OUT_RIGHT, OUT_BACK, OUT_FRONT /) + elseif (m % n_dimension == 3) then + filt % surfaces = (/ OUT_LEFT, OUT_RIGHT, OUT_BACK, OUT_FRONT, & + OUT_BOTTOM, OUT_TOP /) + end if + end select t % find_filter(FILTER_SURFACE) = n_filters ! Allocate and set filters - t % n_filters = n_filters allocate(t % filters(n_filters)) - t % filters = filters(1:n_filters) - - ! Deallocate filters bins array - deallocate(filters(n_filters) % int_bins) + do j = 1, n_filters + call move_alloc(filters(j) % obj, t % filters(j) % obj) + end do ! Allocate macro reactions allocate(t % score_bins(1)) @@ -558,15 +564,10 @@ contains ! We need to increase the dimension by one since we also need ! currents coming into and out of the boundary mesh cells. i_filter_mesh = t % find_filter(FILTER_MESH) - t % filters(i_filter_mesh) % n_bins = product(m % dimension + 1) + t % filters(i_filter_mesh) % obj % n_bins = product(m % dimension + 1) end if - ! Deallocate filter bins - deallocate(filters(1) % int_bins) - if (check_for_node(node_mesh, "energy")) & - deallocate(filters(2) % real_bins) - end do ! Put cmfd tallies into active tally array and turn tallies on diff --git a/src/geometry.F90 b/src/geometry.F90 index 767e2db10..6d4ca7767 100644 --- a/src/geometry.F90 +++ b/src/geometry.F90 @@ -499,7 +499,7 @@ contains end if ! Set previous coordinate going slightly past surface crossing - p % last_xyz = p % coord(1) % xyz + TINY_BIT * p % coord(1) % uvw + p % last_xyz_current = p % coord(1) % xyz + TINY_BIT * p % coord(1) % uvw ! Diagnostic message if (verbosity >= 10 .or. trace) then @@ -563,7 +563,7 @@ contains end if ! Set previous coordinate going slightly past surface crossing - p % last_xyz = p % coord(1) % xyz + TINY_BIT * p % coord(1) % uvw + p % last_xyz_current = p % coord(1) % xyz + TINY_BIT * p % coord(1) % uvw ! Diagnostic message if (verbosity >= 10 .or. trace) then diff --git a/src/global.F90 b/src/global.F90 index 1b1e5632a..1558c050e 100644 --- a/src/global.F90 +++ b/src/global.F90 @@ -14,7 +14,7 @@ module global use set_header, only: SetInt use surface_header, only: SurfaceContainer use source_header, only: SourceDistribution - use tally_header, only: TallyObject, TallyMap, TallyResult + use tally_header, only: TallyObject, TallyResult use trigger_header, only: KTrigger use timer_header, only: Timer @@ -137,6 +137,7 @@ module global type(RegularMesh), allocatable, target :: meshes(:) type(TallyObject), allocatable, target :: tallies(:) integer, allocatable :: matching_bins(:) + real(8), allocatable :: filter_weights(:) ! Pointers for different tallies type(TallyObject), pointer :: user_tallies(:) => null() @@ -176,9 +177,6 @@ module global !$omp threadprivate(global_tally_collision, global_tally_absorption, & !$omp& global_tally_tracklength, global_tally_leakage) - ! Tally map structure - type(TallyMap), allocatable :: tally_maps(:) - integer :: n_meshes = 0 ! # of structured meshes integer :: n_user_meshes = 0 ! # of structured user meshes integer :: n_tallies = 0 ! # of tallies @@ -443,7 +441,8 @@ module global type(Nuclide0K), allocatable, target :: nuclides_0K(:) ! 0K nuclides info !$omp threadprivate(micro_xs, material_xs, fission_bank, n_bank, & -!$omp& trace, thread_id, current_work, matching_bins) +!$omp& trace, thread_id, current_work, matching_bins, & +!$omp& filter_weights) contains @@ -509,7 +508,7 @@ contains if (allocated(meshes)) deallocate(meshes) if (allocated(tallies)) deallocate(tallies) if (allocated(matching_bins)) deallocate(matching_bins) - if (allocated(tally_maps)) deallocate(tally_maps) + if (allocated(filter_weights)) deallocate(filter_weights) ! Deallocate fission and source bank and entropy !$omp parallel diff --git a/src/initialize.F90 b/src/initialize.F90 index 37eeb1f3e..99adf967f 100644 --- a/src/initialize.F90 +++ b/src/initialize.F90 @@ -22,8 +22,9 @@ module initialize use state_point, only: load_state_point use string, only: to_str, starts_with, ends_with, str_to_int use summary, only: write_summary - use tally_header, only: TallyObject, TallyResult, TallyFilter + use tally_header, only: TallyObject, TallyResult use tally_initialize,only: configure_tallies + use tally_filter use tally, only: init_tally_routines #ifdef MPI @@ -711,76 +712,15 @@ contains ! ======================================================================= ! ADJUST INDICES FOR EACH TALLY FILTER - FILTER_LOOP: do j = 1, t%n_filters - - select case (t%filters(j)%type) - case (FILTER_DISTRIBCELL) - do k = 1, size(t%filters(j)%int_bins) - id = t%filters(j)%int_bins(k) - if (cell_dict%has_key(id)) then - t%filters(j)%int_bins(k) = cell_dict%get_key(id) - else - call fatal_error("Could not find cell " // trim(to_str(id)) // & - " specified on tally " // trim(to_str(t%id))) - end if - - end do - case (FILTER_CELL, FILTER_CELLBORN) - - do k = 1, t%filters(j)%n_bins - id = t%filters(j)%int_bins(k) - if (cell_dict%has_key(id)) then - t%filters(j)%int_bins(k) = cell_dict%get_key(id) - else - call fatal_error("Could not find cell " // trim(to_str(id)) & - &// " specified on tally " // trim(to_str(t%id))) - end if - end do - - case (FILTER_SURFACE) + FILTER_LOOP: do j = 1, size(t % filters) + select type(filt => t % filters(j) % obj) + type is (SurfaceFilter) ! Check if this is a surface filter only for surface currents - if (any(t%score_bins == SCORE_CURRENT)) cycle FILTER_LOOP - - do k = 1, t%filters(j)%n_bins - id = t%filters(j)%int_bins(k) - if (surface_dict%has_key(id)) then - t%filters(j)%int_bins(k) = surface_dict%get_key(id) - else - call fatal_error("Could not find surface " // trim(to_str(id)) & - &// " specified on tally " // trim(to_str(t%id))) - end if - end do - - case (FILTER_UNIVERSE) - - do k = 1, t%filters(j)%n_bins - id = t%filters(j)%int_bins(k) - if (universe_dict%has_key(id)) then - t%filters(j)%int_bins(k) = universe_dict%get_key(id) - else - call fatal_error("Could not find universe " // trim(to_str(id)) & - &// " specified on tally " // trim(to_str(t%id))) - end if - end do - - case (FILTER_MATERIAL) - - do k = 1, t%filters(j)%n_bins - id = t%filters(j)%int_bins(k) - if (material_dict%has_key(id)) then - t%filters(j)%int_bins(k) = material_dict%get_key(id) - else - call fatal_error("Could not find material " // trim(to_str(id)) & - &// " specified on tally " // trim(to_str(t%id))) - end if - end do - - case (FILTER_MESH) - - ! The mesh filter already has been set to the index in meshes rather - ! than the user-specified id, so it doesn't need to be changed. - + if (.not. any(t % score_bins == SCORE_CURRENT)) & + call filt % initialize() + class default + call filt % initialize() end select end do FILTER_LOOP @@ -894,14 +834,11 @@ contains ! We need distribcell if any tallies have distribcell filters. do i = 1, n_tallies - do j = 1, tallies(i) % n_filters - if (tallies(i) % filters(j) % type == FILTER_DISTRIBCELL) then + do j = 1, size(tallies(i) % filters) + select type(filt => tallies(i) % filters(j) % obj) + type is (DistribcellFilter) distribcell_active = .true. - if (size(tallies(i) % filters(j) % int_bins) > 1) then - call fatal_error("A distribcell filter was specified with & - &multiple bins. This feature is not supported.") - end if - end if + end select end do end do @@ -924,13 +861,12 @@ contains ! Set the number of bins in all distribcell filters. do i = 1, n_tallies - do j = 1, tallies(i) % n_filters - associate (filt => tallies(i) % filters(j)) - if (filt % type == FILTER_DISTRIBCELL) then - ! Set the number of bins to the number of instances of the cell. - filt % n_bins = cells(filt % int_bins(1)) % instances - end if - end associate + do j = 1, size(tallies(i) % filters) + select type(filt => tallies(i) % filters(j) % obj) + type is (DistribcellFilter) + ! Set the number of bins to the number of instances of the cell. + filt % n_bins = cells(filt % cell) % instances + end select end do end do @@ -990,10 +926,11 @@ contains ! List all cells referenced in distribcell filters. do i = 1, n_tallies - do j = 1, tallies(i) % n_filters - if (tallies(i) % filters(j) % type == FILTER_DISTRIBCELL) then - call cell_list % add(tallies(i) % filters(j) % int_bins(1)) - end if + do j = 1, size(tallies(i) % filters) + select type(filt => tallies(i) % filters(j) % obj) + type is (DistribcellFilter) + call cell_list % add(filt % cell) + end select end do end do diff --git a/src/input_xml.F90 b/src/input_xml.F90 index b94c41a65..dbcf2a277 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -24,7 +24,8 @@ module input_xml use stl_vector, only: VectorInt, VectorReal, VectorChar use string, only: to_lower, to_str, str_to_int, str_to_real, & starts_with, ends_with, tokenize, split_string - use tally_header, only: TallyObject, TallyFilter + use tally_header, only: TallyObject + use tally_filter use tally_initialize, only: add_tallies use xml_interface @@ -2667,7 +2668,7 @@ contains type(ElemKeyValueCI), pointer :: pair_list type(TallyObject), pointer :: t type(RegularMesh), pointer :: m - type(TallyFilter), allocatable :: filters(:) ! temporary filters + type(TallyFilterContainer), allocatable :: filters(:) ! temporary filters type(Node), pointer :: doc => null() type(Node), pointer :: node_mesh => null() type(Node), pointer :: node_tal => null() @@ -2907,115 +2908,122 @@ contains call get_node_list(node_tal, "filter", node_filt_list) n_filters = get_list_size(node_filt_list) - if (n_filters /= 0) then + ! Allocate filters array + allocate(t % filters(n_filters)) - ! Allocate filters array - t % n_filters = n_filters - allocate(t % filters(n_filters)) + READ_FILTERS: do j = 1, n_filters + ! Get pointer to filter xml node + call get_list_item(node_filt_list, j, node_filt) - READ_FILTERS: do j = 1, n_filters - ! Get pointer to filter xml node - call get_list_item(node_filt_list, j, node_filt) + ! Convert filter type to lower case + temp_str = '' + if (check_for_node(node_filt, "type")) & + call get_node_value(node_filt, "type", temp_str) + temp_str = to_lower(temp_str) - ! Convert filter type to lower case - temp_str = '' - if (check_for_node(node_filt, "type")) & - call get_node_value(node_filt, "type", temp_str) - temp_str = to_lower(temp_str) - - ! Determine number of bins - if (check_for_node(node_filt, "bins")) then - if (temp_str == 'energy' .or. temp_str == 'energyout' .or. & - temp_str == 'mu' .or. temp_str == 'polar' .or. & - temp_str == 'azimuthal') then - n_words = get_arraysize_double(node_filt, "bins") - else - n_words = get_arraysize_integer(node_filt, "bins") - end if + ! Determine number of bins + if (check_for_node(node_filt, "bins")) then + if (temp_str == 'energy' .or. temp_str == 'energyout' .or. & + temp_str == 'mu' .or. temp_str == 'polar' .or. & + temp_str == 'azimuthal') then + n_words = get_arraysize_double(node_filt, "bins") else - call fatal_error("Bins not set in filter on tally " & - // trim(to_str(t % id))) + n_words = get_arraysize_integer(node_filt, "bins") end if + else + call fatal_error("Bins not set in filter on tally " & + // trim(to_str(t % id))) + end if - ! Determine type of filter - select case (temp_str) + ! Determine type of filter + select case (temp_str) - case ('distribcell') - - ! Set type of filter - t % filters(j) % type = FILTER_DISTRIBCELL - - ! Going to add new filters to this tally if n_words > 1 + case ('distribcell') + ! Allocate and declare the filter type + allocate(DistribcellFilter::t % filters(j) % obj) + select type (filt => t % filters(j) % obj) + type is (DistribcellFilter) + if (n_words /= 1) call fatal_error("Only one cell can be & + &specified per distribcell filter.") + ! Store bins + call get_node_value(node_filt, "bins", filt % cell) + end select + ! Set the filter index in the tally find_filter array + t % find_filter(FILTER_DISTRIBCELL) = j + case ('cell') + ! Allocate and declare the filter type + allocate(CellFilter::t % filters(j) % obj) + select type (filt => t % filters(j) % obj) + type is (CellFilter) ! Allocate and store bins - allocate(t % filters(j) % int_bins(n_words)) - call get_node_array(node_filt, "bins", t % filters(j) % int_bins) - - case ('cell') - ! Set type of filter - t % filters(j) % type = FILTER_CELL - - ! Set number of bins - t % filters(j) % n_bins = n_words + filt % n_bins = n_words + allocate(filt % cells(n_words)) + call get_node_array(node_filt, "bins", filt % cells) + end select + ! Set the filter index in the tally find_filter array + t % find_filter(FILTER_CELL) = j + case ('cellborn') + ! Allocate and declare the filter type + allocate(CellbornFilter::t % filters(j) % obj) + select type (filt => t % filters(j) % obj) + type is (CellbornFilter) ! Allocate and store bins - allocate(t % filters(j) % int_bins(n_words)) - call get_node_array(node_filt, "bins", t % filters(j) % int_bins) - - case ('cellborn') - ! Set type of filter - t % filters(j) % type = FILTER_CELLBORN - - ! Set number of bins - t % filters(j) % n_bins = n_words + filt % n_bins = n_words + allocate(filt % cells(n_words)) + call get_node_array(node_filt, "bins", filt % cells) + end select + ! Set the filter index in the tally find_filter array + t % find_filter(FILTER_CELLBORN) = j + case ('material') + ! Allocate and declare the filter type + allocate(MaterialFilter::t % filters(j) % obj) + select type (filt => t % filters(j) % obj) + type is (MaterialFilter) ! Allocate and store bins - allocate(t % filters(j) % int_bins(n_words)) - call get_node_array(node_filt, "bins", t % filters(j) % int_bins) - - case ('material') - ! Set type of filter - t % filters(j) % type = FILTER_MATERIAL - - ! Set number of bins - t % filters(j) % n_bins = n_words + filt % n_bins = n_words + allocate(filt % materials(n_words)) + call get_node_array(node_filt, "bins", filt % materials) + end select + ! Set the filter index in the tally find_filter array + t % find_filter(FILTER_MATERIAL) = j + case ('universe') + ! Allocate and declare the filter type + allocate(UniverseFilter::t % filters(j) % obj) + select type (filt => t % filters(j) % obj) + type is (UniverseFilter) ! Allocate and store bins - allocate(t % filters(j) % int_bins(n_words)) - call get_node_array(node_filt, "bins", t % filters(j) % int_bins) - - case ('universe') - ! Set type of filter - t % filters(j) % type = FILTER_UNIVERSE - - ! Set number of bins - t % filters(j) % n_bins = n_words + filt % n_bins = n_words + allocate(filt % universes(n_words)) + call get_node_array(node_filt, "bins", filt % universes) + end select + ! Set the filter index in the tally find_filter array + t % find_filter(FILTER_UNIVERSE) = j + case ('surface') + call fatal_error("Surface filter is not yet supported!") + ! Allocate and declare the filter type + allocate(SurfaceFilter::t % filters(j) % obj) + select type (filt => t % filters(j) % obj) + type is (SurfaceFilter) ! Allocate and store bins - allocate(t % filters(j) % int_bins(n_words)) - call get_node_array(node_filt, "bins", t % filters(j) % int_bins) + filt % n_bins = n_words + allocate(filt % surfaces(n_words)) + call get_node_array(node_filt, "bins", filt % surfaces) + end select + ! Set the filter index in the tally find_filter array + t % find_filter(FILTER_SURFACE) = j - case ('surface') - call fatal_error("Surface filter is not yet supported!") - - ! Set type of filter - t % filters(j) % type = FILTER_SURFACE - - ! Set number of bins - t % filters(j) % n_bins = n_words - - ! Allocate and store bins - allocate(t % filters(j) % int_bins(n_words)) - call get_node_array(node_filt, "bins", t % filters(j) % int_bins) - - case ('mesh') - ! Set type of filter - t % filters(j) % type = FILTER_MESH - - ! Check to make sure multiple meshes weren't given - if (n_words /= 1) then - call fatal_error("Can only have one mesh filter specified.") - end if + case ('mesh') + ! Allocate and declare the filter type + allocate(MeshFilter::t % filters(j) % obj) + select type (filt => t % filters(j) % obj) + type is (MeshFilter) + if (n_words /= 1) call fatal_error("Only one mesh can be & + &specified per mesh filter.") ! Determine id of mesh call get_node_value(node_filt, "bins", id) @@ -3030,214 +3038,222 @@ contains end if ! Determine number of bins - t % filters(j) % n_bins = product(m % dimension) + filt % n_bins = product(m % dimension) - ! Allocate and store index of mesh - allocate(t % filters(j) % int_bins(1)) - t % filters(j) % int_bins(1) = i_mesh + ! Store the index of the mesh + filt % mesh = i_mesh + end select - case ('energy') - ! Set type of filter - t % filters(j) % type = FILTER_ENERGYIN + ! Set the filter index in the tally find_filter array + t % find_filter(FILTER_MESH) = j - ! Set number of bins - t % filters(j) % n_bins = n_words - 1 + case ('energy') + ! Allocate and declare the filter type + allocate(EnergyFilter::t % filters(j) % obj) + select type (filt => t % filters(j) % obj) + type is (EnergyFilter) ! Allocate and store bins - allocate(t % filters(j) % real_bins(n_words)) - call get_node_array(node_filt, "bins", t % filters(j) % real_bins) + filt % n_bins = n_words - 1 + allocate(filt % bins(n_words)) + call get_node_array(node_filt, "bins", filt % bins) - ! We can save tallying time if we know that the tally bins - ! match the energy group structure. In that case, the matching bin + ! We can save tallying time if we know that the tally bins match + ! the energy group structure. In that case, the matching bin ! index is simply the group (after flipping for the different ! ordering of the library and tallying systems). if (.not. run_CE) then if (n_words == energy_groups + 1) then - if (all(t % filters(j) % real_bins == & - energy_bins(energy_groups + 1:1:-1))) & - t % energy_matches_groups = .true. + if (all(filt % bins == energy_bins(energy_groups + 1:1:-1))) & + then + filt % matches_transport_groups = .true. + end if end if end if + end select + ! Set the filter index in the tally find_filter array + t % find_filter(FILTER_ENERGYIN) = j - case ('energyout') - ! Set type of filter - t % filters(j) % type = FILTER_ENERGYOUT - - ! Set number of bins - t % filters(j) % n_bins = n_words - 1 - + case ('energyout') + ! Allocate and declare the filter type + allocate(EnergyoutFilter::t % filters(j) % obj) + select type (filt => t % filters(j) % obj) + type is (EnergyoutFilter) ! Allocate and store bins - allocate(t % filters(j) % real_bins(n_words)) - call get_node_array(node_filt, "bins", t % filters(j) % real_bins) + filt % n_bins = n_words - 1 + allocate(filt % bins(n_words)) + call get_node_array(node_filt, "bins", filt % bins) - ! We can save tallying time if we know that the tally bins - ! match the energy group structure. In that case, the matching bin + ! We can save tallying time if we know that the tally bins match + ! the energy group structure. In that case, the matching bin ! index is simply the group (after flipping for the different ! ordering of the library and tallying systems). if (.not. run_CE) then if (n_words == energy_groups + 1) then - if (all(t % filters(j) % real_bins == & - energy_bins(energy_groups + 1:1:-1))) & - t % energyout_matches_groups = .true. + if (all(filt % bins == energy_bins(energy_groups + 1:1:-1))) & + then + filt % matches_transport_groups = .true. + end if end if end if + end select + ! Set the filter index in the tally find_filter array + t % find_filter(FILTER_ENERGYOUT) = j - ! Set to analog estimator - t % estimator = ESTIMATOR_ANALOG + ! Set to analog estimator + t % estimator = ESTIMATOR_ANALOG - case ('delayedgroup') - ! Check to see if running in MG mode, because if so, the current - ! system isnt set up yet to support delayed group data and thus - ! these tallies - if (.not. run_CE) then - call fatal_error("delayedgroup filter on tally " & - // trim(to_str(t % id)) // " not yet supported& - & for multi-group mode.") - end if - - ! Set type of filter - t % filters(j) % type = FILTER_DELAYEDGROUP - - ! Set number of bins - t % filters(j) % n_bins = n_words + case ('delayedgroup') + ! Check to see if running in MG mode, because if so, the current + ! system isnt set up yet to support delayed group data and thus + ! these tallies + if (.not. run_CE) then + call fatal_error("delayedgroup filter on tally " & + // trim(to_str(t % id)) // " not yet supported& + & for multi-group mode.") + end if + ! Allocate and declare the filter type + allocate(DelayedGroupFilter::t % filters(j) % obj) + select type (filt => t % filters(j) % obj) + type is (DelayedGroupFilter) ! Allocate and store bins - allocate(t % filters(j) % int_bins(n_words)) - call get_node_array(node_filt, "bins", t % filters(j) % int_bins) + filt % n_bins = n_words + allocate(filt % groups(n_words)) + call get_node_array(node_filt, "bins", filt % groups) - ! Check bins to make sure all are between 1 and MAX_DELAYED_GROUPS + ! Check that bins are all are between 1 and MAX_DELAYED_GROUPS do d = 1, n_words - if (t % filters(j) % int_bins(d) < 1 .or. & - t % filters(j) % int_bins(d) > MAX_DELAYED_GROUPS) then + if (filt % groups(d) < 1 .or. & + filt % groups(d) > MAX_DELAYED_GROUPS) then call fatal_error("Encountered delayedgroup bin with index " & - // trim(to_str(t % filters(j) % int_bins(d))) // " that is& - & outside the range of 1 to MAX_DELAYED_GROUPS ( " & + // trim(to_str(filt % groups(d))) // " that is outside & + &the range of 1 to MAX_DELAYED_GROUPS ( " & // trim(to_str(MAX_DELAYED_GROUPS)) // ")") end if end do + end select + ! Set the filter index in the tally find_filter array + t % find_filter(FILTER_DELAYEDGROUP) = j - case ('mu') - ! Set type of filter - t % filters(j) % type = FILTER_MU - - ! Set number of bins - t % filters(j) % n_bins = n_words - 1 - + case ('mu') + ! Allocate and declare the filter type + allocate(MuFilter::t % filters(j) % obj) + select type (filt => t % filters(j) % obj) + type is (MuFilter) ! Allocate and store bins - allocate(t % filters(j) % real_bins(n_words)) - call get_node_array(node_filt, "bins", t % filters(j) % real_bins) + filt % n_bins = n_words - 1 + allocate(filt % bins(n_words)) + call get_node_array(node_filt, "bins", filt % bins) - ! Allow a user to input a lone number which will mean that - ! you subivide [-1,1] evenly with the input being the number of bins + ! Allow a user to input a lone number which will mean that you + ! subdivide [-1,1] evenly with the input being the number of bins if (n_words == 1) then - Nangle = int(t % filters(j) % real_bins(1)) + Nangle = int(filt % bins(1)) if (Nangle > 1) then - t % filters(j) % n_bins = Nangle + filt % n_bins = Nangle dangle = TWO / real(Nangle,8) - deallocate(t % filters(j) % real_bins) - allocate(t % filters(j) % real_bins(Nangle + 1)) + deallocate(filt % bins) + allocate(filt % bins(Nangle + 1)) do iangle = 1, Nangle - t % filters(j) % real_bins(iangle) = -ONE + (iangle - 1) * dangle + filt % bins(iangle) = -ONE + (iangle - 1) * dangle end do - t % filters(j) % real_bins(Nangle + 1) = ONE + filt % bins(Nangle + 1) = ONE else call fatal_error("Number of bins for mu filter must be& - & greater than 1 on tally " // trim(to_str(t % id)) // ".") + & greater than 1 on tally " & + // trim(to_str(t % id)) // ".") end if - end if + end select + ! Set the filter index in the tally find_filter array + t % find_filter(FILTER_MU) = j - ! Set to analog estimator - t % estimator = ESTIMATOR_ANALOG - - case ('polar') - ! Set type of filter - t % filters(j) % type = FILTER_POLAR - - ! Set number of bins - t % filters(j) % n_bins = n_words - 1 + ! Set to analog estimator + t % estimator = ESTIMATOR_ANALOG + case ('polar') + ! Allocate and declare the filter type + allocate(PolarFilter::t % filters(j) % obj) + select type (filt => t % filters(j) % obj) + type is (PolarFilter) ! Allocate and store bins - allocate(t % filters(j) % real_bins(n_words)) - call get_node_array(node_filt, "bins", t % filters(j) % real_bins) + filt % n_bins = n_words - 1 + allocate(filt % bins(n_words)) + call get_node_array(node_filt, "bins", filt % bins) - ! Allow a user to input a lone number which will mean that - ! you subivide [0,pi] evenly with the input being the number of bins + ! Allow a user to input a lone number which will mean that you + ! subdivide [0,pi] evenly with the input being the number of bins if (n_words == 1) then - Nangle = int(t % filters(j) % real_bins(1)) + Nangle = int(filt % bins(1)) if (Nangle > 1) then - t % filters(j) % n_bins = Nangle + filt % n_bins = Nangle dangle = PI / real(Nangle,8) - deallocate(t % filters(j) % real_bins) - allocate(t % filters(j) % real_bins(Nangle + 1)) + deallocate(filt % bins) + allocate(filt % bins(Nangle + 1)) do iangle = 1, Nangle - t % filters(j) % real_bins(iangle) = (iangle - 1) * dangle + filt % bins(iangle) = (iangle - 1) * dangle end do - t % filters(j) % real_bins(Nangle + 1) = PI + filt % bins(Nangle + 1) = PI else - call fatal_error("Number of bins for polar filter must be& - & greater than 1 on tally " // trim(to_str(t % id)) // ".") + call fatal_error("Number of bins for mu filter must be& + & greater than 1 on tally " & + // trim(to_str(t % id)) // ".") end if - end if + end select + ! Set the filter index in the tally find_filter array + t % find_filter(FILTER_POLAR) = j - case ('azimuthal') - ! Set type of filter - t % filters(j) % type = FILTER_AZIMUTHAL - - ! Set number of bins - t % filters(j) % n_bins = n_words - 1 - + case ('azimuthal') + ! Allocate and declare the filter type + allocate(AzimuthalFilter::t % filters(j) % obj) + select type (filt => t % filters(j) % obj) + type is (AzimuthalFilter) ! Allocate and store bins - allocate(t % filters(j) % real_bins(n_words)) - call get_node_array(node_filt, "bins", t % filters(j) % real_bins) + filt % n_bins = n_words - 1 + allocate(filt % bins(n_words)) + call get_node_array(node_filt, "bins", filt % bins) - ! Allow a user to input a lone number which will mean that - ! you sub-divide [-pi,pi) evenly with the input being the number of + ! Allow a user to input a lone number which will mean that you + ! subdivide [-pi,pi) evenly with the input being the number of ! bins if (n_words == 1) then - Nangle = int(t % filters(j) % real_bins(1)) + Nangle = int(filt % bins(1)) if (Nangle > 1) then - t % filters(j) % n_bins = Nangle + filt % n_bins = Nangle dangle = TWO * PI / real(Nangle,8) - deallocate(t % filters(j) % real_bins) - allocate(t % filters(j) % real_bins(Nangle + 1)) + deallocate(filt % bins) + allocate(filt % bins(Nangle + 1)) do iangle = 1, Nangle - t % filters(j) % real_bins(iangle) = -PI + (iangle - 1) * dangle + filt % bins(iangle) = -PI + (iangle - 1) * dangle end do - t % filters(j) % real_bins(Nangle + 1) = PI + filt % bins(Nangle + 1) = PI else - call fatal_error("Number of bins for azimuthal filter must be& - & greater than 1 on tally " // trim(to_str(t % id)) // ".") + call fatal_error("Number of bins for mu filter must be& + & greater than 1 on tally " & + // trim(to_str(t % id)) // ".") end if - end if - - case default - ! Specified tally filter is invalid, raise error - call fatal_error("Unknown filter type '" & - // trim(temp_str) // "' on tally " & - // trim(to_str(t % id)) // ".") - end select + ! Set the filter index in the tally find_filter array + t % find_filter(FILTER_AZIMUTHAL) = j - ! Set find_filter, e.g. if filter(3) has type FILTER_CELL, then - ! find_filter(FILTER_CELL) would be set to 3. + case default + ! Specified tally filter is invalid, raise error + call fatal_error("Unknown filter type '" & + // trim(temp_str) // "' on tally " & + // trim(to_str(t % id)) // ".") - t % find_filter(t % filters(j) % type) = j + end select - end do READ_FILTERS + end do READ_FILTERS - ! Check that both cell and surface weren't specified - if (t % find_filter(FILTER_CELL) > 0 .and. & - t % find_filter(FILTER_SURFACE) > 0) then - call fatal_error("Cannot specify both cell and surface filters for & - &tally " // trim(to_str(t % id))) - end if - - else - ! No filters were specified - t % n_filters = 0 + ! Check that both cell and surface weren't specified + if (t % find_filter(FILTER_CELL) > 0 .and. & + t % find_filter(FILTER_SURFACE) > 0) then + call fatal_error("Cannot specify both cell and surface filters for & + &tally " // trim(to_str(t % id))) end if ! ======================================================================= @@ -3273,8 +3289,9 @@ contains if (trim(sarray(j)) == 'total') then ! Check if a delayedgroup filter is present for this tally - do l = 1, t % n_filters - if (t % filters(l) % type == FILTER_DELAYEDGROUP) then + do l = 1, size(t % filters) + select type(filt => t % filters(l) % obj) + type is (DelayedGroupFilter) call warning("A delayedgroup filter was used on a total & &nuclide tally. Cross section libraries are not & &guaranteed to have the same delayed group structure & @@ -3283,7 +3300,7 @@ contains &all isotopes while the JEFF 3.1.1 library has the same & &delayed group structure across all isotopes. Use with & &caution!") - end if + end select end do t % nuclide_bins(j) = -1 @@ -3332,8 +3349,9 @@ contains t % n_nuclide_bins = 1 ! Check if a delayedgroup filter is present for this tally - do l = 1, t % n_filters - if (t % filters(l) % type == FILTER_DELAYEDGROUP) then + do l = 1, size(t % filters) + select type(filt => t % filters(l) % obj) + type is (DelayedGroupFilter) call warning("A delayedgroup filter was used on a total nuclide & &tally. Cross section libraries are not guaranteed to have the& & same delayed group structure across all isotopes. In & @@ -3341,7 +3359,7 @@ contains &group structure across all isotopes while the JEFF 3.1.1 & &library has the same delayed group structure across all & &isotopes. Use with caution!") - end if + end select end do end if @@ -3653,32 +3671,29 @@ contains &filter.") end if - ! Get pointer to mesh - i_mesh = t % filters(k) % int_bins(1) - m => meshes(i_mesh) - ! Copy filters to temporary array - allocate(filters(t % n_filters + 1)) - filters(1:t % n_filters) = t % filters + allocate(filters(size(t % filters) + 1)) + filters(1:size(t % filters)) = t % filters ! Move allocation back -- filters becomes deallocated during ! this call call move_alloc(FROM=filters, TO=t%filters) ! Add surface filter - t % n_filters = t % n_filters + 1 - t % filters(t % n_filters) % type = FILTER_SURFACE - t % filters(t % n_filters) % n_bins = 2 * m % n_dimension - allocate(t % filters(t % n_filters) % int_bins(& - 2 * m % n_dimension)) - if (m % n_dimension == 2) then - t % filters(t % n_filters) % int_bins = (/ OUT_LEFT, & - OUT_RIGHT, OUT_BACK, OUT_FRONT /) - elseif (m % n_dimension == 3) then - t % filters(t % n_filters) % int_bins = (/ OUT_LEFT, & - OUT_RIGHT, OUT_BACK, OUT_FRONT, OUT_BOTTOM, OUT_TOP /) - end if - t % find_filter(FILTER_SURFACE) = t % n_filters + n_filters = size(t % filters) + allocate(SurfaceFilter :: t % filters(n_filters) % obj) + select type (filt => t % filters(size(t % filters)) % obj) + type is (SurfaceFilter) + filt % n_bins = 2 * m % n_dimension + allocate(filt % surfaces(2 * m % n_dimension)) + if (m % n_dimension == 2) then + filt % surfaces = (/ OUT_LEFT, OUT_RIGHT, OUT_BACK, OUT_FRONT /) + elseif (m % n_dimension == 3) then + filt % surfaces = (/ OUT_LEFT, OUT_RIGHT, OUT_BACK, OUT_FRONT,& + OUT_BOTTOM, OUT_TOP /) + end if + end select + t % find_filter(FILTER_SURFACE) = size(t % filters) case ('events') t % score_bins(j) = SCORE_EVENTS @@ -4388,9 +4403,11 @@ contains &meshlines on plot " // trim(to_str(pl % id))) end if - i_mesh = cmfd_tallies(1) % & - filters(cmfd_tallies(1) % find_filter(FILTER_MESH)) % & - int_bins(1) + select type(filt => cmfd_tallies(1) % & + filters(cmfd_tallies(1) % find_filter(FILTER_MESH)) % obj) + type is (MeshFilter) + i_mesh = filt % mesh + end select pl % meshlines_mesh => meshes(i_mesh) case ('entropy') diff --git a/src/mesh.F90 b/src/mesh.F90 index 0543eaa9f..137590e65 100644 --- a/src/mesh.F90 +++ b/src/mesh.F90 @@ -281,34 +281,42 @@ contains ! Check if line intersects left surface -- calculate the intersection point ! y - yi = y0 + (xm0 - x0) * (y1 - y0) / (x1 - x0) - if (yi >= ym0 .and. yi < ym1) then - intersects = .true. - return + if ((x0 < xm0 .and. x1 > xm0) .or. (x0 > xm0 .and. x1 < xm0)) then + yi = y0 + (xm0 - x0) * (y1 - y0) / (x1 - x0) + if (yi >= ym0 .and. yi < ym1) then + intersects = .true. + return + end if end if ! Check if line intersects back surface -- calculate the intersection point ! x - xi = x0 + (ym0 - y0) * (x1 - x0) / (y1 - y0) - if (xi >= xm0 .and. xi < xm1) then - intersects = .true. - return + if ((y0 < ym0 .and. y1 > ym0) .or. (y0 > ym0 .and. y1 < ym0)) then + xi = x0 + (ym0 - y0) * (x1 - x0) / (y1 - y0) + if (xi >= xm0 .and. xi < xm1) then + intersects = .true. + return + end if end if ! Check if line intersects right surface -- calculate the intersection ! point y - yi = y0 + (xm1 - x0) * (y1 - y0) / (x1 - x0) - if (yi >= ym0 .and. yi < ym1) then - intersects = .true. - return + if ((x0 < xm1 .and. x1 > xm1) .or. (x0 > xm1 .and. x1 < xm1)) then + yi = y0 + (xm1 - x0) * (y1 - y0) / (x1 - x0) + if (yi >= ym0 .and. yi < ym1) then + intersects = .true. + return + end if end if ! Check if line intersects front surface -- calculate the intersection point ! x - xi = x0 + (ym1 - y0) * (x1 - x0) / (y1 - y0) - if (xi >= xm0 .and. xi < xm1) then - intersects = .true. - return + if ((y0 < ym1 .and. y1 > ym1) .or. (y0 > ym1 .and. y1 < ym1)) then + xi = x0 + (ym1 - y0) * (x1 - x0) / (y1 - y0) + if (xi >= xm0 .and. xi < xm1) then + intersects = .true. + return + end if end if end function mesh_intersects_2d @@ -350,56 +358,68 @@ contains ! Check if line intersects left surface -- calculate the intersection point ! (y,z) - yi = y0 + (xm0 - x0) * (y1 - y0) / (x1 - x0) - zi = z0 + (xm0 - x0) * (z1 - z0) / (x1 - x0) - if (yi >= ym0 .and. yi < ym1 .and. zi >= zm0 .and. zi < zm1) then - intersects = .true. - return + if ((x0 < xm0 .and. x1 > xm0) .or. (x0 > xm0 .and. x1 < xm0)) then + yi = y0 + (xm0 - x0) * (y1 - y0) / (x1 - x0) + zi = z0 + (xm0 - x0) * (z1 - z0) / (x1 - x0) + if (yi >= ym0 .and. yi < ym1 .and. zi >= zm0 .and. zi < zm1) then + intersects = .true. + return + end if end if ! Check if line intersects back surface -- calculate the intersection point ! (x,z) - xi = x0 + (ym0 - y0) * (x1 - x0) / (y1 - y0) - zi = z0 + (ym0 - y0) * (z1 - z0) / (y1 - y0) - if (xi >= xm0 .and. xi < xm1 .and. zi >= zm0 .and. zi < zm1) then - intersects = .true. - return + if ((y0 < ym0 .and. y1 > ym0) .or. (y0 > ym0 .and. y1 < ym0)) then + xi = x0 + (ym0 - y0) * (x1 - x0) / (y1 - y0) + zi = z0 + (ym0 - y0) * (z1 - z0) / (y1 - y0) + if (xi >= xm0 .and. xi < xm1 .and. zi >= zm0 .and. zi < zm1) then + intersects = .true. + return + end if end if ! Check if line intersects bottom surface -- calculate the intersection ! point (x,y) - xi = x0 + (zm0 - z0) * (x1 - x0) / (z1 - z0) - yi = y0 + (zm0 - z0) * (y1 - y0) / (z1 - z0) - if (xi >= xm0 .and. xi < xm1 .and. yi >= ym0 .and. yi < ym1) then - intersects = .true. - return + if ((z0 < zm0 .and. z1 > zm0) .or. (z0 > zm0 .and. z1 < zm0)) then + xi = x0 + (zm0 - z0) * (x1 - x0) / (z1 - z0) + yi = y0 + (zm0 - z0) * (y1 - y0) / (z1 - z0) + if (xi >= xm0 .and. xi < xm1 .and. yi >= ym0 .and. yi < ym1) then + intersects = .true. + return + end if end if ! Check if line intersects right surface -- calculate the intersection point ! (y,z) - yi = y0 + (xm1 - x0) * (y1 - y0) / (x1 - x0) - zi = z0 + (xm1 - x0) * (z1 - z0) / (x1 - x0) - if (yi >= ym0 .and. yi < ym1 .and. zi >= zm0 .and. zi < zm1) then - intersects = .true. - return + if ((x0 < xm1 .and. x1 > xm1) .or. (x0 > xm1 .and. x1 < xm1)) then + yi = y0 + (xm1 - x0) * (y1 - y0) / (x1 - x0) + zi = z0 + (xm1 - x0) * (z1 - z0) / (x1 - x0) + if (yi >= ym0 .and. yi < ym1 .and. zi >= zm0 .and. zi < zm1) then + intersects = .true. + return + end if end if ! Check if line intersects front surface -- calculate the intersection point ! (x,z) - xi = x0 + (ym1 - y0) * (x1 - x0) / (y1 - y0) - zi = z0 + (ym1 - y0) * (z1 - z0) / (y1 - y0) - if (xi >= xm0 .and. xi < xm1 .and. zi >= zm0 .and. zi < zm1) then - intersects = .true. - return + if ((y0 < ym1 .and. y1 > ym1) .or. (y0 > ym1 .and. y1 < ym1)) then + xi = x0 + (ym1 - y0) * (x1 - x0) / (y1 - y0) + zi = z0 + (ym1 - y0) * (z1 - z0) / (y1 - y0) + if (xi >= xm0 .and. xi < xm1 .and. zi >= zm0 .and. zi < zm1) then + intersects = .true. + return + end if end if ! Check if line intersects top surface -- calculate the intersection point ! (x,y) - xi = x0 + (zm1 - z0) * (x1 - x0) / (z1 - z0) - yi = y0 + (zm1 - z0) * (y1 - y0) / (z1 - z0) - if (xi >= xm0 .and. xi < xm1 .and. yi >= ym0 .and. yi < ym1) then - intersects = .true. - return + if ((z0 < zm1 .and. z1 > zm1) .or. (z0 > zm1 .and. z1 < zm1)) then + xi = x0 + (zm1 - z0) * (x1 - x0) / (z1 - z0) + yi = y0 + (zm1 - z0) * (y1 - y0) / (z1 - z0) + if (xi >= xm0 .and. xi < xm1 .and. yi >= ym0 .and. yi < ym1) then + intersects = .true. + return + end if end if end function mesh_intersects_3d diff --git a/src/output.F90 b/src/output.F90 index efe75ad8b..3b0e2c783 100644 --- a/src/output.F90 +++ b/src/output.F90 @@ -17,6 +17,7 @@ module output use sab_header, only: SAlphaBeta use string, only: to_upper, to_str use tally_header, only: TallyObject + use tally_filter implicit none @@ -736,7 +737,6 @@ contains integer :: k ! loop index for scoring bins integer :: n ! loop index for nuclides integer :: l ! loop index for user scores - integer :: type ! type of tally filter integer :: indent ! number of spaces to preceed output integer :: filter_index ! index in results array for filters integer :: score_index ! scoring bin index @@ -747,7 +747,6 @@ contains real(8) :: t_value ! t-values for confidence intervals real(8) :: alpha ! significance level for CI character(MAX_FILE_LEN) :: filename ! name of output file - character(16) :: filter_name(N_FILTER_TYPES) ! names of tally filters character(36) :: score_names(N_SCORE_TYPES) ! names of scoring function character(36) :: score_name ! names of scoring function ! to be applied at write-time @@ -756,21 +755,6 @@ contains ! Skip if there are no tallies if (n_tallies == 0) return - ! Initialize names for tally filter types - filter_name(FILTER_UNIVERSE) = "Universe" - filter_name(FILTER_MATERIAL) = "Material" - filter_name(FILTER_DISTRIBCELL) = "Distributed Cell" - filter_name(FILTER_CELL) = "Cell" - filter_name(FILTER_CELLBORN) = "Birth Cell" - filter_name(FILTER_SURFACE) = "Surface" - filter_name(FILTER_MESH) = "Mesh" - filter_name(FILTER_ENERGYIN) = "Incoming Energy" - filter_name(FILTER_ENERGYOUT) = "Outgoing Energy" - filter_name(FILTER_MU) = "Change-in-Angle" - filter_name(FILTER_POLAR) = "Polar Angle" - filter_name(FILTER_AZIMUTHAL) = "Azimuthal Angle" - filter_name(FILTER_DELAYEDGROUP) = "Delayed Group" - ! Initialize names for scores score_names(abs(SCORE_FLUX)) = "Flux" score_names(abs(SCORE_TOTAL)) = "Total Reaction Rate" @@ -841,14 +825,14 @@ contains ! to be used for a given tally. ! Initialize bins, filter level, and indentation - matching_bins(1:t%n_filters) = 0 + matching_bins(1:size(t % filters)) = 0 j = 1 indent = 0 print_bin: do find_bin: do ! Check for no filters - if (t % n_filters == 0) exit find_bin + if (size(t % filters) == 0) exit find_bin ! Increment bin combination matching_bins(j) = matching_bins(j) + 1 @@ -856,7 +840,7 @@ contains ! ================================================================= ! REACHED END OF BINS FOR THIS FILTER, MOVE TO NEXT FILTER - if (matching_bins(j) > t % filters(j) % n_bins) then + if (matching_bins(j) > t % filters(j) % obj % n_bins) then ! If this is the first filter, then exit if (j == 1) exit print_bin @@ -869,12 +853,11 @@ contains else ! Check if this is last filter - if (j == t % n_filters) exit find_bin + if (j == size(t % filters)) exit find_bin ! Print current filter information - type = t % filters(j) % type - write(UNIT=unit_tally, FMT='(1X,2A,1X,A)') repeat(" ", indent), & - trim(filter_name(type)), trim(get_label(t, j)) + write(UNIT=unit_tally, FMT='(1X,2A)') repeat(" ", indent), & + trim(t % filters(j) % obj % text_label(matching_bins(j))) indent = indent + 2 j = j + 1 end if @@ -882,25 +865,25 @@ contains end do find_bin ! Print filter information - if (t % n_filters > 0) then - type = t % filters(j) % type - write(UNIT=unit_tally, FMT='(1X,2A,1X,A)') repeat(" ", indent), & - trim(filter_name(type)), trim(get_label(t, j)) + if (size(t % filters) > 0) then + write(UNIT=unit_tally, FMT='(1X,2A)') repeat(" ", indent), & + trim(t % filters(j) % obj % text_label(matching_bins(j))) end if ! Determine scoring index for this bin combination -- note that unlike ! in the score_tally subroutine, we have to use max(bins,1) since all ! bins below the lowest filter level will be zeros - if (t % n_filters > 0) then - filter_index = sum((max(matching_bins(1:t%n_filters),1) - 1) * t % stride) + 1 + if (size(t % filters) > 0) then + filter_index = sum((max(matching_bins(1:size(t % filters)),1) - 1) & + * t % stride) + 1 else filter_index = 1 end if ! Write results for this filter bin combination score_index = 0 - if (t % n_filters > 0) indent = indent + 2 + if (size(t % filters) > 0) indent = indent + 2 do n = 1, t % n_nuclide_bins ! Write label for nuclide i_nuclide = t % nuclide_bins(n) @@ -972,7 +955,7 @@ contains end do indent = indent - 2 - if (t % n_filters == 0) exit print_bin + if (size(t % filters) == 0) exit print_bin end do print_bin @@ -1009,16 +992,19 @@ contains ! Get pointer to mesh i_filter_mesh = t % find_filter(FILTER_MESH) i_filter_surf = t % find_filter(FILTER_SURFACE) - m => meshes(t % filters(i_filter_mesh) % int_bins(1)) + select type(filt => t % filters(i_filter_mesh) % obj) + type is (MeshFilter) + m => meshes(filt % mesh) + end select ! initialize bins array - matching_bins(1:t%n_filters) = 1 + matching_bins(1:size(t % filters)) = 1 ! determine how many energy in bins there are i_filter_ein = t % find_filter(FILTER_ENERGYIN) if (i_filter_ein > 0) then print_ebin = .true. - n = t % filters(i_filter_ein) % n_bins + n = t % filters(i_filter_ein) % obj % n_bins else print_ebin = .false. n = 1 @@ -1041,15 +1027,17 @@ contains matching_bins(i_filter_ein) = l ! Write incoming energy bin - write(UNIT=unit_tally, FMT='(3X,A,1X,A)') & - "Incoming Energy", trim(get_label(t, i_filter_ein)) + write(UNIT=unit_tally, FMT='(3X,A)') & + trim(t % filters(i_filter_ein) % obj % text_label( & + matching_bins(i_filter_ein))) end if ! Left Surface matching_bins(i_filter_mesh) = & mesh_indices_to_bin(m, (/ i, j, k /)) - matching_bins(i_filter_surf) = OUT_LEFT - filter_index = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 + matching_bins(i_filter_surf) = OUT_RIGHT + filter_index = sum((matching_bins(1:size(t % filters)) - 1) & + * t % stride) + 1 write(UNIT=unit_tally, FMT='(5X,A,T35,A,"+/- ",A)') & "Outgoing Current to Left", & to_str(t % results(1,filter_index) % sum), & @@ -1059,7 +1047,8 @@ contains matching_bins(i_filter_mesh) = & mesh_indices_to_bin(m, (/ i, j, k /)) matching_bins(i_filter_surf) = OUT_RIGHT - filter_index = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 + filter_index = sum((matching_bins(1:size(t % filters)) - 1) & + * t % stride) + 1 write(UNIT=unit_tally, FMT='(5X,A,T35,A,"+/- ",A)') & "Outgoing Current to Right", & to_str(t % results(1,filter_index) % sum), & @@ -1069,7 +1058,8 @@ contains matching_bins(i_filter_mesh) = & mesh_indices_to_bin(m, (/ i, j, k /)) matching_bins(i_filter_surf) = OUT_BACK - filter_index = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 + filter_index = sum((matching_bins(1:size(t % filters)) - 1) & + * t % stride) + 1 write(UNIT=unit_tally, FMT='(5X,A,T35,A,"+/- ",A)') & "Outgoing Current to Back", & to_str(t % results(1,filter_index) % sum), & @@ -1079,7 +1069,8 @@ contains matching_bins(i_filter_mesh) = & mesh_indices_to_bin(m, (/ i, j, k /)) matching_bins(i_filter_surf) = OUT_FRONT - filter_index = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 + filter_index = sum((matching_bins(1:size(t % filters)) - 1) & + * t % stride) + 1 write(UNIT=unit_tally, FMT='(5X,A,T35,A,"+/- ",A)') & "Outgoing Current to Front", & to_str(t % results(1,filter_index) % sum), & @@ -1089,7 +1080,8 @@ contains matching_bins(i_filter_mesh) = & mesh_indices_to_bin(m, (/ i, j, k /)) matching_bins(i_filter_surf) = OUT_BOTTOM - filter_index = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 + filter_index = sum((matching_bins(1:size(t % filters)) - 1) & + * t % stride) + 1 write(UNIT=unit_tally, FMT='(5X,A,T35,A,"+/- ",A)') & "Outgoing Current to Bottom", & to_str(t % results(1,filter_index) % sum), & @@ -1099,7 +1091,8 @@ contains matching_bins(i_filter_mesh) = & mesh_indices_to_bin(m, (/ i, j, k /)) matching_bins(i_filter_surf) = OUT_TOP - filter_index = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 + filter_index = sum((matching_bins(1:size(t % filters)) - 1) & + * t % stride) + 1 write(UNIT=unit_tally, FMT='(5X,A,T35,A,"+/- ",A)') & "Outgoing Current to Top", & to_str(t % results(1,filter_index) % sum), & @@ -1112,329 +1105,4 @@ contains end subroutine write_surface_current -!=============================================================================== -! GET_LABEL returns a label for a cell/surface/etc given a tally, filter type, -! and corresponding bin -!=============================================================================== - - function get_label(t, i_filter) result(label) - type(TallyObject), intent(in) :: t ! tally object - integer, intent(in) :: i_filter ! index in filters array - character(MAX_LINE_LEN) :: label ! user-specified identifier - - integer :: i ! index in cells/surfaces/etc array - integer :: bin - integer :: offset - integer, allocatable :: ijk(:) ! indices in mesh - real(8) :: E0 ! lower bound for energy bin - real(8) :: E1 ! upper bound for energy bin - type(RegularMesh), pointer :: m - type(Universe), pointer :: univ - - bin = matching_bins(i_filter) - - select case(t % filters(i_filter) % type) - case (FILTER_UNIVERSE) - i = t % filters(i_filter) % int_bins(bin) - label = to_str(universes(i) % id) - case (FILTER_MATERIAL) - i = t % filters(i_filter) % int_bins(bin) - label = to_str(materials(i) % id) - case (FILTER_CELL, FILTER_CELLBORN) - i = t % filters(i_filter) % int_bins(bin) - label = to_str(cells(i) % id) - case (FILTER_DISTRIBCELL) - label = '' - univ => universes(BASE_UNIVERSE) - offset = 0 - call find_offset(t % filters(i_filter) % int_bins(1), & - univ, bin-1, offset, label) - case (FILTER_SURFACE) - i = t % filters(i_filter) % int_bins(bin) - label = to_str(surfaces(i)%obj%id) - case (FILTER_MESH) - m => meshes(t % filters(i_filter) % int_bins(1)) - allocate(ijk(m % n_dimension)) - call bin_to_mesh_indices(m, bin, ijk) - if (m % n_dimension == 2) then - label = "Index (" // trim(to_str(ijk(1))) // ", " // & - trim(to_str(ijk(2))) // ")" - elseif (m % n_dimension == 3) then - label = "Index (" // trim(to_str(ijk(1))) // ", " // & - trim(to_str(ijk(2))) // ", " // trim(to_str(ijk(3))) // ")" - end if - case (FILTER_ENERGYIN, FILTER_ENERGYOUT, FILTER_MU, FILTER_POLAR, & - FILTER_AZIMUTHAL) - E0 = t % filters(i_filter) % real_bins(bin) - E1 = t % filters(i_filter) % real_bins(bin + 1) - label = "[" // trim(to_str(E0)) // ", " // trim(to_str(E1)) // ")" - case (FILTER_DELAYEDGROUP) - i = t % filters(i_filter) % int_bins(bin) - label = to_str(i) - end select - - end function get_label - -!=============================================================================== -! FIND_OFFSET uses a given map number, a target cell ID, and a target offset -! to build a string which is the path from the base universe to the target cell -! with the given offset -!=============================================================================== - - recursive subroutine find_offset(goal, univ, final, offset, path) - - integer, intent(in) :: goal ! The target cell index - type(Universe), intent(in) :: univ ! Universe to begin search - integer, intent(in) :: final ! Target offset - integer, intent(inout) :: offset ! Current offset - character(*), intent(inout) :: path ! Path to offset - - integer :: map ! Index in maps vector - integer :: i, j ! Index over cells - integer :: k, l, m ! Indices in lattice - integer :: old_k, old_l, old_m ! Previous indices in lattice - integer :: n_x, n_y, n_z ! Lattice cell array dimensions - integer :: n ! Number of cells to search - integer :: cell_index ! Index in cells array - integer :: lat_offset ! Offset from lattice - integer :: temp_offset ! Looped sum of offsets - logical :: this_cell = .false. ! Advance in this cell? - logical :: later_cell = .false. ! Fill cells after this one? - type(Cell), pointer :: c ! Pointer to current cell - type(Universe), pointer :: next_univ ! Next universe to loop through - class(Lattice), pointer :: lat ! Pointer to current lattice - - ! Get the distribcell index for this cell - map = cells(goal) % distribcell_index - - n = univ % n_cells - - ! Write to the geometry stack - if (univ%id == 0) then - path = trim(path) // to_str(univ%id) - else - path = trim(path) // "->" // to_str(univ%id) - end if - - ! Look through all cells in this universe - do i = 1, n - ! If the cell matches the goal and the offset matches final, write to the - ! geometry stack - if (univ % cells(i) == goal .and. offset == final) then - c => cells(univ % cells(i)) - path = trim(path) // "->" // to_str(c % id) - return - end if - end do - - ! Find the fill cell or lattice cell that we need to enter - do i = 1, n - - later_cell = .false. - - cell_index = univ % cells(i) - c => cells(cell_index) - - this_cell = .false. - - ! If we got here, we still think the target is in this universe - ! or further down, but it's not this exact cell. - ! Compare offset to next cell to see if we should enter this cell - if (i /= n) then - - do j = i+1, n - - cell_index = univ % cells(j) - c => cells(cell_index) - - ! Skip normal cells which do not have offsets - if (c % type == CELL_NORMAL) then - cycle - end if - - ! Break loop once we've found the next cell with an offset - exit - end do - - ! Ensure we didn't just end the loop by iteration - if (c % type /= CELL_NORMAL) then - - ! There are more cells in this universe that it could be in - later_cell = .true. - - ! Two cases, lattice or fill cell - if (c % type == CELL_FILL) then - temp_offset = c % offset(map) - - ! Get the offset of the first lattice location - else - lat => lattices(c % fill) % obj - temp_offset = lat % offset(map, 1, 1, 1) - end if - - ! If the final offset is in the range of offset - temp_offset+offset - ! then the goal is in this cell - if (final < temp_offset + offset) then - this_cell = .true. - end if - end if - end if - - if (n == 1 .and. c % type /= CELL_NORMAL) then - this_cell = .true. - end if - - if (.not. later_cell) then - this_cell = .true. - end if - - ! Get pointer to THIS cell because target must be in this cell - if (this_cell) then - - cell_index = univ % cells(i) - c => cells(cell_index) - - path = trim(path) // "->" // to_str(c%id) - - ! ==================================================================== - ! CELL CONTAINS LOWER UNIVERSE, RECURSIVELY FIND CELL - if (c % type == CELL_FILL) then - - ! Enter this cell to update the current offset - offset = c % offset(map) + offset - - next_univ => universes(c % fill) - call find_offset(goal, next_univ, final, offset, path) - return - - ! ==================================================================== - ! CELL CONTAINS LATTICE, RECURSIVELY FIND CELL - elseif (c % type == CELL_LATTICE) then - - ! Set current lattice - lat => lattices(c % fill) % obj - - select type (lat) - - ! ================================================================== - ! RECTANGULAR LATTICES - type is (RectLattice) - - ! Write to the geometry stack - path = trim(path) // "->" // to_str(lat%id) - - n_x = lat % n_cells(1) - n_y = lat % n_cells(2) - n_z = lat % n_cells(3) - old_m = 1 - old_l = 1 - old_k = 1 - - ! Loop over lattice coordinates - do k = 1, n_x - do l = 1, n_y - do m = 1, n_z - - if (final >= lat % offset(map, k, l, m) + offset) then - if (k == n_x .and. l == n_y .and. m == n_z) then - ! This is last lattice cell, so target must be here - lat_offset = lat % offset(map, k, l, m) - offset = offset + lat_offset - next_univ => universes(lat % universes(k, l, m)) - path = trim(path) // "(" // trim(to_str(k)) // & - "," // trim(to_str(l)) // "," // & - trim(to_str(m)) // ")" - call find_offset(goal, next_univ, final, offset, path) - return - else - old_m = m - old_l = l - old_k = k - cycle - end if - else - ! Target is at this lattice position - lat_offset = lat % offset(map, old_k, old_l, old_m) - offset = offset + lat_offset - next_univ => universes(lat % universes(old_k, old_l, old_m)) - path = trim(path) // "(" // trim(to_str(old_k)) // & - "," // trim(to_str(old_l)) // "," // & - trim(to_str(old_m)) // ")" - call find_offset(goal, next_univ, final, offset, path) - return - end if - - end do - end do - end do - - ! ================================================================== - ! HEXAGONAL LATTICES - type is (HexLattice) - - ! Write to the geometry stack - path = trim(path) // "->" // to_str(lat%id) - - n_z = lat % n_axial - n_y = 2 * lat % n_rings - 1 - n_x = 2 * lat % n_rings - 1 - old_m = 1 - old_l = 1 - old_k = 1 - - ! Loop over lattice coordinates - do m = 1, n_z - do l = 1, n_y - do k = 1, n_x - - ! This array position is never used - if (k + l < lat % n_rings + 1) then - cycle - ! This array position is never used - else if (k + l > 3*lat % n_rings - 1) then - cycle - end if - - if (final >= lat % offset(map, k, l, m) + offset) then - if (k == lat % n_rings .and. l == n_y .and. m == n_z) then - ! This is last lattice cell, so target must be here - lat_offset = lat % offset(map, k, l, m) - offset = offset + lat_offset - next_univ => universes(lat % universes(k, l, m)) - path = trim(path) // "(" // & - trim(to_str(k - lat % n_rings)) // "," // & - trim(to_str(l - lat % n_rings)) // "," // & - trim(to_str(m)) // ")" - call find_offset(goal, next_univ, final, offset, path) - return - else - old_m = m - old_l = l - old_k = k - cycle - end if - else - ! Target is at this lattice position - lat_offset = lat % offset(map, old_k, old_l, old_m) - offset = offset + lat_offset - next_univ => universes(lat % universes(old_k, old_l, old_m)) - path = trim(path) // "(" // & - trim(to_str(old_k - lat % n_rings)) // "," // & - trim(to_str(old_l - lat % n_rings)) // "," // & - trim(to_str(old_m)) // ")" - call find_offset(goal, next_univ, final, offset, path) - return - end if - - end do - end do - end do - - end select - - end if - end if - end do - end subroutine find_offset - end module output diff --git a/src/particle_header.F90 b/src/particle_header.F90 index a313c6ed5..ee854aea3 100644 --- a/src/particle_header.F90 +++ b/src/particle_header.F90 @@ -59,10 +59,13 @@ module particle_header logical :: alive ! is particle alive? ! Pre-collision physical data - real(8) :: last_xyz(3) ! previous coordinates - real(8) :: last_uvw(3) ! previous direction coordinates - real(8) :: last_wgt ! pre-collision particle weight - real(8) :: absorb_wgt ! weight absorbed for survival biasing + real(8) :: last_xyz_current(3) ! coordinates of the last collision or + ! reflective/periodic surface crossing + ! for current tallies + real(8) :: last_xyz(3) ! previous coordinates + real(8) :: last_uvw(3) ! previous direction coordinates + real(8) :: last_wgt ! pre-collision particle weight + real(8) :: absorb_wgt ! weight absorbed for survival biasing ! What event last took place logical :: fission ! did the particle cause implicit fission @@ -193,20 +196,21 @@ contains call this % initialize() ! copy attributes from source bank site - this % wgt = src % wgt - this % last_wgt = src % wgt - this % coord(1) % xyz = src % xyz - this % coord(1) % uvw = src % uvw - this % last_xyz = src % xyz - this % last_uvw = src % uvw + this % wgt = src % wgt + this % last_wgt = src % wgt + this % coord(1) % xyz = src % xyz + this % coord(1) % uvw = src % uvw + this % last_xyz_current = src % xyz + this % last_xyz = src % xyz + this % last_uvw = src % uvw if (run_CE) then - this % E = src % E + this % E = src % E else - this % g = int(src % E) - this % last_g = int(src % E) - this % E = energy_bin_avg(this % g) + this % g = int(src % E) + this % last_g = int(src % E) + this % E = energy_bin_avg(this % g) end if - this % last_E = this % E + this % last_E = this % E end subroutine initialize_from_source diff --git a/src/particle_restart.F90 b/src/particle_restart.F90 index 846d37160..a1c32f723 100644 --- a/src/particle_restart.F90 +++ b/src/particle_restart.F90 @@ -107,11 +107,12 @@ contains end if ! Set particle last attributes - p % last_wgt = p % wgt - p % last_xyz = p % coord(1)%xyz - p % last_uvw = p % coord(1)%uvw - p % last_E = p % E - p % last_g = p % g + p % last_wgt = p % wgt + p % last_xyz_current = p % coord(1)%xyz + p % last_xyz = p % coord(1)%xyz + p % last_uvw = p % coord(1)%uvw + p % last_E = p % E + p % last_g = p % g ! Close hdf5 file call file_close(file_id) diff --git a/src/state_point.F90 b/src/state_point.F90 index 87ef4ead7..abe034897 100644 --- a/src/state_point.F90 +++ b/src/state_point.F90 @@ -237,57 +237,13 @@ contains end select call write_dataset(tally_group, "n_realizations", & tally % n_realizations) - call write_dataset(tally_group, "n_filters", tally % n_filters) + call write_dataset(tally_group, "n_filters", size(tally % filters)) ! Write filter information - FILTER_LOOP: do j = 1, tally % n_filters + FILTER_LOOP: do j = 1, size(tally % filters) filter_group = create_group(tally_group, "filter " // & trim(to_str(j))) - - ! Write name of type - select case (tally % filters(j) % type) - case(FILTER_UNIVERSE) - call write_dataset(filter_group, "type", "universe") - case(FILTER_MATERIAL) - call write_dataset(filter_group, "type", "material") - case(FILTER_CELL) - call write_dataset(filter_group, "type", "cell") - case(FILTER_CELLBORN) - call write_dataset(filter_group, "type", "cellborn") - case(FILTER_SURFACE) - call write_dataset(filter_group, "type", "surface") - case(FILTER_MESH) - call write_dataset(filter_group, "type", "mesh") - case(FILTER_ENERGYIN) - call write_dataset(filter_group, "type", "energy") - case(FILTER_ENERGYOUT) - call write_dataset(filter_group, "type", "energyout") - case(FILTER_MU) - call write_dataset(filter_group, "type", "mu") - case(FILTER_POLAR) - call write_dataset(filter_group, "type", "polar") - case(FILTER_AZIMUTHAL) - call write_dataset(filter_group, "type", "azimuthal") - case(FILTER_DISTRIBCELL) - call write_dataset(filter_group, "type", "distribcell") - case(FILTER_DELAYEDGROUP) - call write_dataset(filter_group, "type", "delayedgroup") - end select - - call write_dataset(filter_group, "n_bins", & - tally % filters(j) % n_bins) - if (tally % filters(j) % type == FILTER_ENERGYIN .or. & - tally % filters(j) % type == FILTER_ENERGYOUT .or. & - tally % filters(j) % type == FILTER_MU .or. & - tally % filters(j) % type == FILTER_POLAR .or. & - tally % filters(j) % type == FILTER_AZIMUTHAL) then - call write_dataset(filter_group, "bins", & - tally % filters(j) % real_bins) - else - call write_dataset(filter_group, "bins", & - tally % filters(j) % int_bins) - end if - + call tally % filters(j) % obj % to_statepoint(filter_group) call close_group(filter_group) end do FILTER_LOOP diff --git a/src/summary.F90 b/src/summary.F90 index 9ad9aa23c..b02336d97 100644 --- a/src/summary.F90 +++ b/src/summary.F90 @@ -3,7 +3,7 @@ module summary use constants use endf, only: reaction_name use geometry_header, only: Cell, Universe, Lattice, RectLattice, & - &HexLattice, BASE_UNIVERSE + &HexLattice use global use hdf5_interface use material_header, only: Material @@ -13,7 +13,6 @@ module summary use surface_header use string, only: to_str use tally_header, only: TallyObject - use output, only: find_offset use hdf5 @@ -584,10 +583,6 @@ contains type(RegularMesh), pointer :: m type(TallyObject), pointer :: t - integer :: offset ! distibcell offset - character(MAX_LINE_LEN), allocatable :: paths(:) ! distribcell paths array - character(MAX_LINE_LEN) :: path ! distribcell path - tallies_group = create_group(file_id, "tallies") ! Write total number of meshes @@ -628,74 +623,11 @@ contains call write_dataset(tally_group, "name", t%name) ! Write number of filters - call write_dataset(tally_group, "n_filters", t%n_filters) + call write_dataset(tally_group, "n_filters", size(t % filters)) - FILTER_LOOP: do j = 1, t % n_filters + FILTER_LOOP: do j = 1, size(t % filters) filter_group = create_group(tally_group, "filter " // trim(to_str(j))) - - ! Write number of bins for this filter - call write_dataset(filter_group, "n_bins", t % filters(j) % n_bins) - - ! Write filter bins - if (t % filters(j) % type == FILTER_ENERGYIN .or. & - t % filters(j)% type == FILTER_ENERGYOUT .or. & - t % filters(j) % type == FILTER_MU .or. & - t % filters(j) % type == FILTER_POLAR .or. & - t % filters(j) % type == FILTER_AZIMUTHAL) then - call write_dataset(filter_group, "bins", t % filters(j) % real_bins) - else - call write_dataset(filter_group, "bins", t % filters(j) % int_bins) - end if - - ! Write paths to reach each distribcell instance - if (t % filters(j) % type == FILTER_DISTRIBCELL) then - ! Allocate array of strings for each distribcell path - allocate(paths(t % filters(j) % n_bins)) - - ! Store path for each distribcell instance - do k = 1, t % filters(j) % n_bins - path = '' - offset = 1 - call find_offset(t % filters(j) % int_bins(1), & - universes(BASE_UNIVERSE), k, offset, path) - paths(k) = path - end do - - ! Write array of distribcell paths to summary file - call write_dataset(filter_group, "paths", paths) - deallocate(paths) - end if - - ! Write name of type - select case (t%filters(j)%type) - case(FILTER_UNIVERSE) - call write_dataset(filter_group, "type", "universe") - case(FILTER_MATERIAL) - call write_dataset(filter_group, "type", "material") - case(FILTER_CELL) - call write_dataset(filter_group, "type", "cell") - case(FILTER_CELLBORN) - call write_dataset(filter_group, "type", "cellborn") - case(FILTER_SURFACE) - call write_dataset(filter_group, "type", "surface") - case(FILTER_MESH) - call write_dataset(filter_group, "type", "mesh") - case(FILTER_ENERGYIN) - call write_dataset(filter_group, "type", "energy") - case(FILTER_ENERGYOUT) - call write_dataset(filter_group, "type", "energyout") - case(FILTER_DISTRIBCELL) - call write_dataset(filter_group, "type", "distribcell") - case(FILTER_MU) - call write_dataset(filter_group, "type", "mu") - case(FILTER_POLAR) - call write_dataset(filter_group, "type", "polar") - case(FILTER_AZIMUTHAL) - call write_dataset(filter_group, "type", "azimuthal") - case(FILTER_DELAYEDGROUP) - call write_dataset(filter_group, "type", "delayedgroup") - end select - + call t % filters(j) % obj % to_summary(filter_group) call close_group(filter_group) end do FILTER_LOOP diff --git a/src/tally.F90 b/src/tally.F90 index 5f9268216..407ac4019 100644 --- a/src/tally.F90 +++ b/src/tally.F90 @@ -14,7 +14,8 @@ module tally use particle_header, only: LocalCoord, Particle use search, only: binary_search use string, only: to_str - use tally_header, only: TallyResult, TallyMapItem, TallyMapElement + use tally_header, only: TallyResult + use tally_filter #ifdef MPI use message_passing @@ -28,7 +29,6 @@ module tally procedure(score_general_), pointer :: score_general => null() procedure(score_analog_tally_), pointer :: score_analog_tally => null() - procedure(get_scoring_bins_), pointer :: get_scoring_bins => null() abstract interface subroutine score_general_(p, t, start_index, filter_index, i_nuclide, & @@ -48,13 +48,6 @@ module tally import Particle type(Particle), intent(in) :: p end subroutine score_analog_tally_ - - subroutine get_scoring_bins_(p, i_tally, found_bin) - import Particle - type(Particle), intent(in) :: p - integer, intent(in) :: i_tally - logical, intent(out) :: found_bin - end subroutine get_scoring_bins_ end interface contains @@ -68,18 +61,18 @@ contains if (run_CE) then score_general => score_general_ce score_analog_tally => score_analog_tally_ce - get_scoring_bins => get_scoring_bins_ce else score_general => score_general_mg score_analog_tally => score_analog_tally_mg - get_scoring_bins => get_scoring_bins_mg end if end subroutine init_tally_routines !=============================================================================== ! SCORE_GENERAL* adds scores to the tally array for the given filter and -! nuclide. This will work for either analog or tracklength tallies. Note that -! atom_density and flux are not used for analog tallies. +! nuclide. This function is called by all volume tallies. For analog tallies, +! the flux estimate depends on the score type so the flux argument is really +! just used for filter weights. The atom_density argument is not used for +! analog tallies. !=============================================================================== subroutine score_general_ce(p, t, start_index, filter_index, i_nuclide, & @@ -137,7 +130,7 @@ contains else score = p % last_wgt end if - score = score / material_xs % total + score = score / material_xs % total * flux else ! For flux, we need no cross section @@ -153,9 +146,9 @@ contains if (survival_biasing) then ! We need to account for the fact that some weight was already ! absorbed - score = p % last_wgt + p % absorb_wgt + score = p % last_wgt + p % absorb_wgt * flux else - score = p % last_wgt + score = p % last_wgt * flux end if else @@ -190,7 +183,7 @@ contains ! Score the flux weighted inverse velocity with velocity in units of ! cm/s score = score / material_xs % total & - / (sqrt(TWO * E / (MASS_NEUTRON_MEV)) * C_LIGHT * 100.0_8) + / (sqrt(TWO * E / (MASS_NEUTRON_MEV)) * C_LIGHT * 100.0_8) * flux else ! For inverse velocity, we don't need a cross section. The velocity is @@ -206,7 +199,7 @@ contains ! Since only scattering events make it here, again we can use ! the weight entering the collision as the estimator for the ! reaction rate - score = p % last_wgt + score = p % last_wgt * flux else ! Note SCORE_SCATTER_N not available for tracklength/collision. @@ -229,7 +222,7 @@ contains ! Since only scattering events make it here, again we can use ! the weight entering the collision as the estimator for the ! reaction rate - score = p % last_wgt + score = p % last_wgt * flux case (SCORE_SCATTER_YN) @@ -242,7 +235,7 @@ contains ! Since only scattering events make it here, again we can use ! the weight entering the collision as the estimator for the ! reaction rate - score = p % last_wgt + score = p % last_wgt * flux case (SCORE_NU_SCATTER, SCORE_NU_SCATTER_N) @@ -256,7 +249,7 @@ contains (p % event_MT >= N_N1 .and. p % event_MT <= N_NC)) then ! Don't waste time on very common reactions we know have multiplicities ! of one. - score = p % last_wgt + score = p % last_wgt * flux else m = nuclides(p%event_nuclide)%reaction_index% & get_key(p % event_MT) @@ -266,11 +259,11 @@ contains select type (yield => rxn % products(1) % yield) type is (Constant1D) ! Grab the yield from the reaction - score = p % last_wgt * yield % y + score = p % last_wgt * yield % y * flux class default ! the yield was already incorporated in to p % wgt per the ! scattering routine - score = p % wgt + score = p % wgt * flux end select end associate end if @@ -290,7 +283,7 @@ contains (p % event_MT >= N_N1 .and. p % event_MT <= N_NC)) then ! Don't waste time on very common reactions we know have multiplicities ! of one. - score = p % last_wgt + score = p % last_wgt * flux else m = nuclides(p%event_nuclide)%reaction_index% & get_key(p % event_MT) @@ -300,11 +293,11 @@ contains select type (yield => rxn % products(1) % yield) type is (Constant1D) ! Grab the yield from the reaction - score = p % last_wgt * yield % y + score = p % last_wgt * yield % y * flux class default ! the yield was already incorporated in to p % wgt per the ! scattering routine - score = p % wgt + score = p % wgt * flux end select end associate end if @@ -324,7 +317,7 @@ contains (p % event_MT >= N_N1 .and. p % event_MT <= N_NC)) then ! Don't waste time on very common reactions we know have multiplicities ! of one. - score = p % last_wgt + score = p % last_wgt * flux else m = nuclides(p%event_nuclide)%reaction_index% & get_key(p % event_MT) @@ -334,11 +327,11 @@ contains select type (yield => rxn % products(1) % yield) type is (Constant1D) ! Grab the yield from the reaction - score = p % last_wgt * yield % y + score = p % last_wgt * yield % y * flux class default ! the yield was already incorporated in to p % wgt per the ! scattering routine - score = p % wgt + score = p % wgt * flux end select end associate end if @@ -349,13 +342,13 @@ contains if (survival_biasing) then ! No absorption events actually occur if survival biasing is on -- ! just use weight absorbed in survival biasing - score = p % absorb_wgt + score = p % absorb_wgt * flux else ! Skip any event where the particle wasn't absorbed if (p % event == EVENT_SCATTER) cycle SCORE_LOOP ! All fission and absorption events will contribute here, so we ! can just use the particle's weight entering the collision - score = p % last_wgt + score = p % last_wgt * flux end if else @@ -375,7 +368,7 @@ contains ! fission if (micro_xs(p % event_nuclide) % absorption > ZERO) then score = p % absorb_wgt * micro_xs(p % event_nuclide) % fission & - / micro_xs(p % event_nuclide) % absorption + / micro_xs(p % event_nuclide) % absorption * flux else score = ZERO end if @@ -386,7 +379,7 @@ contains ! particle's weight entering the collision as the estimate for the ! fission reaction rate score = p % last_wgt * micro_xs(p % event_nuclide) % fission & - / micro_xs(p % event_nuclide) % absorption + / micro_xs(p % event_nuclide) % absorption * flux end if else @@ -417,7 +410,7 @@ contains ! nu-fission if (micro_xs(p % event_nuclide) % absorption > ZERO) then score = p % absorb_wgt * micro_xs(p % event_nuclide) % & - nu_fission / micro_xs(p % event_nuclide) % absorption + nu_fission / micro_xs(p % event_nuclide) % absorption * flux else score = ZERO end if @@ -429,7 +422,7 @@ contains ! score the number of particles that were banked in the fission ! bank. Since this was weighted by 1/keff, we multiply by keff ! to get the proper score. - score = keff * p % wgt_bank + score = keff * p % wgt_bank * flux end if else @@ -478,6 +471,13 @@ contains end if else + ! make sure the correct energy is used + if (t % estimator == ESTIMATOR_TRACKLENGTH) then + E = p % E + else + E = p % last_E + end if + if (i_nuclide > 0) then score = micro_xs(i_nuclide) % fission * nuclides(i_nuclide) % & nu(E, EMISSION_PROMPT) * atom_density * flux @@ -534,23 +534,28 @@ contains ! Check if the delayed group filter is present if (dg_filter > 0) then + select type(filt => t % filters(dg_filter) % obj) + type is (DelayedGroupFilter) - ! Loop over all delayed group bins and tally to them - ! individually - do d_bin = 1, t % filters(dg_filter) % n_bins + ! Loop over all delayed group bins and tally to them + ! individually + do d_bin = 1, filt % n_bins - ! Get the delayed group for this bin - d = t % filters(dg_filter) % int_bins(d_bin) + ! Get the delayed group for this bin + d = filt % groups(d_bin) - ! Compute the yield for this delayed group - yield = nuclides(p % event_nuclide) % nu(E, EMISSION_DELAYED, d) + ! Compute the yield for this delayed group + yield = nuclides(p % event_nuclide) & + % nu(E, EMISSION_DELAYED, d) - ! Compute the score and tally to bin - score = p % absorb_wgt * yield * micro_xs(p % event_nuclide) & - % fission / micro_xs(p % event_nuclide) % absorption - call score_fission_delayed_dg(t, d_bin, score, score_index) - end do - cycle SCORE_LOOP + ! Compute the score and tally to bin + score = p % absorb_wgt * yield & + * micro_xs(p % event_nuclide) % fission & + / micro_xs(p % event_nuclide) % absorption + call score_fission_delayed_dg(t, d_bin, score, score_index) + end do + cycle SCORE_LOOP + end select else ! If the delayed group filter is not present, compute the score ! by multiplying the absorbed weight by the fraction of the @@ -574,18 +579,22 @@ contains ! Check if the delayed group filter is present if (dg_filter > 0) then + select type(filt => t % filters(dg_filter) % obj) + type is (DelayedGroupFilter) - ! Loop over all delayed group bins and tally to them individually - do d_bin = 1, t % filters(dg_filter) % n_bins + ! Loop over all delayed group bins and tally to them + ! individually + do d_bin = 1, filt % n_bins - ! Get the delayed group for this bin - d = t % filters(dg_filter) % int_bins(d_bin) + ! Get the delayed group for this bin + d = filt % groups(d_bin) - ! Compute the score and tally to bin - score = keff * p % wgt_bank / p % n_bank * p % n_delayed_bank(d) - call score_fission_delayed_dg(t, d_bin, score, score_index) - end do - cycle SCORE_LOOP + ! Compute the score and tally to bin + score = keff * p % wgt_bank / p % n_bank * p % n_delayed_bank(d) + call score_fission_delayed_dg(t, d_bin, score, score_index) + end do + cycle SCORE_LOOP + end select else ! Add the contribution from all delayed groups @@ -599,22 +608,26 @@ contains ! Check if the delayed group filter is present if (dg_filter > 0) then + select type(filt => t % filters(dg_filter) % obj) + type is (DelayedGroupFilter) - ! Loop over all delayed group bins and tally to them individually - do d_bin = 1, t % filters(dg_filter) % n_bins + ! Loop over all delayed group bins and tally to them + ! individually + do d_bin = 1, filt % n_bins - ! Get the delayed group for this bin - d = t % filters(dg_filter) % int_bins(d_bin) + ! Get the delayed group for this bin + d = filt % groups(d_bin) - ! Compute the yield for this delayed group - yield = nuclides(i_nuclide) % nu(E, EMISSION_DELAYED, d) + ! Compute the yield for this delayed group + yield = nuclides(i_nuclide) % nu(E, EMISSION_DELAYED, d) - ! Compute the score and tally to bin - score = micro_xs(i_nuclide) % fission * yield * & - atom_density * flux - call score_fission_delayed_dg(t, d_bin, score, score_index) - end do - cycle SCORE_LOOP + ! Compute the score and tally to bin + score = micro_xs(i_nuclide) % fission * yield * & + atom_density * flux + call score_fission_delayed_dg(t, d_bin, score, score_index) + end do + cycle SCORE_LOOP + end select else ! If the delayed group filter is not present, compute the score @@ -628,31 +641,35 @@ contains ! Check if the delayed group filter is present if (dg_filter > 0) then + select type(filt => t % filters(dg_filter) % obj) + type is (DelayedGroupFilter) - ! Loop over all nuclides in the current material - do l = 1, materials(p % material) % n_nuclides + ! Loop over all nuclides in the current material + do l = 1, materials(p % material) % n_nuclides - ! Get atom density - atom_density_ = materials(p % material) % atom_density(l) + ! Get atom density + atom_density_ = materials(p % material) % atom_density(l) - ! Get index in nuclides array - i_nuc = materials(p % material) % nuclide(l) + ! Get index in nuclides array + i_nuc = materials(p % material) % nuclide(l) - ! Loop over all delayed group bins and tally to them individually - do d_bin = 1, t % filters(dg_filter) % n_bins + ! Loop over all delayed group bins and tally to them + ! individually + do d_bin = 1, filt % n_bins - ! Get the delayed group for this bin - d = t % filters(dg_filter) % int_bins(d_bin) + ! Get the delayed group for this bin + d = filt % groups(d_bin) - ! Get the yield for the desired nuclide and delayed group - yield = nuclides(i_nuc) % nu(E, EMISSION_DELAYED, d) + ! Get the yield for the desired nuclide and delayed group + yield = nuclides(i_nuc) % nu(E, EMISSION_DELAYED, d) - ! Compute the score and tally to bin - score = micro_xs(i_nuc) % fission * yield * atom_density_ * flux - call score_fission_delayed_dg(t, d_bin, score, score_index) + ! Compute the score and tally to bin + score = micro_xs(i_nuc) % fission * yield * atom_density_ * flux + call score_fission_delayed_dg(t, d_bin, score, score_index) + end do end do - end do - cycle SCORE_LOOP + cycle SCORE_LOOP + end select else score = ZERO @@ -693,7 +710,7 @@ contains score = p%absorb_wgt * & nuc%reactions(nuc%index_fission(1))%Q_value * & micro_xs(p%event_nuclide)%fission / & - micro_xs(p%event_nuclide)%absorption + micro_xs(p%event_nuclide)%absorption * flux end if end associate else @@ -707,7 +724,7 @@ contains score = p%last_wgt * & nuc%reactions(nuc%index_fission(1))%Q_value * & micro_xs(p%event_nuclide)%fission / & - micro_xs(p%event_nuclide)%absorption + micro_xs(p%event_nuclide)%absorption * flux end if end associate end if @@ -745,7 +762,7 @@ contains if (t % estimator == ESTIMATOR_ANALOG) then ! Check if event MT matches if (p % event_MT /= ELASTIC) cycle SCORE_LOOP - score = p % last_wgt + score = p % last_wgt * flux else if (i_nuclide > 0) then @@ -760,7 +777,7 @@ contains ! Any other score is assumed to be a MT number. Thus, we just need ! to check if it matches the MT number of the event if (p % event_MT /= score_bin) cycle SCORE_LOOP - score = p % last_wgt + score = p % last_wgt * flux else ! Any other cross section has to be calculated on-the-fly. For @@ -916,7 +933,7 @@ contains else score = p % last_wgt end if - score = score / material_xs % total + score = score / material_xs % total * flux else ! For flux, we need no cross section @@ -939,7 +956,7 @@ contains if (i_nuclide > 0) then score = score * atom_density * & nucxs % get_xs('total', p_g, UVW=p_uvw) / & - matxs % get_xs('total', p_g, UVW=p_uvw) + matxs % get_xs('total', p_g, UVW=p_uvw) * flux end if else @@ -965,7 +982,7 @@ contains else score = p % last_wgt end if - score = score * inverse_velocities(p_g) / material_xs % total + score = score * inverse_velocities(p_g) / material_xs % total * flux else ! For inverse velocity, we need no cross section @@ -988,7 +1005,7 @@ contains ! Since only scattering events make it here, again we can use ! the weight entering the collision as the estimator for the ! reaction rate - score = p % last_wgt + score = p % last_wgt * flux ! Since we transport based on material data, the angle selected ! was not selected from the f(mu) for the nuclide. Therefore @@ -1031,7 +1048,7 @@ contains ! For scattering production, we need to use the pre-collision ! weight times the multiplicity as the estimate for the number of ! neutrons exiting a reaction with neutrons in the exit channel - score = p % wgt + score = p % wgt * flux ! Since we transport based on material data, the angle selected ! was not selected from the f(mu) for the nuclide. Therefore @@ -1061,13 +1078,13 @@ contains if (survival_biasing) then ! No absorption events actually occur if survival biasing is on -- ! just use weight absorbed in survival biasing - score = p % absorb_wgt + score = p % absorb_wgt * flux else ! Skip any event where the particle wasn't absorbed if (p % event == EVENT_SCATTER) cycle SCORE_LOOP ! All fission and absorption events will contribute here, so we ! can just use the particle's weight entering the collision - score = p % last_wgt + score = p % last_wgt * flux end if if (i_nuclide > 0) then score = score * atom_density * & @@ -1090,24 +1107,24 @@ contains ! No fission events occur if survival biasing is on -- need to ! calculate fraction of absorptions that would have resulted in ! fission - score = p % absorb_wgt + score = p % absorb_wgt * flux else ! Skip any non-absorption events if (p % event == EVENT_SCATTER) cycle SCORE_LOOP ! All fission events will contribute, so again we can use ! particle's weight entering the collision as the estimate for the ! fission reaction rate - score = p % last_wgt + score = p % last_wgt * flux end if if (i_nuclide > 0) then - score = score * atom_density * & - nucxs % get_xs('fission', p_g, UVW=p_uvw) / & - matxs % get_xs('absorption', p_g, UVW=p_uvw) - else - score = score * & - matxs % get_xs('fission', p_g, UVW=p_uvw) / & - matxs % get_xs('absorption', p_g, UVW=p_uvw) - end if + score = score * atom_density * & + nucxs % get_xs('fission', p_g, UVW=p_uvw) / & + matxs % get_xs('absorption', p_g, UVW=p_uvw) + else + score = score * & + matxs % get_xs('fission', p_g, UVW=p_uvw) / & + matxs % get_xs('absorption', p_g, UVW=p_uvw) + end if else if (i_nuclide > 0) then score = nucxs % get_xs('fission', p_g, UVW=p_uvw) * & @@ -1137,7 +1154,7 @@ contains ! No fission events occur if survival biasing is on -- need to ! calculate fraction of absorptions that would have resulted in ! nu-fission - score = p % absorb_wgt + score = p % absorb_wgt * flux if (i_nuclide > 0) then score = score * atom_density * & nucxs % get_xs('nu_fission', p_g, UVW=p_uvw) / & @@ -1155,7 +1172,7 @@ contains ! score the number of particles that were banked in the fission ! bank. Since this was weighted by 1/keff, we multiply by keff ! to get the proper score. - score = keff * p % wgt_bank + score = keff * p % wgt_bank * flux if (i_nuclide > 0) then score = score * atom_density * & nucxs % get_xs('fission', p_g, UVW=p_uvw) / & @@ -1179,14 +1196,14 @@ contains ! No fission events occur if survival biasing is on -- need to ! calculate fraction of absorptions that would have resulted in ! fission - score = p % absorb_wgt + score = p % absorb_wgt * flux else ! Skip any non-absorption events if (p % event == EVENT_SCATTER) cycle SCORE_LOOP ! All fission events will contribute, so again we can use ! particle's weight entering the collision as the estimate for the ! fission reaction rate - score = p % last_wgt + score = p % last_wgt * flux end if if (i_nuclide > 0) then score = score * atom_density * & @@ -1402,11 +1419,12 @@ contains integer :: i integer :: i_tally + integer :: i_filt integer :: k ! loop index for nuclide bins ! position during the loop integer :: filter_index ! single index for single bin integer :: i_nuclide ! index in nuclides array - logical :: found_bin ! scoring bin found? + real(8) :: filter_weight ! combined weight of all filters type(TallyObject), pointer :: t ! A loop over all tallies is necessary because we need to simultaneously @@ -1417,63 +1435,103 @@ contains i_tally = active_analog_tallies % get_item(i) t => tallies(i_tally) - ! ======================================================================= - ! DETERMINE SCORING BIN COMBINATION + ! Find the first bin in each filter. There may be more than one matching + ! bin per filter, but we'll deal with those later. + do i_filt = 1, size(t % filters) + call t % filters(i_filt) % obj % get_next_bin(p, t % estimator, & + NO_BIN_FOUND, matching_bins(i_filt), filter_weights(i_filt)) + ! If there are no valid bins for this filter, then there is nothing to + ! score and we can move on to the next tally. + if (matching_bins(i_filt) == NO_BIN_FOUND) cycle TALLY_LOOP + end do - call get_scoring_bins(p, i_tally, found_bin) - if (.not. found_bin) cycle + ! ======================================================================== + ! Loop until we've covered all valid bins on each of the filters. - ! ======================================================================= - ! CALCULATE RESULTS AND ACCUMULATE TALLY + FILTER_LOOP: do - ! If we have made it here, we have a scoring combination of bins for this - ! tally -- now we need to determine where in the results array we should - ! be accumulating the tally values + ! Determine scoring index and weight for this filter combination + filter_index = sum((matching_bins(1:size(t % filters)) - 1) & + * t % stride) + 1 + filter_weight = product(filter_weights(:size(t % filters))) - ! Determine scoring index for this filter combination - filter_index = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 + ! ====================================================================== + ! Nuclide logic - ! Check for nuclide bins - k = 0 - NUCLIDE_LOOP: do while (k < t % n_nuclide_bins) + ! Check for nuclide bins + k = 0 + NUCLIDE_LOOP: do while (k < t % n_nuclide_bins) - ! Increment the index in the list of nuclide bins - k = k + 1 + ! Increment the index in the list of nuclide bins + k = k + 1 + + if (t % all_nuclides) then + ! In the case that the user has requested to tally all nuclides, we + ! can take advantage of the fact that we know exactly how nuclide + ! bins correspond to nuclide indices. + if (k == 1) then + ! If we just entered, set the nuclide bin index to the index in + ! the nuclides array since this will match the index in the + ! nuclide bin array. + k = p % event_nuclide + elseif (k == p % event_nuclide + 1) then + ! After we've tallied the individual nuclide bin, we also need + ! to contribute to the total material bin which is the last bin + k = n_nuclides_total + 1 + else + ! After we've tallied in both the individual nuclide bin and the + ! total material bin, we're done + exit + end if - if (t % all_nuclides) then - ! In the case that the user has requested to tally all nuclides, we - ! can take advantage of the fact that we know exactly how nuclide - ! bins correspond to nuclide indices. - if (k == 1) then - ! If we just entered, set the nuclide bin index to the index in - ! the nuclides array since this will match the index in the - ! nuclide bin array. - k = p % event_nuclide - elseif (k == p % event_nuclide + 1) then - ! After we've tallied the individual nuclide bin, we also need - ! to contribute to the total material bin which is the last bin - k = n_nuclides_total + 1 else - ! After we've tallied in both the individual nuclide bin and the - ! total material bin, we're done - exit + ! If the user has explicitly specified nuclides (or specified + ! none), we need to search through the nuclide bin list one by + ! one. First we need to get the value of the nuclide bin + i_nuclide = t % nuclide_bins(k) + + ! Now compare the value against that of the colliding nuclide. + if (i_nuclide /= p % event_nuclide .and. i_nuclide /= -1) cycle end if - else - ! If the user has explicitly specified nuclides (or specified - ! none), we need to search through the nuclide bin list one by - ! one. First we need to get the value of the nuclide bin - i_nuclide = t % nuclide_bins(k) + ! Determine score for each bin + call score_general(p, t, (k-1)*t % n_score_bins, filter_index, & + i_nuclide, ZERO, filter_weight) - ! Now compare the value against that of the colliding nuclide. - if (i_nuclide /= p % event_nuclide .and. i_nuclide /= -1) cycle - end if + end do NUCLIDE_LOOP - ! Determine score for each bin - call score_general(p, t, (k-1)*t % n_score_bins, filter_index, & - i_nuclide, ZERO, ZERO) + ! ====================================================================== + ! Filter logic - end do NUCLIDE_LOOP + ! If there are no filters, then we are done. + if (size(t % filters) == 0) exit FILTER_LOOP + + ! Increment the filter bins, starting with the last filter. If we get a + ! NO_BIN_FOUND for the last filter, it means we finished all valid bins + ! for that filter, but next-to-last filter might have more than one + ! valid bin so we need to increment that one as well, and so on. + do i_filt = size(t % filters), 1, -1 + call t % filters(i_filt) % obj % get_next_bin(p, t % estimator, & + matching_bins(i_filt), matching_bins(i_filt), & + filter_weights(i_filt)) + if (matching_bins(i_filt) /= NO_BIN_FOUND) exit + end do + + ! If we got all NO_BIN_FOUNDs, then we have finished all valid bins for + ! each of the filters. Exit the loop. + if (all(matching_bins(:size(t % filters)) == NO_BIN_FOUND)) & + exit FILTER_LOOP + + ! Reset all the filters with NO_BIN_FOUND. This will set them back to + ! their first valid bin. + do i_filt = 1, size(t % filters) + if (matching_bins(i_filt) == NO_BIN_FOUND) then + call t % filters(i_filt) % obj % get_next_bin(p, t % estimator, & + matching_bins(i_filt), matching_bins(i_filt), & + filter_weights(i_filt)) + end if + end do + end do FILTER_LOOP ! If the user has specified that we can assume all tallies are spatially ! separate, this implies that once a tally has been scored to, we needn't @@ -1495,14 +1553,15 @@ contains integer :: i, m integer :: i_tally + integer :: i_filt integer :: k ! loop index for nuclide bins ! position during the loop integer :: filter_index ! single index for single bin integer :: i_nuclide ! index in nuclides array - logical :: found_bin ! scoring bin found? + real(8) :: filter_weight ! combined weight of all filters + real(8) :: atom_density type(TallyObject), pointer :: t type(Material), pointer :: mat - real(8) :: atom_density ! A loop over all tallies is necessary because we need to simultaneously ! determine different filter bins for the same tally in order to score to it @@ -1516,44 +1575,84 @@ contains ! nuclides are in the material mat => materials(p % material) - ! ======================================================================= - ! DETERMINE SCORING BIN COMBINATION + ! Find the first bin in each filter. There may be more than one matching + ! bin per filter, but we'll deal with those later. + do i_filt = 1, size(t % filters) + call t % filters(i_filt) % obj % get_next_bin(p, t % estimator, & + NO_BIN_FOUND, matching_bins(i_filt), filter_weights(i_filt)) + ! If there are no valid bins for this filter, then there is nothing to + ! score and we can move on to the next tally. + if (matching_bins(i_filt) == NO_BIN_FOUND) cycle TALLY_LOOP + end do - call get_scoring_bins(p, i_tally, found_bin) - if (.not. found_bin) cycle + ! ======================================================================== + ! Loop until we've covered all valid bins on each of the filters. - ! ======================================================================= - ! CALCULATE RESULTS AND ACCUMULATE TALLY + FILTER_LOOP: do - ! If we have made it here, we have a scoring combination of bins for this - ! tally -- now we need to determine where in the results array we should - ! be accumulating the tally values + ! Determine scoring index and weight for this filter combination + filter_index = sum((matching_bins(1:size(t % filters)) - 1) & + * t % stride) + 1 + filter_weight = product(filter_weights(:size(t % filters))) - ! Determine scoring index for this filter combination - filter_index = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 + ! ====================================================================== + ! Nuclide logic - ! Check for nuclide bins - k = 0 - NUCLIDE_LOOP: do while (k < t % n_nuclide_bins) + ! Check for nuclide bins + k = 0 + NUCLIDE_LOOP: do while (k < t % n_nuclide_bins) - ! Increment the index in the list of nuclide bins - k = k + 1 + ! Increment the index in the list of nuclide bins + k = k + 1 - i_nuclide = t % nuclide_bins(k) + i_nuclide = t % nuclide_bins(k) - ! Check to see if this nuclide was in the material of our collision. - do m = 1, mat % n_nuclides - if (mat % nuclide(m) == i_nuclide) then - atom_density = mat % atom_density(m) - exit - end if + ! Check to see if this nuclide was in the material of our collision. + do m = 1, mat % n_nuclides + if (mat % nuclide(m) == i_nuclide) then + atom_density = mat % atom_density(m) + exit + end if + end do + + ! Determine score for each bin + call score_general(p, t, (k-1)*t % n_score_bins, filter_index, & + i_nuclide, atom_density, filter_weight) + + end do NUCLIDE_LOOP + + ! ====================================================================== + ! Filter logic + + ! If there are no filters, then we are done. + if (size(t % filters) == 0) exit FILTER_LOOP + + ! Increment the filter bins, starting with the last filter. If we get a + ! NO_BIN_FOUND for the last filter, it means we finished all valid bins + ! for that filter, but next-to-last filter might have more than one + ! valid bin so we need to increment that one as well, and so on. + do i_filt = size(t % filters), 1, -1 + call t % filters(i_filt) % obj % get_next_bin(p, t % estimator, & + matching_bins(i_filt), matching_bins(i_filt), & + filter_weights(i_filt)) + if (matching_bins(i_filt) /= NO_BIN_FOUND) exit end do - ! Determine score for each bin - call score_general(p, t, (k-1)*t % n_score_bins, filter_index, & - i_nuclide, atom_density, ZERO) + ! If we got all NO_BIN_FOUNDs, then we have finished all valid bins for + ! each of the filters. Exit the loop. + if (all(matching_bins(:size(t % filters)) == NO_BIN_FOUND)) & + exit FILTER_LOOP - end do NUCLIDE_LOOP + ! Reset all the filters with NO_BIN_FOUND. This will set them back to + ! their first valid bin. + do i_filt = 1, size(t % filters) + if (matching_bins(i_filt) == NO_BIN_FOUND) then + call t % filters(i_filt) % obj % get_next_bin(p, t % estimator, & + matching_bins(i_filt), matching_bins(i_filt), & + filter_weights(i_filt)) + end if + end do + end do FILTER_LOOP ! If the user has specified that we can assume all tallies are spatially ! separate, this implies that once a tally has been scored to, we needn't @@ -1592,6 +1691,7 @@ contains integer :: k ! loop index for bank sites integer :: bin_energyout ! original outgoing energy bin integer :: i_filter ! index for matching filter bin combination + real(8) :: filter_weight ! combined weight of all filters real(8) :: score ! actual score real(8) :: E_out ! energy of fission bank site @@ -1599,77 +1699,93 @@ contains i = t % find_filter(FILTER_ENERGYOUT) bin_energyout = matching_bins(i) - ! Get number of energies on filter - n = size(t % filters(i) % real_bins) + ! declare the energyout filter type + select type(eo_filt => t % filters(i) % obj) + type is (EnergyoutFilter) - ! Since the creation of fission sites is weighted such that it is - ! expected to create n_particles sites, we need to multiply the - ! score by keff to get the true nu-fission rate. Otherwise, the sum - ! of all nu-fission rates would be ~1.0. + ! Get number of energies on filter + n = size(eo_filt % bins) - ! loop over number of particles banked - do k = 1, p % n_bank + ! Since the creation of fission sites is weighted such that it is + ! expected to create n_particles sites, we need to multiply the + ! score by keff to get the true nu-fission rate. Otherwise, the sum + ! of all nu-fission rates would be ~1.0. - ! get the delayed group - g = fission_bank(n_bank - p % n_bank + k) % delayed_group + ! loop over number of particles banked + do k = 1, p % n_bank - ! determine score based on bank site weight and keff - score = keff * fission_bank(n_bank - p % n_bank + k) % wgt + ! get the delayed group + g = fission_bank(n_bank - p % n_bank + k) % delayed_group - ! determine outgoing energy from fission bank - E_out = fission_bank(n_bank - p % n_bank + k) % E + ! determine score based on bank site weight and keff + score = keff * fission_bank(n_bank - p % n_bank + k) % wgt - ! check if outgoing energy is within specified range on filter - if (E_out < t % filters(i) % real_bins(1) .or. & - E_out > t % filters(i) % real_bins(n)) cycle + ! determine outgoing energy from fission bank + E_out = fission_bank(n_bank - p % n_bank + k) % E - ! change outgoing energy bin - matching_bins(i) = binary_search(t % filters(i) % real_bins, n, E_out) + ! check if outgoing energy is within specified range on filter + if (E_out < eo_filt % bins(1) .or. E_out > eo_filt % bins(n)) cycle - ! Case for tallying prompt neutrons - if (score_bin == SCORE_NU_FISSION .or. & - (score_bin == SCORE_PROMPT_NU_FISSION .and. g == 0)) then + ! change outgoing energy bin + Matching_bins(i) = binary_search(eo_filt % bins, n, E_out) - ! determine scoring index - i_filter = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 + ! Case for tallying prompt neutrons + if (score_bin == SCORE_NU_FISSION .or. & + (score_bin == SCORE_PROMPT_NU_FISSION .and. g == 0)) then - ! Add score to tally -!$omp atomic - t % results(i_score, i_filter) % value = & - t % results(i_score, i_filter) % value + score - - ! Case for tallying delayed emissions - else if (score_bin == SCORE_DELAYED_NU_FISSION .and. g /= 0) then - - ! Get the index of delayed group filter - j = t % find_filter(FILTER_DELAYEDGROUP) - - ! if the delayed group filter is present, tally to corresponding - ! delayed group bin if it exists - if (j > 0) then - - ! loop over delayed group bins until the corresponding bin is found - do d_bin = 1, t % filters(j) % n_bins - d = t % filters(j) % int_bins(d_bin) - - ! check whether the delayed group of the particle is equal to the - ! delayed group of this bin - if (d == g) call score_fission_delayed_dg(t, d_bin, score, i_score) - end do - - ! if the delayed group filter is not present, add score to tally - else - - ! determine scoring index - i_filter = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 + ! determine scoring index and weight for this filter combination + i_filter = sum((matching_bins(1:size(t%filters)) - 1) * t % stride) & + + 1 + filter_weight = product(filter_weights(:size(t % filters))) ! Add score to tally !$omp atomic t % results(i_score, i_filter) % value = & - t % results(i_score, i_filter) % value + score + t % results(i_score, i_filter) % value + score * filter_weight + + ! Case for tallying delayed emissions + else if (score_bin == SCORE_DELAYED_NU_FISSION .and. g /= 0) then + + ! Get the index of delayed group filter + j = t % find_filter(FILTER_DELAYEDGROUP) + + ! if the delayed group filter is present, tally to corresponding + ! delayed group bin if it exists + if (j > 0) then + + ! declare the delayed group filter type + select type(dg_filt => t % filters(j) % obj) + type is (DelayedGroupFilter) + + ! loop over delayed group bins until the corresponding bin is + ! found + do d_bin = 1, dg_filt % n_bins + d = dg_filt % groups(d_bin) + + ! check whether the delayed group of the particle is equal to + ! the delayed group of this bin + if (d == g) then + call score_fission_delayed_dg(t, d_bin, score, i_score) + end if + end do + end select + + ! if the delayed group filter is not present, add score to tally + else + + ! determine scoring index and weight for this filter combination + i_filter = sum((matching_bins(1:size(t%filters)) - 1) * t % stride)& + + 1 + filter_weight = product(filter_weights(:size(t % filters))) + + ! Add score to tally +!$omp atomic + t % results(i_score, i_filter) % value = & + t % results(i_score, i_filter) % value + score * filter_weight + end if end if - end if - end do + end do + end select ! reset outgoing energy bin and score index matching_bins(i) = bin_energyout @@ -1688,6 +1804,7 @@ contains integer :: k ! loop index for bank sites integer :: bin_energyout ! original outgoing energy bin integer :: i_filter ! index for matching filter bin combination + real(8) :: filter_weight ! combined weight of all filters real(8) :: score ! actual score integer :: gout ! energy group of fission bank site integer :: gin ! energy group of incident particle @@ -1697,60 +1814,65 @@ contains i = t % find_filter(FILTER_ENERGYOUT) bin_energyout = matching_bins(i) - ! Get number of energies on filter - n = size(t % filters(i) % real_bins) + ! Declare the filter type + select type(filt => t % filters(i) % obj) + type is (EnergyoutFilter) - ! Since the creation of fission sites is weighted such that it is - ! expected to create n_particles sites, we need to multiply the - ! score by keff to get the true nu-fission rate. Otherwise, the sum - ! of all nu-fission rates would be ~1.0. + ! Get number of energies on filter + n = size(filt % bins) - ! loop over number of particles banked - do k = 1, p % n_bank - ! determine score based on bank site weight and keff - score = keff * fission_bank(n_bank - p % n_bank + k) % wgt - if (i_nuclide > 0) then - if (survival_biasing) then - gin = p % g - else - gin = p % last_g + ! Since the creation of fission sites is weighted such that it is + ! expected to create n_particles sites, we need to multiply the + ! score by keff to get the true nu-fission rate. Otherwise, the sum + ! of all nu-fission rates would be ~1.0. + + ! loop over number of particles banked + do k = 1, p % n_bank + ! determine score based on bank site weight and keff + score = keff * fission_bank(n_bank - p % n_bank + k) % wgt + if (i_nuclide > 0) then + if (survival_biasing) then + gin = p % g + else + gin = p % last_g + end if + score = score * atom_density * & + nuclides_MG(i_nuclide) % obj % get_xs('fission', gin, & + UVW=p % last_uvw) / & + macro_xs(p % material) % obj % get_xs('fission', gin, & + UVW=p % last_uvw) end if - score = score * atom_density * & - nuclides_MG(i_nuclide) % obj % get_xs('fission', gin, & - UVW=p % last_uvw) / & - macro_xs(p % material) % obj % get_xs('fission', gin, & - UVW=p % last_uvw) - end if - if (t % energyout_matches_groups) then - ! determine outgoing energy from fission bank - gout = int(fission_bank(n_bank - p % n_bank + k) % E) + if (filt % matches_transport_groups) then + ! determine outgoing energy from fission bank + gout = int(fission_bank(n_bank - p % n_bank + k) % E) - ! change outgoing energy bin - matching_bins(i) = gout - else - ! determine outgoing energy from fission bank - E_out = energy_bin_avg(int(fission_bank(n_bank - p % n_bank + k) % E)) + ! change outgoing energy bin + matching_bins(i) = gout + else + ! determine outgoing energy from fission bank + E_out = energy_bin_avg(int(fission_bank(n_bank - p % n_bank + k) % E)) - ! check if outgoing energy is within specified range on filter - if (E_out < t % filters(i) % real_bins(1) .or. & - E_out > t % filters(i) % real_bins(n)) cycle + ! check if outgoing energy is within specified range on filter + if (E_out < filt % bins(1) .or. E_out > filt % bins(n)) cycle - ! change outgoing energy bin - matching_bins(i) = binary_search(t % filters(i) % real_bins, n, E_out) - end if + ! change outgoing energy bin + matching_bins(i) = binary_search(filt % bins, n, E_out) + end if - ! determine scoring index - i_filter = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 + ! determine scoring index and weight for this filter combination + i_filter = sum((matching_bins(1:size(t%filters)) - 1) * t % stride) + 1 + filter_weight = product(filter_weights(:size(t % filters))) - ! Add score to tally + ! Add score to tally !$omp atomic - t % results(i_score, i_filter) % value = & - t % results(i_score, i_filter) % value + score - end do + t % results(i_score, i_filter) % value = & + t % results(i_score, i_filter) % value + score * filter_weight + end do ! reset outgoing energy bin and score index matching_bins(i) = bin_energyout + end select end subroutine score_fission_eout_mg @@ -1762,23 +1884,26 @@ contains subroutine score_fission_delayed_dg(t, d_bin, score, score_index) type(TallyObject), intent(inout) :: t - integer, intent(in) :: score_index ! index for score integer, intent(in) :: d_bin ! delayed group bin index + real(8), intent(in) :: score ! actual score + integer, intent(in) :: score_index ! index for score integer :: bin_original ! original bin index integer :: filter_index ! index for matching filter bin combination - real(8) :: score ! actual score + real(8) :: filter_weight ! combined weight of all filters ! save original delayed group bin bin_original = matching_bins(t % find_filter(FILTER_DELAYEDGROUP)) matching_bins(t % find_filter(FILTER_DELAYEDGROUP)) = d_bin - ! Compute the filter index based on the modified matching_bins - filter_index = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 + ! determine scoring index and weight on the modified matching_bins + filter_index = sum((matching_bins(1:size(t % filters)) - 1) * t % stride) & + + 1 + filter_weight = product(filter_weights(:size(t % filters))) !$omp atomic t % results(score_index, filter_index) % value = & - t % results(score_index, filter_index) % value + score + t % results(score_index, filter_index) % value + score * filter_weight ! reset original delayed group bin matching_bins(t % find_filter(FILTER_DELAYEDGROUP)) = bin_original @@ -1799,13 +1924,14 @@ contains integer :: i integer :: i_tally + integer :: i_filt integer :: j ! loop index for scoring bins integer :: k ! loop index for nuclide bins integer :: filter_index ! single index for single bin integer :: i_nuclide ! index in nuclides array (from bins) real(8) :: flux ! tracklength estimate of flux real(8) :: atom_density ! atom density of single nuclide in atom/b-cm - logical :: found_bin ! scoring bin found? + real(8) :: filter_weight ! combined weight of all filters type(TallyObject), pointer :: t type(Material), pointer :: mat @@ -1820,70 +1946,103 @@ contains i_tally = active_tracklength_tallies % get_item(i) t => tallies(i_tally) - ! Check if this tally has a mesh filter -- if so, we treat it separately - ! since multiple bins can be scored to with a single track + ! Find the first bin in each filter. There may be more than one matching + ! bin per filter, but we'll deal with those later. + do i_filt = 1, size(t % filters) + call t % filters(i_filt) % obj % get_next_bin(p, t % estimator, & + NO_BIN_FOUND, matching_bins(i_filt), filter_weights(i_filt)) + ! If there are no valid bins for this filter, then there is nothing to + ! score and we can move on to the next tally. + if (matching_bins(i_filt) == NO_BIN_FOUND) cycle TALLY_LOOP + end do - if (t % find_filter(FILTER_MESH) > 0) then - call score_tl_on_mesh(p, i_tally, distance) - cycle - end if + ! ======================================================================== + ! Loop until we've covered all valid bins on each of the filters. - ! ======================================================================= - ! DETERMINE SCORING BIN COMBINATION + FILTER_LOOP: do - call get_scoring_bins(p, i_tally, found_bin) - if (.not. found_bin) cycle + ! Determine scoring index and weight for this filter combination + filter_index = sum((matching_bins(1:size(t % filters)) - 1) & + * t % stride) + 1 + filter_weight = product(filter_weights(:size(t % filters))) - ! ======================================================================= - ! CALCULATE RESULTS AND ACCUMULATE TALLY + ! ====================================================================== + ! Nuclide logic - ! If we have made it here, we have a scoring combination of bins for this - ! tally -- now we need to determine where in the results array we should - ! be accumulating the tally values - - ! Determine scoring index for this filter combination - filter_index = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 - - if (t % all_nuclides) then - if (p % material /= MATERIAL_VOID) then - call score_all_nuclides(p, i_tally, flux, filter_index) - end if - else - - NUCLIDE_BIN_LOOP: do k = 1, t % n_nuclide_bins - ! Get index of nuclide in nuclides array - i_nuclide = t % nuclide_bins(k) - - if (i_nuclide > 0) then - if (p % material /= MATERIAL_VOID) then - ! Get pointer to current material - mat => materials(p % material) - - ! Determine if nuclide is actually in material - NUCLIDE_MAT_LOOP: do j = 1, mat % n_nuclides - ! If index of nuclide matches the j-th nuclide listed in the - ! material, break out of the loop - if (i_nuclide == mat % nuclide(j)) exit - - ! If we've reached the last nuclide in the material, it means - ! the specified nuclide to be tallied is not in this material - if (j == mat % n_nuclides) then - cycle NUCLIDE_BIN_LOOP - end if - end do NUCLIDE_MAT_LOOP - - atom_density = mat % atom_density(j) - else - atom_density = ZERO - end if + if (t % all_nuclides) then + if (p % material /= MATERIAL_VOID) then + call score_all_nuclides(p, i_tally, flux, filter_index) end if + else - ! Determine score for each bin - call score_general(p, t, (k-1)*t % n_score_bins, filter_index, & - i_nuclide, atom_density, flux) + NUCLIDE_BIN_LOOP: do k = 1, t % n_nuclide_bins + ! Get index of nuclide in nuclides array + i_nuclide = t % nuclide_bins(k) - end do NUCLIDE_BIN_LOOP - end if + if (i_nuclide > 0) then + if (p % material /= MATERIAL_VOID) then + ! Get pointer to current material + mat => materials(p % material) + + ! Determine if nuclide is actually in material + NUCLIDE_MAT_LOOP: do j = 1, mat % n_nuclides + ! If index of nuclide matches the j-th nuclide listed in the + ! material, break out of the loop + if (i_nuclide == mat % nuclide(j)) exit + + ! If we've reached the last nuclide in the material, it means + ! the specified nuclide to be tallied is not in this material + if (j == mat % n_nuclides) then + cycle NUCLIDE_BIN_LOOP + end if + end do NUCLIDE_MAT_LOOP + + atom_density = mat % atom_density(j) + else + atom_density = ZERO + end if + end if + + ! Determine score for each bin + call score_general(p, t, (k-1)*t % n_score_bins, filter_index, & + i_nuclide, atom_density, flux * filter_weight) + + end do NUCLIDE_BIN_LOOP + + end if + + ! ====================================================================== + ! Filter logic + + ! If there are no filters, then we are done. + if (size(t % filters) == 0) exit FILTER_LOOP + + ! Increment the filter bins, starting with the last filter. If we get a + ! NO_BIN_FOUND for the last filter, it means we finished all valid bins + ! for that filter, but next-to-last filter might have more than one + ! valid bin so we need to increment that one as well, and so on. + do i_filt = size(t % filters), 1, -1 + call t % filters(i_filt) % obj % get_next_bin(p, t % estimator, & + matching_bins(i_filt), matching_bins(i_filt), & + filter_weights(i_filt)) + if (matching_bins(i_filt) /= NO_BIN_FOUND) exit + end do + + ! If we got all NO_BIN_FOUNDs, then we have finished all valid bins for + ! each of the filters. Exit the loop. + if (all(matching_bins(:size(t % filters)) == NO_BIN_FOUND)) & + exit FILTER_LOOP + + ! Reset all the filters with NO_BIN_FOUND. This will set them back to + ! their first valid bin. + do i_filt = 1, size(t % filters) + if (matching_bins(i_filt) == NO_BIN_FOUND) then + call t % filters(i_filt) % obj % get_next_bin(p, t % estimator, & + matching_bins(i_filt), matching_bins(i_filt), & + filter_weights(i_filt)) + end if + end do + end do FILTER_LOOP ! If the user has specified that we can assume all tallies are spatially ! separate, this implies that once a tally has been scored to, we needn't @@ -1899,289 +2058,6 @@ contains end subroutine score_tracklength_tally -!=============================================================================== -! SCORE_TL_ON_MESH calculate fluxes and reaction rates based on the track-length -! estimate of the flux specifically for tallies that have mesh filters. For -! these tallies, it is possible to score to multiple mesh cells for each track. -!=============================================================================== - - subroutine score_tl_on_mesh(p, i_tally, d_track) - - type(Particle), intent(in) :: p - integer, intent(in) :: i_tally - real(8), intent(in) :: d_track - - integer :: i ! loop index for filter/score bins - integer :: j ! loop index for direction - integer :: k ! loop index for mesh cell crossings - integer :: b ! loop index for nuclide bins - integer :: ijk0(3) ! indices of starting coordinates - integer :: ijk1(3) ! indices of ending coordinates - integer :: ijk_cross(3) ! indices of mesh cell crossed - integer :: n_cross ! number of surface crossings - integer :: filter_index ! single index for single bin - integer :: i_nuclide ! index in nuclides array - integer :: i_filter_mesh ! index of mesh filter in filters array - real(8) :: atom_density ! density of individual nuclide in atom/b-cm - real(8) :: flux ! tracklength estimate of flux - real(8) :: uvw(3) ! cosine of angle of particle - real(8) :: xyz0(3) ! starting/intermediate coordinates - real(8) :: xyz1(3) ! ending coordinates of particle - real(8) :: xyz_cross(3) ! coordinates of next boundary - real(8) :: d(3) ! distance to each bounding surface - real(8) :: distance ! distance traveled in mesh cell - logical :: found_bin ! was a scoring bin found? - logical :: start_in_mesh ! starting coordinates inside mesh? - logical :: end_in_mesh ! ending coordinates inside mesh? - real(8) :: theta - real(8) :: phi - type(TallyObject), pointer :: t - type(RegularMesh), pointer :: m - type(Material), pointer :: mat - - t => tallies(i_tally) - matching_bins(1:t%n_filters) = 1 - - ! ========================================================================== - ! CHECK IF THIS TRACK INTERSECTS THE MESH - - ! Copy starting and ending location of particle - xyz0 = p % coord(1) % xyz - (d_track - TINY_BIT) * p % coord(1) % uvw - xyz1 = p % coord(1) % xyz - TINY_BIT * p % coord(1) % uvw - - ! Get index for mesh filter - i_filter_mesh = t % find_filter(FILTER_MESH) - - ! Determine indices for starting and ending location - m => meshes(t % filters(i_filter_mesh) % int_bins(1)) - call get_mesh_indices(m, xyz0, ijk0(:m % n_dimension), start_in_mesh) - call get_mesh_indices(m, xyz1, ijk1(:m % n_dimension), end_in_mesh) - - ! Check if start or end is in mesh -- if not, check if track still - ! intersects with mesh - if ((.not. start_in_mesh) .and. (.not. end_in_mesh)) then - if (m % n_dimension == 2) then - if (.not. mesh_intersects_2d(m, xyz0, xyz1)) return - else - if (.not. mesh_intersects_3d(m, xyz0, xyz1)) return - end if - end if - - ! Reset starting and ending location - xyz0 = p % coord(1) % xyz - d_track * p % coord(1) % uvw - xyz1 = p % coord(1) % xyz - - ! ========================================================================= - ! CHECK FOR SCORING COMBINATION FOR FILTERS OTHER THAN MESH - - FILTER_LOOP: do i = 1, t % n_filters - - select case (t % filters(i) % type) - case (FILTER_UNIVERSE) - ! determine next universe bin - ! TODO: Account for multiple universes when performing this filter - matching_bins(i) = get_next_bin(FILTER_UNIVERSE, & - p % coord(p % n_coord) % universe, i_tally) - - case (FILTER_MATERIAL) - matching_bins(i) = get_next_bin(FILTER_MATERIAL, & - p % material, i_tally) - - case (FILTER_CELL) - ! determine next cell bin - do j = 1, p % n_coord - position(FILTER_CELL) = 0 - matching_bins(i) = get_next_bin(FILTER_CELL, & - p % coord(j) % cell, i_tally) - if (matching_bins(i) /= NO_BIN_FOUND) exit - end do - - case (FILTER_CELLBORN) - ! determine next cellborn bin - matching_bins(i) = get_next_bin(FILTER_CELLBORN, & - p % cell_born, i_tally) - - case (FILTER_SURFACE) - ! determine next surface bin - matching_bins(i) = get_next_bin(FILTER_SURFACE, & - p % surface, i_tally) - - case (FILTER_ENERGYIN) - ! determine incoming energy bin - k = t % filters(i) % n_bins - - ! check if energy of the particle is within energy bins - if (p % E < t % filters(i) % real_bins(1) .or. & - p % E > t % filters(i) % real_bins(k + 1)) then - matching_bins(i) = NO_BIN_FOUND - else - ! search to find incoming energy bin - matching_bins(i) = binary_search(t % filters(i) % real_bins, & - k + 1, p % E) - end if - - case (FILTER_POLAR) - ! Get theta value - theta = acos(p % coord(1) % uvw(3)) - - ! determine polar angle bin - k = t % filters(i) % n_bins - - ! check if particle is within polar angle bins - if (theta < t % filters(i) % real_bins(1) .or. & - theta > t % filters(i) % real_bins(k + 1)) then - matching_bins(i) = NO_BIN_FOUND - else - ! search to find polar angle bin - matching_bins(i) = binary_search(t % filters(i) % real_bins, & - k + 1, theta) - end if - - case (FILTER_AZIMUTHAL) - ! make sure the correct direction vector is used - phi = atan2(p % coord(1) % uvw(2), p % coord(1) % uvw(1)) - - ! determine mu bin - k = t % filters(i) % n_bins - - ! check if particle is within azimuthal angle bins - if (phi < t % filters(i) % real_bins(1) .or. & - phi > t % filters(i) % real_bins(k + 1)) then - matching_bins(i) = NO_BIN_FOUND - else - ! search to find azimuthal angle bin - matching_bins(i) = binary_search(t % filters(i) % real_bins, & - k + 1, phi) - end if - - end select - - ! Check if no matching bin was found - if (matching_bins(i) == NO_BIN_FOUND) return - - end do FILTER_LOOP - - ! ========================================================================== - ! DETERMINE WHICH MESH CELLS TO SCORE TO - - ! Calculate number of surface crossings - n_cross = sum(abs(ijk1(:m % n_dimension) - ijk0(:m % n_dimension))) + 1 - - ! Copy particle's direction - uvw = p % coord(1) % uvw - - ! Bounding coordinates - do j = 1, m % n_dimension - if (uvw(j) > 0) then - xyz_cross(j) = m % lower_left(j) + ijk0(j) * m % width(j) - else - xyz_cross(j) = m % lower_left(j) + (ijk0(j) - 1) * m % width(j) - end if - end do - - MESH_LOOP: do k = 1, n_cross - found_bin = .false. - - ! Calculate distance to each bounding surface. We need to treat special - ! case where the cosine of the angle is zero since this would result in a - ! divide-by-zero. - - if (k == n_cross) xyz_cross = xyz1 - - do j = 1, m % n_dimension - if (uvw(j) == 0) then - d(j) = INFINITY - else - d(j) = (xyz_cross(j) - xyz0(j))/uvw(j) - end if - end do - - ! Determine the closest bounding surface of the mesh cell by calculating - ! the minimum distance - - j = minloc(d(:m % n_dimension), 1) - distance = d(j) - - ! Now use the minimum distance and diretion of the particle to determine - ! which surface was crossed - - if (all(ijk0(:m % n_dimension) >= 1) .and. all(ijk0(:m % n_dimension) <= m % dimension)) then - ijk_cross = ijk0 - found_bin = .true. - end if - - ! Increment indices and determine new crossing point - if (uvw(j) > 0) then - ijk0(j) = ijk0(j) + 1 - xyz_cross(j) = xyz_cross(j) + m % width(j) - else - ijk0(j) = ijk0(j) - 1 - xyz_cross(j) = xyz_cross(j) - m % width(j) - end if - - ! ======================================================================= - ! SCORE TO THIS MESH CELL - - if (found_bin) then - ! Calculate track-length estimate of flux - flux = p % wgt * distance - - ! Determine mesh bin - matching_bins(i_filter_mesh) = mesh_indices_to_bin(m, ijk_cross) - - ! Determining scoring index - filter_index = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 - - if (t % all_nuclides) then - if (p % material /= MATERIAL_VOID) then - ! Score reaction rates for each nuclide in material - call score_all_nuclides(p, i_tally, flux, filter_index) - end if - else - NUCLIDE_BIN_LOOP: do b = 1, t % n_nuclide_bins - ! Get index of nuclide in nuclides array - i_nuclide = t % nuclide_bins(b) - - if (i_nuclide > 0) then - if (p % material /= MATERIAL_VOID) then - ! Get pointer to current material - mat => materials(p % material) - - ! Determine if nuclide is actually in material - NUCLIDE_MAT_LOOP: do j = 1, mat % n_nuclides - ! If index of nuclide matches the j-th nuclide listed in - ! the material, break out of the loop - if (i_nuclide == mat % nuclide(j)) exit - - ! If we've reached the last nuclide in the material, it - ! means the specified nuclide to be tallied is not in this - ! material - if (j == mat % n_nuclides) then - cycle NUCLIDE_BIN_LOOP - end if - end do NUCLIDE_MAT_LOOP - - atom_density = mat % atom_density(j) - else - atom_density = ZERO - end if - end if - - ! Determine score for each bin - call score_general(p, t, (b-1)*t % n_score_bins, filter_index, & - i_nuclide, atom_density, flux) - - end do NUCLIDE_BIN_LOOP - end if - end if - - ! Calculate new coordinates - xyz0 = xyz0 + distance * uvw - - end do MESH_LOOP - - end subroutine score_tl_on_mesh - !=============================================================================== ! SCORE_COLLISION_TALLY calculates fluxes and reaction rates based on the ! 1/Sigma_t estimate of the flux. This is triggered after every collision. It @@ -2196,6 +2072,7 @@ contains integer :: i integer :: i_tally + integer :: i_filt integer :: j ! loop index for scoring bins integer :: k ! loop index for nuclide bins integer :: filter_index ! single index for single bin @@ -2203,7 +2080,7 @@ contains real(8) :: flux ! collision estimate of flux real(8) :: atom_density ! atom density of single nuclide ! in atom/b-cm - logical :: found_bin ! scoring bin found? + real(8) :: filter_weight ! combined weight of all filters type(TallyObject), pointer :: t type(Material), pointer :: mat @@ -2223,62 +2100,103 @@ contains i_tally = active_collision_tallies % get_item(i) t => tallies(i_tally) - ! ======================================================================= - ! DETERMINE SCORING BIN COMBINATION + ! Find the first bin in each filter. There may be more than one matching + ! bin per filter, but we'll deal with those later. + do i_filt = 1, size(t % filters) + call t % filters(i_filt) % obj % get_next_bin(p, t % estimator, & + NO_BIN_FOUND, matching_bins(i_filt), filter_weights(i_filt)) + ! If there are no valid bins for this filter, then there is nothing to + ! score and we can move on to the next tally. + if (matching_bins(i_filt) == NO_BIN_FOUND) cycle TALLY_LOOP + end do - call get_scoring_bins(p, i_tally, found_bin) - if (.not. found_bin) cycle + ! ======================================================================== + ! Loop until we've covered all valid bins on each of the filters. - ! ======================================================================= - ! CALCULATE RESULTS AND ACCUMULATE TALLY + FILTER_LOOP: do - ! If we have made it here, we have a scoring combination of bins for this - ! tally -- now we need to determine where in the results array we should - ! be accumulating the tally values + ! Determine scoring index and weight for this filter combination + filter_index = sum((matching_bins(1:size(t % filters)) - 1) & + * t % stride) + 1 + filter_weight = product(filter_weights(:size(t % filters))) - ! Determine scoring index for this filter combination - filter_index = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 + ! ====================================================================== + ! Nuclide logic - if (t % all_nuclides) then - if (p % material /= MATERIAL_VOID) then - call score_all_nuclides(p, i_tally, flux, filter_index) - end if - else - - NUCLIDE_BIN_LOOP: do k = 1, t % n_nuclide_bins - ! Get index of nuclide in nuclides array - i_nuclide = t % nuclide_bins(k) - - if (i_nuclide > 0) then - if (p % material /= MATERIAL_VOID) then - ! Get pointer to current material - mat => materials(p % material) - - ! Determine if nuclide is actually in material - NUCLIDE_MAT_LOOP: do j = 1, mat % n_nuclides - ! If index of nuclide matches the j-th nuclide listed in the - ! material, break out of the loop - if (i_nuclide == mat % nuclide(j)) exit - - ! If we've reached the last nuclide in the material, it means - ! the specified nuclide to be tallied is not in this material - if (j == mat % n_nuclides) then - cycle NUCLIDE_BIN_LOOP - end if - end do NUCLIDE_MAT_LOOP - - atom_density = mat % atom_density(j) - else - atom_density = ZERO - end if + if (t % all_nuclides) then + if (p % material /= MATERIAL_VOID) then + call score_all_nuclides(p, i_tally, flux, filter_index) end if + else - ! Determine score for each bin - call score_general(p, t, (k-1)*t % n_score_bins, filter_index, & - i_nuclide, atom_density, flux) + NUCLIDE_BIN_LOOP: do k = 1, t % n_nuclide_bins + ! Get index of nuclide in nuclides array + i_nuclide = t % nuclide_bins(k) - end do NUCLIDE_BIN_LOOP - end if + if (i_nuclide > 0) then + if (p % material /= MATERIAL_VOID) then + ! Get pointer to current material + mat => materials(p % material) + + ! Determine if nuclide is actually in material + NUCLIDE_MAT_LOOP: do j = 1, mat % n_nuclides + ! If index of nuclide matches the j-th nuclide listed in the + ! material, break out of the loop + if (i_nuclide == mat % nuclide(j)) exit + + ! If we've reached the last nuclide in the material, it means + ! the specified nuclide to be tallied is not in this material + if (j == mat % n_nuclides) then + cycle NUCLIDE_BIN_LOOP + end if + end do NUCLIDE_MAT_LOOP + + atom_density = mat % atom_density(j) + else + atom_density = ZERO + end if + end if + + ! Determine score for each bin + call score_general(p, t, (k-1)*t % n_score_bins, filter_index, & + i_nuclide, atom_density, flux * filter_weight) + + end do NUCLIDE_BIN_LOOP + + end if + + ! ====================================================================== + ! Filter logic + + ! If there are no filters, then we are done. + if (size(t % filters) == 0) exit FILTER_LOOP + + ! Increment the filter bins, starting with the last filter. If we get a + ! NO_BIN_FOUND for the last filter, it means we finished all valid bins + ! for that filter, but next-to-last filter might have more than one + ! valid bin so we need to increment that one as well, and so on. + do i_filt = size(t % filters), 1, -1 + call t % filters(i_filt) % obj % get_next_bin(p, t % estimator, & + matching_bins(i_filt), matching_bins(i_filt), & + filter_weights(i_filt)) + if (matching_bins(i_filt) /= NO_BIN_FOUND) exit + end do + + ! If we got all NO_BIN_FOUNDs, then we have finished all valid bins for + ! each of the filters. Exit the loop. + if (all(matching_bins(:size(t % filters)) == NO_BIN_FOUND)) & + exit FILTER_LOOP + + ! Reset all the filters with NO_BIN_FOUND. This will set them back to + ! their first valid bin. + do i_filt = 1, size(t % filters) + if (matching_bins(i_filt) == NO_BIN_FOUND) then + call t % filters(i_filt) % obj % get_next_bin(p, t % estimator, & + matching_bins(i_filt), matching_bins(i_filt), & + filter_weights(i_filt)) + end if + end do + end do FILTER_LOOP ! If the user has specified that we can assume all tallies are spatially ! separate, this implies that once a tally has been scored to, we needn't @@ -2294,433 +2212,6 @@ contains end subroutine score_collision_tally -!=============================================================================== -! GET_SCORING_BINS determines a combination of filter bins that should be scored -! for a tally based on the particle's current attributes. -!=============================================================================== - - subroutine get_scoring_bins_ce(p, i_tally, found_bin) - - type(Particle), intent(in) :: p - integer, intent(in) :: i_tally - logical, intent(out) :: found_bin - - integer :: i ! loop index for filters - integer :: j - integer :: n ! number of bins for single filter - integer :: offset ! offset for distribcell - integer :: distribcell_index ! index in distribcell arrays - real(8) :: E ! particle energy - real(8) :: theta, phi ! Polar and Azimuthal Angles, respectively - type(TallyObject), pointer :: t - type(RegularMesh), pointer :: m - - found_bin = .true. - t => tallies(i_tally) - matching_bins(1:t%n_filters) = 1 - - FILTER_LOOP: do i = 1, t % n_filters - - select case (t % filters(i) % type) - case (FILTER_MESH) - ! determine mesh bin - m => meshes(t % filters(i) % int_bins(1)) - - ! Determine if we're in the mesh first - call get_mesh_bin(m, p % coord(1) % xyz, matching_bins(i)) - - case (FILTER_UNIVERSE) - ! determine next universe bin - ! TODO: Account for multiple universes when performing this filter - matching_bins(i) = get_next_bin(FILTER_UNIVERSE, & - p % coord(p % n_coord) % universe, i_tally) - - case (FILTER_MATERIAL) - if (p % material == MATERIAL_VOID) then - matching_bins(i) = NO_BIN_FOUND - else - matching_bins(i) = get_next_bin(FILTER_MATERIAL, & - p % material, i_tally) - endif - - case (FILTER_CELL) - ! determine next cell bin - do j = 1, p % n_coord - position(FILTER_CELL) = 0 - matching_bins(i) = get_next_bin(FILTER_CELL, & - p % coord(j) % cell, i_tally) - if (matching_bins(i) /= NO_BIN_FOUND) exit - end do - - case (FILTER_DISTRIBCELL) - ! determine next distribcell bin - distribcell_index = cells(t % filters(i) % int_bins(1)) & - % distribcell_index - matching_bins(i) = NO_BIN_FOUND - offset = 0 - do j = 1, p % n_coord - if (cells(p % coord(j) % cell) % type == CELL_FILL) then - offset = offset + cells(p % coord(j) % cell) % & - offset(distribcell_index) - elseif(cells(p % coord(j) % cell) % type == CELL_LATTICE) then - if (lattices(p % coord(j + 1) % lattice) % obj & - % are_valid_indices([& - p % coord(j + 1) % lattice_x, & - p % coord(j + 1) % lattice_y, & - p % coord(j + 1) % lattice_z])) then - offset = offset + lattices(p % coord(j + 1) % lattice) % obj % & - offset(distribcell_index, & - p % coord(j + 1) % lattice_x, & - p % coord(j + 1) % lattice_y, & - p % coord(j + 1) % lattice_z) - end if - end if - if (t % filters(i) % int_bins(1) == p % coord(j) % cell) then - matching_bins(i) = offset + 1 - exit - end if - end do - - case (FILTER_CELLBORN) - ! determine next cellborn bin - matching_bins(i) = get_next_bin(FILTER_CELLBORN, & - p % cell_born, i_tally) - - case (FILTER_SURFACE) - ! determine next surface bin - matching_bins(i) = get_next_bin(FILTER_SURFACE, & - p % surface, i_tally) - - case (FILTER_ENERGYIN) - ! determine incoming energy bin - n = t % filters(i) % n_bins - - ! make sure the correct energy is used - if (t % estimator == ESTIMATOR_TRACKLENGTH) then - E = p % E - else - E = p % last_E - end if - - ! check if energy of the particle is within energy bins - if (E < t % filters(i) % real_bins(1) .or. & - E > t % filters(i) % real_bins(n + 1)) then - matching_bins(i) = NO_BIN_FOUND - else - ! search to find incoming energy bin - matching_bins(i) = binary_search(t % filters(i) % real_bins, & - n + 1, E) - end if - - case (FILTER_ENERGYOUT) - ! determine outgoing energy bin - n = t % filters(i) % n_bins - - ! check if energy of the particle is within energy bins - if (p % E < t % filters(i) % real_bins(1) .or. & - p % E > t % filters(i) % real_bins(n + 1)) then - matching_bins(i) = NO_BIN_FOUND - else - ! search to find incoming energy bin - matching_bins(i) = binary_search(t % filters(i) % real_bins, & - n + 1, p % E) - end if - - case (FILTER_DELAYEDGROUP) - - if (survival_biasing .and. t % find_filter(FILTER_ENERGYOUT) <= 0) then - matching_bins(i) = 1 - elseif (active_tracklength_tallies % size() > 0) then - matching_bins(i) = 1 - else - if (p % delayed_group == 0) then - matching_bins = NO_BIN_FOUND - else - matching_bins(i) = p % delayed_group - end if - end if - - case (FILTER_MU) - ! determine mu bin - n = t % filters(i) % n_bins - - ! check if particle is within mu bins - if (p % mu < t % filters(i) % real_bins(1) .or. & - p % mu > t % filters(i) % real_bins(n + 1)) then - matching_bins(i) = NO_BIN_FOUND - else - ! search to find mu bin - matching_bins(i) = binary_search(t % filters(i) % real_bins, & - n + 1, p % mu) - end if - - case (FILTER_POLAR) - ! make sure the correct direction vector is used - if (t % estimator == ESTIMATOR_TRACKLENGTH) then - theta = acos(p % coord(1) % uvw(3)) - else - theta = acos(p % last_uvw(3)) - end if - - ! determine polar angle bin - n = t % filters(i) % n_bins - - ! check if particle is within polar angle bins - if (theta < t % filters(i) % real_bins(1) .or. & - theta > t % filters(i) % real_bins(n + 1)) then - matching_bins(i) = NO_BIN_FOUND - else - ! search to find polar angle bin - matching_bins(i) = binary_search(t % filters(i) % real_bins, & - n + 1, theta) - end if - - case (FILTER_AZIMUTHAL) - ! make sure the correct direction vector is used - if (t % estimator == ESTIMATOR_TRACKLENGTH) then - phi = atan2(p % coord(1) % uvw(2), p % coord(1) % uvw(1)) - else - phi = atan2(p % last_uvw(2), p % last_uvw(1)) - end if - ! determine mu bin - n = t % filters(i) % n_bins - - ! check if particle is within azimuthal angle bins - if (phi < t % filters(i) % real_bins(1) .or. & - phi > t % filters(i) % real_bins(n + 1)) then - matching_bins(i) = NO_BIN_FOUND - else - ! search to find azimuthal angle bin - matching_bins(i) = binary_search(t % filters(i) % real_bins, & - n + 1, phi) - end if - - end select - - ! If the current filter didn't match, exit this subroutine - if (matching_bins(i) == NO_BIN_FOUND) then - found_bin = .false. - return - end if - - end do FILTER_LOOP - - end subroutine get_scoring_bins_ce - - subroutine get_scoring_bins_mg(p, i_tally, found_bin) - - type(Particle), intent(in) :: p - integer, intent(in) :: i_tally - logical, intent(out) :: found_bin - - integer :: i ! loop index for filters - integer :: j - integer :: n ! number of bins for single filter - integer :: distribcell_index ! index in distribcell arrays - integer :: offset ! offset for distribcell - real(8) :: theta, phi ! Polar and Azimuthal Angles, respectively - real(8) :: E - type(TallyObject), pointer :: t - type(RegularMesh), pointer :: m - - found_bin = .true. - t => tallies(i_tally) - matching_bins(1:t%n_filters) = 1 - - FILTER_LOOP: do i = 1, t % n_filters - - select case (t % filters(i) % type) - case (FILTER_MESH) - ! determine mesh bin - m => meshes(t % filters(i) % int_bins(1)) - - ! Determine if we're in the mesh first - call get_mesh_bin(m, p % coord(1) % xyz, matching_bins(i)) - - case (FILTER_UNIVERSE) - ! determine next universe bin - ! TODO: Account for multiple universes when performing this filter - matching_bins(i) = get_next_bin(FILTER_UNIVERSE, & - p % coord(p % n_coord) % universe, i_tally) - - case (FILTER_MATERIAL) - if (p % material == MATERIAL_VOID) then - matching_bins(i) = NO_BIN_FOUND - else - matching_bins(i) = get_next_bin(FILTER_MATERIAL, & - p % material, i_tally) - endif - - case (FILTER_CELL) - ! determine next cell bin - do j = 1, p % n_coord - position(FILTER_CELL) = 0 - matching_bins(i) = get_next_bin(FILTER_CELL, & - p % coord(j) % cell, i_tally) - if (matching_bins(i) /= NO_BIN_FOUND) exit - end do - - case (FILTER_DISTRIBCELL) - ! determine next distribcell bin - distribcell_index = cells(t % filters(i) % int_bins(1)) & - % distribcell_index - matching_bins(i) = NO_BIN_FOUND - offset = 0 - do j = 1, p % n_coord - if (cells(p % coord(j) % cell) % type == CELL_FILL) then - offset = offset + cells(p % coord(j) % cell) % & - offset(distribcell_index) - elseif(cells(p % coord(j) % cell) % type == CELL_LATTICE) then - if (lattices(p % coord(j + 1) % lattice) % obj & - % are_valid_indices([& - p % coord(j + 1) % lattice_x, & - p % coord(j + 1) % lattice_y, & - p % coord(j + 1) % lattice_z])) then - offset = offset + lattices(p % coord(j + 1) % lattice) % obj % & - offset(distribcell_index, & - p % coord(j + 1) % lattice_x, & - p % coord(j + 1) % lattice_y, & - p % coord(j + 1) % lattice_z) - end if - end if - if (t % filters(i) % int_bins(1) == p % coord(j) % cell) then - matching_bins(i) = offset + 1 - exit - end if - end do - - case (FILTER_CELLBORN) - ! determine next cellborn bin - matching_bins(i) = get_next_bin(FILTER_CELLBORN, & - p % cell_born, i_tally) - - case (FILTER_SURFACE) - ! determine next surface bin - matching_bins(i) = get_next_bin(FILTER_SURFACE, & - p % surface, i_tally) - - case (FILTER_ENERGYIN) - if (t % energy_matches_groups) then - ! make sure the correct energy group is used - ! Since all groups are filters, the filter bin is the group - if (t % estimator == ESTIMATOR_TRACKLENGTH) then - matching_bins(i) = p % g - else - matching_bins(i) = p % last_g - end if - ! Tallies are ordered in increasing groups, group indices - ! however are the opposite, so switch - matching_bins(i) = energy_groups - matching_bins(i) + 1 - else - ! make sure the correct energy is used - if (t % estimator == ESTIMATOR_TRACKLENGTH) then - E = p % E - else - E = p % last_E - end if - n = t % filters(i) % n_bins - - ! check if energy of the particle is within energy bins - if (E < t % filters(i) % real_bins(1) .or. & - E > t % filters(i) % real_bins(n + 1)) then - matching_bins(i) = NO_BIN_FOUND - else - ! search to find incoming energy bin - matching_bins(i) = binary_search(t % filters(i) % real_bins, & - n + 1, E) - end if - end if - - case (FILTER_ENERGYOUT) - if (t % energyout_matches_groups) then - ! Since all groups are filters, the filter bin is the group - matching_bins(i) = p % g - - ! Tallies are ordered in increasing groups, group indices - ! however are the opposite, so switch - matching_bins(i) = energy_groups - matching_bins(i) + 1 - else - ! determine outgoing energy bin - n = t % filters(i) % n_bins - - ! check if energy of the particle is within energy bins - if (p % E < t % filters(i) % real_bins(1) .or. & - p % E > t % filters(i) % real_bins(n + 1)) then - matching_bins(i) = NO_BIN_FOUND - else - ! search to find incoming energy bin - matching_bins(i) = binary_search(t % filters(i) % real_bins, & - n + 1, p % E) - end if - end if - - case (FILTER_MU) - ! determine mu bin - n = t % filters(i) % n_bins - - ! check if particle is within mu bins - if (p % mu < t % filters(i) % real_bins(1) .or. & - p % mu > t % filters(i) % real_bins(n + 1)) then - matching_bins(i) = NO_BIN_FOUND - else - ! search to find mu bin - matching_bins(i) = binary_search(t % filters(i) % real_bins, & - n + 1, p % mu) - end if - - case (FILTER_POLAR) - ! make sure the correct direction vector is used - if (t % estimator == ESTIMATOR_TRACKLENGTH) then - theta = acos(p % coord(1) % uvw(3)) - else - theta = acos(p % last_uvw(3)) - end if - - ! determine polar angle bin - n = t % filters(i) % n_bins - - ! check if particle is within polar angle bins - if (theta < t % filters(i) % real_bins(1) .or. & - theta > t % filters(i) % real_bins(n + 1)) then - matching_bins(i) = NO_BIN_FOUND - else - ! search to find polar angle bin - matching_bins(i) = binary_search(t % filters(i) % real_bins, & - n + 1, theta) - end if - - case (FILTER_AZIMUTHAL) - ! make sure the correct direction vector is used - if (t % estimator == ESTIMATOR_TRACKLENGTH) then - phi = atan2(p % coord(1) % uvw(2), p % coord(1) % uvw(1)) - else - phi = atan2(p % last_uvw(2), p % last_uvw(1)) - end if - ! determine mu bin - n = t % filters(i) % n_bins - - ! check if particle is within azimuthal angle bins - if (phi < t % filters(i) % real_bins(1) .or. & - phi > t % filters(i) % real_bins(n + 1)) then - matching_bins(i) = NO_BIN_FOUND - else - ! search to find azimuthal angle bin - matching_bins(i) = binary_search(t % filters(i) % real_bins, & - n + 1, phi) - end if - - end select - - ! If the current filter didn't match, exit this subroutine - if (matching_bins(i) == NO_BIN_FOUND) then - found_bin = .false. - return - end if - - end do FILTER_LOOP - - end subroutine get_scoring_bins_mg - !=============================================================================== ! SCORE_SURFACE_CURRENT tallies surface crossings in a mesh tally by manually ! determining which mesh surfaces were crossed @@ -2737,7 +2228,6 @@ contains integer :: ijk0(3) ! indices of starting coordinates integer :: ijk1(3) ! indices of ending coordinates integer :: n_cross ! number of surface crossings - integer :: n ! number of incoming energy bins integer :: filter_index ! index of scoring bin integer :: i_filter_mesh ! index of mesh filter in filters array integer :: i_filter_surf ! index of surface filter in filters @@ -2747,6 +2237,7 @@ contains real(8) :: xyz_cross(3) ! coordinates of bounding surfaces real(8) :: d(3) ! distance to each bounding surface real(8) :: distance ! actual distance traveled + real(8) :: filt_score ! score applied by filters logical :: start_in_mesh ! particle's starting xyz in mesh? logical :: end_in_mesh ! particle's ending xyz in mesh? logical :: x_same ! same starting/ending x index (i) @@ -2757,7 +2248,7 @@ contains TALLY_LOOP: do i = 1, active_current_tallies % size() ! Copy starting and ending location of particle - xyz0 = p % last_xyz + xyz0 = p % last_xyz_current xyz1 = p % coord(1) % xyz ! Get pointer to tally @@ -2768,8 +2259,13 @@ contains i_filter_mesh = t % find_filter(FILTER_MESH) i_filter_surf = t % find_filter(FILTER_SURFACE) + ! Get pointer to mesh + select type(filt => t % filters(i_filter_mesh) % obj) + type is (MeshFilter) + m => meshes(filt % mesh) + end select + ! Determine indices for starting and ending location - m => meshes(t % filters(i_filter_mesh) % int_bins(1)) call get_mesh_indices(m, xyz0, ijk0(:m % n_dimension), start_in_mesh) call get_mesh_indices(m, xyz1, ijk1(:m % n_dimension), end_in_mesh) @@ -2792,19 +2288,13 @@ contains ! Copy particle's direction uvw = p % coord(1) % uvw - ! determine incoming energy bin + ! Determine incoming energy bin. We need to tell the energy filter this + ! is a tracklength tally so it uses the pre-collision energy. j = t % find_filter(FILTER_ENERGYIN) if (j > 0) then - n = t % filters(j) % n_bins - ! check if energy of the particle is within energy bins - if (p % E < t % filters(j) % real_bins(1) .or. & - p % E > t % filters(j) % real_bins(n + 1)) then - cycle - end if - - ! search to find incoming energy bin - matching_bins(j) = binary_search(t % filters(j) % real_bins, & - n + 1, p % E) + call t % filters(i) % obj % get_next_bin(p, ESTIMATOR_TRACKLENGTH, & + & NO_BIN_FOUND, matching_bins(j), filt_score) + if (matching_bins(j) == NO_BIN_FOUND) cycle end if ! ======================================================================= @@ -2823,7 +2313,8 @@ contains matching_bins(i_filter_surf) = OUT_TOP matching_bins(i_filter_mesh) = & mesh_indices_to_bin(m, ijk0) - filter_index = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 + filter_index = sum((matching_bins(1:size(t % filters)) - 1) & + * t % stride) + 1 !$omp atomic t % results(1, filter_index) % value = & t % results(1, filter_index) % value + p % wgt @@ -2836,7 +2327,8 @@ contains matching_bins(i_filter_surf) = OUT_BOTTOM matching_bins(i_filter_mesh) = & mesh_indices_to_bin(m, ijk0) - filter_index = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 + filter_index = sum((matching_bins(1:size(t % filters)) - 1) & + * t % stride) + 1 !$omp atomic t % results(1, filter_index) % value = & t % results(1, filter_index) % value + p % wgt @@ -2853,7 +2345,8 @@ contains matching_bins(i_filter_surf) = OUT_FRONT matching_bins(i_filter_mesh) = & mesh_indices_to_bin(m, ijk0) - filter_index = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 + filter_index = sum((matching_bins(1:size(t % filters)) - 1) & + * t % stride) + 1 !$omp atomic t % results(1, filter_index) % value = & t % results(1, filter_index) % value + p % wgt @@ -2866,7 +2359,8 @@ contains matching_bins(i_filter_surf) = OUT_BACK matching_bins(i_filter_mesh) = & mesh_indices_to_bin(m, ijk0) - filter_index = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 + filter_index = sum((matching_bins(1:size(t % filters)) - 1) & + * t % stride) + 1 !$omp atomic t % results(1, filter_index) % value = & t % results(1, filter_index) % value + p % wgt @@ -2883,7 +2377,8 @@ contains matching_bins(i_filter_surf) = OUT_RIGHT matching_bins(i_filter_mesh) = & mesh_indices_to_bin(m, ijk0) - filter_index = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 + filter_index = sum((matching_bins(1:size(t % filters)) - 1) & + * t % stride) + 1 !$omp atomic t % results(1, filter_index) % value = & t % results(1, filter_index) % value + p % wgt @@ -2896,7 +2391,8 @@ contains matching_bins(i_filter_surf) = OUT_LEFT matching_bins(i_filter_mesh) = & mesh_indices_to_bin(m, ijk0) - filter_index = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 + filter_index = sum((matching_bins(1:size(t % filters)) - 1) & + * t % stride) + 1 !$omp atomic t % results(1, filter_index) % value = & t % results(1, filter_index) % value + p % wgt @@ -3012,7 +2508,8 @@ contains ! Determine scoring index if (matching_bins(i_filter_surf) > 0) then - filter_index = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 + filter_index = sum((matching_bins(1:size(t % filters)) - 1) & + * t % stride) + 1 ! Check for errors if (filter_index <= 0 .or. filter_index > & @@ -3034,61 +2531,6 @@ contains end subroutine score_surface_current -!=============================================================================== -! GET_NEXT_BIN determines the next scoring bin for a particular filter variable -!=============================================================================== - - function get_next_bin(filter_type, filter_value, i_tally) result(bin) - - integer, intent(in) :: filter_type ! e.g. FILTER_MATERIAL - integer, intent(in) :: filter_value ! value of filter, e.g. material 3 - integer, intent(in) :: i_tally ! index of tally - integer :: bin ! index of filter - - integer :: i_tally_check - integer :: n - - ! If there are no scoring bins for this item, then return immediately - if (.not. allocated(tally_maps(filter_type) % items(filter_value) % elements)) then - bin = NO_BIN_FOUND - return - end if - - ! Check how many elements there are for this item - n = size(tally_maps(filter_type) % items(filter_value) % elements) - - do - ! Increment position in elements - position(filter_type) = position(filter_type) + 1 - - ! If we've reached the end of the array, there is no more bin to score to - if (position(filter_type) > n) then - position(filter_type) = 0 - bin = NO_BIN_FOUND - return - end if - - i_tally_check = tally_maps(filter_type) % items(filter_value) % & - elements(position(filter_type)) % index_tally - - if (i_tally_check > i_tally) then - ! Since the index being checked against is greater than the index we - ! need (and the tally indices were added to elements sequentially), we - ! know that no more bins will be scoring bins for this tally - position(filter_type) = 0 - bin = NO_BIN_FOUND - return - elseif (i_tally_check == i_tally) then - ! Found a match - bin = tally_maps(filter_type) % items(filter_value) % & - elements(position(filter_type)) % index_bin - return - end if - - end do - - end function get_next_bin - !=============================================================================== ! SYNCHRONIZE_TALLIES accumulates the sum of the contributions from each history ! within the batch to a new random variable diff --git a/src/tally_filter.F90 b/src/tally_filter.F90 new file mode 100644 index 000000000..c0e1c8853 --- /dev/null +++ b/src/tally_filter.F90 @@ -0,0 +1,1491 @@ +module tally_filter + + use constants, only: ONE, NO_BIN_FOUND, FP_PRECISION + use dict_header, only: DictIntInt + use geometry_header, only: BASE_UNIVERSE, RectLattice, HexLattice + use global + use hdf5_interface + use mesh_header, only: RegularMesh + use mesh, only: get_mesh_bin, bin_to_mesh_indices, & + get_mesh_indices, mesh_indices_to_bin, & + mesh_intersects_2d, mesh_intersects_3d + use particle_header, only: Particle + use search, only: binary_search + use string, only: to_str + use tally_filter_header, only: TallyFilter, TallyFilterContainer + + use hdf5, only: HID_T + + implicit none + +!=============================================================================== +! MESHFILTER indexes the location of particle events to a regular mesh. For +! tracklength tallies, it will produce multiple valid bins and the bin weight +! will correspond to the fraction of the track length that lies in that bin. +!=============================================================================== + type, extends(TallyFilter) :: MeshFilter + integer :: mesh + contains + procedure :: get_next_bin => get_next_bin_mesh + procedure :: to_statepoint => to_statepoint_mesh + procedure :: text_label => text_label_mesh + end type MeshFilter + +!=============================================================================== +! UNIVERSEFILTER specifies which geometric universes tally events reside in. +!=============================================================================== + type, extends(TallyFilter) :: UniverseFilter + integer, allocatable :: universes(:) + type(DictIntInt) :: map + contains + procedure :: get_next_bin => get_next_bin_universe + procedure :: to_statepoint => to_statepoint_universe + procedure :: text_label => text_label_universe + procedure :: initialize => initialize_universe + end type UniverseFilter + +!=============================================================================== +! MATERIAL specifies which material tally events reside in. +!=============================================================================== + type, extends(TallyFilter) :: MaterialFilter + integer, allocatable :: materials(:) + type(DictIntInt) :: map + contains + procedure :: get_next_bin => get_next_bin_material + procedure :: to_statepoint => to_statepoint_material + procedure :: text_label => text_label_material + procedure :: initialize => initialize_material + end type MaterialFilter + +!=============================================================================== +! CELLFILTER specifies which geometric cells tally events reside in. +!=============================================================================== + type, extends(TallyFilter) :: CellFilter + integer, allocatable :: cells(:) + type(DictIntInt) :: map + contains + procedure :: get_next_bin => get_next_bin_cell + procedure :: to_statepoint => to_statepoint_cell + procedure :: text_label => text_label_cell + procedure :: initialize => initialize_cell + end type CellFilter + +!=============================================================================== +! DISTRIBCELLFILTER specifies which distributed geometric cells tally events +! reside in. +!=============================================================================== + type, extends(TallyFilter) :: DistribcellFilter + integer :: cell + contains + procedure :: get_next_bin => get_next_bin_distribcell + procedure :: to_statepoint => to_statepoint_distribcell + procedure :: to_summary => to_summary_distribcell + procedure :: text_label => text_label_distribcell + procedure :: initialize => initialize_distribcell + end type DistribcellFilter + +!=============================================================================== +! CELLBORNFILTER specifies which cell the particle was born in. +!=============================================================================== + type, extends(TallyFilter) :: CellbornFilter + integer, allocatable :: cells(:) + type(DictIntInt) :: map + contains + procedure :: get_next_bin => get_next_bin_cellborn + procedure :: to_statepoint => to_statepoint_cellborn + procedure :: text_label => text_label_cellborn + procedure :: initialize => initialize_cellborn + end type CellbornFilter + +!=============================================================================== +! SURFACEFILTER is currently not implemented for usual geometric surfaces, but +! it is used as a placeholder for mesh surfaces used in current tallies. +!=============================================================================== + type, extends(TallyFilter) :: SurfaceFilter + integer, allocatable :: surfaces(:) + contains + procedure :: get_next_bin => get_next_bin_surface + procedure :: to_statepoint => to_statepoint_surface + procedure :: text_label => text_label_surface + procedure :: initialize => initialize_surface + end type SurfaceFilter + +!=============================================================================== +! ENERGYFILTER bins the incident neutron energy. +!=============================================================================== + type, extends(TallyFilter) :: EnergyFilter + real(8), allocatable :: bins(:) + + ! True if transport group number can be used directly to get bin number + logical :: matches_transport_groups = .false. + + contains + procedure :: get_next_bin => get_next_bin_energy + procedure :: to_statepoint => to_statepoint_energy + procedure :: text_label => text_label_energy + end type EnergyFilter + +!=============================================================================== +! ENERGYOUTFILTER bins the outgoing neutron energy. Only scattering events use +! the get_next_bin functionality. Nu-fission tallies manually iterate over the +! filter bins. +!=============================================================================== + type, extends(TallyFilter) :: EnergyoutFilter + real(8), allocatable :: bins(:) + + ! True if transport group number can be used directly to get bin number + logical :: matches_transport_groups = .false. + + contains + procedure :: get_next_bin => get_next_bin_energyout + procedure :: to_statepoint => to_statepoint_energyout + procedure :: text_label => text_label_energyout + end type EnergyoutFilter + +!=============================================================================== +! DELAYEDGROUPFILTER bins outgoing fission neutrons in their delayed groups. +! The get_next_bin functionality is not actually used. The bins are manually +! iterated over in the scoring subroutines. +!=============================================================================== + type, extends(TallyFilter) :: DelayedGroupFilter + integer, allocatable :: groups(:) + contains + procedure :: get_next_bin => get_next_bin_dg + procedure :: to_statepoint => to_statepoint_dg + procedure :: text_label => text_label_dg + end type DelayedGroupFilter + +!=============================================================================== +! MUFILTER bins the incoming-outgoing direction cosine. This is only used for +! scatter reactions. +!=============================================================================== + type, extends(TallyFilter) :: MuFilter + real(8), allocatable :: bins(:) + contains + procedure :: get_next_bin => get_next_bin_mu + procedure :: to_statepoint => to_statepoint_mu + procedure :: text_label => text_label_mu + end type MuFilter + +!=============================================================================== +! POLARFILTER bins the incident neutron polar angle (relative to the global +! z-axis). +!=============================================================================== + type, extends(TallyFilter) :: PolarFilter + real(8), allocatable :: bins(:) + contains + procedure :: get_next_bin => get_next_bin_polar + procedure :: to_statepoint => to_statepoint_polar + procedure :: text_label => text_label_polar + end type PolarFilter + +!=============================================================================== +! AZIMUTHALFILTER bins the incident neutron azimuthal angle (relative to the +! global xy-plane). +!=============================================================================== + type, extends(TallyFilter) :: AzimuthalFilter + real(8), allocatable :: bins(:) + contains + procedure :: get_next_bin => get_next_bin_azimuthal + procedure :: to_statepoint => to_statepoint_azimuthal + procedure :: text_label => text_label_azimuthal + end type AzimuthalFilter + +contains + +!=============================================================================== +! METHODS: for a description of these methods, see their counterparts bound to +! the abstract TallyFilter class. +!=============================================================================== + +!=============================================================================== +! MeshFilter methods +!=============================================================================== + subroutine get_next_bin_mesh(this, p, estimator, current_bin, next_bin, & + weight) + class(MeshFilter), intent(in) :: this + type(Particle), intent(in) :: p + integer, intent(in) :: estimator + integer, value, intent(in) :: current_bin + integer, intent(out) :: next_bin + real(8), intent(out) :: weight + + integer, parameter :: MAX_SEARCH_ITER = 100 ! Maximum number of times we can + ! can loop while trying to find + ! the first intersection. + + integer :: j ! loop index for direction + integer :: ijk0(3) ! indices of starting coordinates + integer :: ijk1(3) ! indices of ending coordinates + integer :: search_iter ! loop count for intersection search + real(8) :: uvw(3) ! cosine of angle of particle + real(8) :: xyz0(3) ! starting/intermediate coordinates + real(8) :: xyz1(3) ! ending coordinates of particle + real(8) :: xyz_cross ! coordinates of next boundary + real(8) :: d(3) ! distance to each bounding surface + real(8) :: total_distance ! distance of entire particle track + real(8) :: distance ! distance traveled in mesh cell + logical :: start_in_mesh ! starting coordinates inside mesh? + logical :: end_in_mesh ! ending coordinates inside mesh? + type(RegularMesh), pointer :: m + + ! Get a pointer to the mesh. + m => meshes(this % mesh) + + if (estimator /= ESTIMATOR_TRACKLENGTH) then + ! If this is an analog or collision tally, then there can only be one + ! valid mesh bin. + if (current_bin == NO_BIN_FOUND) then + call get_mesh_bin(m, p % coord(1) % xyz, next_bin) + else + next_bin = NO_BIN_FOUND + end if + weight = ONE + + else + ! A track can span multiple mesh bins so we need to handle a lot of + ! intersection logic for tracklength tallies. + + ! Copy the starting and ending coordinates of the particle. Offset these + ! just a bit for the purposes of determining if there was an intersection + ! in case the mesh surfaces coincide with lattice/geometric surfaces which + ! might produce finite-precision errors. + xyz0 = p % last_xyz + TINY_BIT * p % coord(1) % uvw + xyz1 = p % coord(1) % xyz - TINY_BIT * p % coord(1) % uvw + + ! Determine indices for starting and ending location. + call get_mesh_indices(m, xyz0, ijk0(:m % n_dimension), start_in_mesh) + call get_mesh_indices(m, xyz1, ijk1(:m % n_dimension), end_in_mesh) + + ! If this is the first iteration of the filter loop, check if the track + ! intersects any part of the mesh. + if (current_bin == NO_BIN_FOUND) then + if ((.not. start_in_mesh) .and. (.not. end_in_mesh)) then + if (m % n_dimension == 2) then + if (.not. mesh_intersects_2d(m, xyz0, xyz1)) then + next_bin = NO_BIN_FOUND + return + end if + else + if (.not. mesh_intersects_3d(m, xyz0, xyz1)) then + next_bin = NO_BIN_FOUND + return + end if + end if + end if + end if + + ! Copy the un-modified coordinates the particle direction. + xyz0 = p % last_xyz + xyz1 = p % coord(1) % xyz + uvw = p % coord(1) % uvw + + ! Compute the length of the entire track. + total_distance = sqrt(sum((xyz1 - xyz0)**2)) + + ! If we're looking for the first valid bin, check to see if the particle + ! starts inside the mesh. + if (current_bin == NO_BIN_FOUND) then + if (any(ijk0(:m % n_dimension) < 1) & + .or. any(ijk0(:m % n_dimension) > m % dimension)) then + + ! The particle does not start in the mesh so keep iterating the ijk0 + ! indices to cross the nearest mesh surface until we've found a valid + ! bin. MAX_SEARCH_ITER prevents an infinite loop. + search_iter = 0 + do while (any(ijk0(:m % n_dimension) < 1) & + .or. any(ijk0(:m % n_dimension) > m % dimension)) + if (search_iter == MAX_SEARCH_ITER) call fatal_error("Failed to & + &find a mesh intersection on a tally mesh filter.") + + do j = 1, m % n_dimension + if (abs(uvw(j)) < FP_PRECISION) then + d(j) = INFINITY + else if (uvw(j) > 0) then + xyz_cross = m % lower_left(j) + ijk0(j) * m % width(j) + d(j) = (xyz_cross - xyz0(j)) / uvw(j) + else + xyz_cross = m % lower_left(j) + (ijk0(j) - 1) * m % width(j) + d(j) = (xyz_cross - xyz0(j)) / uvw(j) + end if + end do + j = minloc(d(:m % n_dimension), 1) + if (uvw(j) > ZERO) then + ijk0(j) = ijk0(j) + 1 + else + ijk0(j) = ijk0(j) - 1 + end if + end do + distance = d(j) + xyz0 = xyz0 + distance * uvw + + end if + end if + + ! ======================================================================== + ! If we've already scored some mesh bins, figure out which mesh cell is + ! next and where the particle enters that cell. + + if (current_bin /= NO_BIN_FOUND) then + ! Get the indices to the last bin. + call bin_to_mesh_indices(m, current_bin, ijk0(:m % n_dimension)) + + ! If the particle track ends in that bin, then we are done. + if (all(ijk0(:m % n_dimension) == ijk1(:m % n_dimension))) then + next_bin = NO_BIN_FOUND + return + end if + + ! Figure out which face of the previous mesh cell our track exits, i.e. + ! the closest surface of that cell for which + ! dot(p % uvw, face_normal) > 0. + do j = 1, m % n_dimension + if (abs(uvw(j)) < FP_PRECISION) then + d(j) = INFINITY + else if (uvw(j) > 0) then + xyz_cross = m % lower_left(j) + ijk0(j) * m % width(j) + d(j) = (xyz_cross - xyz0(j)) / uvw(j) + else + xyz_cross = m % lower_left(j) + (ijk0(j) - 1) * m % width(j) + d(j) = (xyz_cross - xyz0(j)) / uvw(j) + end if + end do + j = minloc(d(:m % n_dimension), 1) + + ! Translate the starting coordintes by the distance to that face. This + ! should be the xyz that we computed the distance to in the last + ! iteration of the filter loop. + distance = d(j) + xyz0 = xyz0 + distance * uvw + + ! Increment the indices into the next mesh cell. + if (uvw(j) > ZERO) then + ijk0(j) = ijk0(j) + 1 + else + ijk0(j) = ijk0(j) - 1 + end if + + ! If the next indices are invalid, then the track has left the mesh and + ! we are done. + if (any(ijk0(:m % n_dimension) < 1) & + .or. any(ijk0(:m % n_dimension) > m % dimension)) then + next_bin = NO_BIN_FOUND + return + end if + end if + + ! Compute the length of the track segment in this mesh cell. + if (all(ijk0(:m % n_dimension) == ijk1(:m % n_dimension))) then + ! The track ends in this cell. Use the particle end location rather + ! than the mesh surface. + distance = sqrt(sum((xyz1 - xyz0)**2)) + else + ! The track exits this cell. Use the distance to the mesh surface. + do j = 1, m % n_dimension + if (abs(uvw(j)) < FP_PRECISION) then + d(j) = INFINITY + else if (uvw(j) > 0) then + xyz_cross = m % lower_left(j) + ijk0(j) * m % width(j) + d(j) = (xyz_cross - xyz0(j)) / uvw(j) + else + xyz_cross = m % lower_left(j) + (ijk0(j) - 1) * m % width(j) + d(j) = (xyz_cross - xyz0(j)) / uvw(j) + end if + end do + distance = minval(d(:m % n_dimension)) + end if + + ! Assign the next tally bin and the score + next_bin = mesh_indices_to_bin(m, ijk0(:m % n_dimension)) + weight = distance / total_distance + endif + end subroutine get_next_bin_mesh + + subroutine to_statepoint_mesh(this, filter_group) + class(MeshFilter), intent(in) :: this + integer(HID_T), intent(in) :: filter_group + + call write_dataset(filter_group, "type", "mesh") + call write_dataset(filter_group, "n_bins", this % n_bins) + call write_dataset(filter_group, "bins", this % mesh ) + end subroutine to_statepoint_mesh + + function text_label_mesh(this, bin) result(label) + class(MeshFilter), intent(in) :: this + integer, intent(in) :: bin + character(MAX_LINE_LEN) :: label + + integer, allocatable :: ijk(:) + type(RegularMesh), pointer :: m + + m => meshes(this % mesh) + allocate(ijk(m % n_dimension)) + call bin_to_mesh_indices(m, bin, ijk) + if (m % n_dimension == 2) then + label = "Mesh Index (" // trim(to_str(ijk(1))) // ", " // & + trim(to_str(ijk(2))) // ")" + elseif (m % n_dimension == 3) then + label = "Mesh Index (" // trim(to_str(ijk(1))) // ", " // & + trim(to_str(ijk(2))) // ", " // trim(to_str(ijk(3))) // ")" + end if + end function text_label_mesh + +!=============================================================================== +! UniverseFilter methods +!=============================================================================== + subroutine get_next_bin_universe(this, p, estimator, current_bin, next_bin, & + weight) + class(UniverseFilter), intent(in) :: this + type(Particle), intent(in) :: p + integer, intent(in) :: estimator + integer, value, intent(in) :: current_bin + integer, intent(out) :: next_bin + real(8), intent(out) :: weight + + integer :: i, start + + ! Find the coordinate level of the last bin we found. + if (current_bin == NO_BIN_FOUND) then + start = 1 + else + do i = 1, p % n_coord + if (p % coord(i) % universe == this % universes(current_bin)) then + start = i + 1 + exit + end if + end do + end if + + ! Starting one coordinate level deeper, find the next bin. + next_bin = NO_BIN_FOUND + do i = start, p % n_coord + if (this % map % has_key(p % coord(i) % universe)) then + next_bin = this % map % get_key(p % coord(i) % universe) + exit + end if + end do + weight = ONE + end subroutine get_next_bin_universe + + subroutine to_statepoint_universe(this, filter_group) + class(UniverseFilter), intent(in) :: this + integer(HID_T), intent(in) :: filter_group + + call write_dataset(filter_group, "type", "universe") + call write_dataset(filter_group, "n_bins", this % n_bins) + call write_dataset(filter_group, "bins", this % universes ) + end subroutine to_statepoint_universe + + subroutine initialize_universe(this) + class(UniverseFilter), intent(inout) :: this + + integer :: i, id + + ! Convert ids to indices. + do i = 1, this % n_bins + id = this % universes(i) + if (universe_dict % has_key(id)) then + this % universes(i) = universe_dict % get_key(id) + else + call fatal_error("Could not find universe " // trim(to_str(id)) & + &// " specified on a tally filter.") + end if + end do + + ! Generate mapping from universe indices to filter bins. + do i = 1, this % n_bins + call this % map % add_key(this % universes(i), i) + end do + end subroutine initialize_universe + + function text_label_universe(this, bin) result(label) + class(UniverseFilter), intent(in) :: this + integer, intent(in) :: bin + character(MAX_LINE_LEN) :: label + + label = "Universe " // to_str(universes(this % universes(bin)) % id) + end function text_label_universe + +!=============================================================================== +! MaterialFilter methods +!=============================================================================== + subroutine get_next_bin_material(this, p, estimator, current_bin, next_bin, & + weight) + class(MaterialFilter), intent(in) :: this + type(Particle), intent(in) :: p + integer, intent(in) :: estimator + integer, value, intent(in) :: current_bin + integer, intent(out) :: next_bin + real(8), intent(out) :: weight + + next_bin = NO_BIN_FOUND + if (current_bin == NO_BIN_FOUND) then + if (this % map % has_key(p % material)) then + next_bin = this % map % get_key(p % material) + end if + end if + weight = ONE + end subroutine get_next_bin_material + + subroutine to_statepoint_material(this, filter_group) + class(MaterialFilter), intent(in) :: this + integer(HID_T), intent(in) :: filter_group + + call write_dataset(filter_group, "type", "material") + call write_dataset(filter_group, "n_bins", this % n_bins) + call write_dataset(filter_group, "bins", this % materials ) + end subroutine to_statepoint_material + + subroutine initialize_material(this) + class(MaterialFilter), intent(inout) :: this + + integer :: i, id + + ! Convert ids to indices. + do i = 1, this % n_bins + id = this % materials(i) + if (material_dict % has_key(id)) then + this % materials(i) = material_dict % get_key(id) + else + call fatal_error("Could not find material " // trim(to_str(id)) & + &// " specified on a tally filter.") + end if + end do + + ! Generate mapping from material indices to filter bins. + do i = 1, this % n_bins + call this % map % add_key(this % materials(i), i) + end do + end subroutine initialize_material + + function text_label_material(this, bin) result(label) + class(MaterialFilter), intent(in) :: this + integer, intent(in) :: bin + character(MAX_LINE_LEN) :: label + + label = "Material " // to_str(materials(this % materials(bin)) % id) + end function text_label_material + +!=============================================================================== +! CellFilter methods +!=============================================================================== + subroutine get_next_bin_cell(this, p, estimator, current_bin, next_bin, & + weight) + class(CellFilter), intent(in) :: this + type(Particle), intent(in) :: p + integer, intent(in) :: estimator + integer, value, intent(in) :: current_bin + integer, intent(out) :: next_bin + real(8), intent(out) :: weight + + integer :: i, start + + ! Find the coordinate level of the last bin we found. + if (current_bin == NO_BIN_FOUND) then + start = 1 + else + do i = 1, p % n_coord + if (p % coord(i) % cell == this % cells(current_bin)) then + start = i + 1 + exit + end if + end do + end if + + ! Starting one coordinate level deeper, find the next bin. + next_bin = NO_BIN_FOUND + do i = start, p % n_coord + if (this % map % has_key(p % coord(i) % cell)) then + next_bin = this % map % get_key(p % coord(i) % cell) + exit + end if + end do + weight = ONE + end subroutine get_next_bin_cell + + subroutine to_statepoint_cell(this, filter_group) + class(CellFilter), intent(in) :: this + integer(HID_T), intent(in) :: filter_group + + call write_dataset(filter_group, "type", "cell") + call write_dataset(filter_group, "n_bins", this % n_bins) + call write_dataset(filter_group, "bins", this % cells ) + end subroutine to_statepoint_cell + + subroutine initialize_cell(this) + class(CellFilter), intent(inout) :: this + + integer :: i, id + + ! Convert ids to indices. + do i = 1, this % n_bins + id = this % cells(i) + if (cell_dict % has_key(id)) then + this % cells(i) = cell_dict % get_key(id) + else + call fatal_error("Could not find cell " // trim(to_str(id)) & + &// " specified on tally filter.") + end if + end do + + ! Generate mapping from cell indices to filter bins. + do i = 1, this % n_bins + call this % map % add_key(this % cells(i), i) + end do + end subroutine initialize_cell + + function text_label_cell(this, bin) result(label) + class(CellFilter), intent(in) :: this + integer, intent(in) :: bin + character(MAX_LINE_LEN) :: label + + label = "Cell " // to_str(cells(this % cells(bin)) % id) + end function text_label_cell + +!=============================================================================== +! DistribcellFilter methods +!=============================================================================== + subroutine get_next_bin_distribcell(this, p, estimator, current_bin, & + next_bin, weight) + class(DistribcellFilter), intent(in) :: this + type(Particle), intent(in) :: p + integer, intent(in) :: estimator + integer, value, intent(in) :: current_bin + integer, intent(out) :: next_bin + real(8), intent(out) :: weight + + logical :: cell_found + integer :: distribcell_index, offset, i + + cell_found = .false. + if (current_bin == NO_BIN_FOUND) then + distribcell_index = cells(this % cell) % distribcell_index + offset = 0 + do i = 1, p % n_coord + if (cells(p % coord(i) % cell) % type == CELL_FILL) then + offset = offset + cells(p % coord(i) % cell) % & + offset(distribcell_index) + elseif (cells(p % coord(i) % cell) % type == CELL_LATTICE) then + if (lattices(p % coord(i + 1) % lattice) % obj & + % are_valid_indices([& + p % coord(i + 1) % lattice_x, & + p % coord(i + 1) % lattice_y, & + p % coord(i + 1) % lattice_z])) then + offset = offset + lattices(p % coord(i + 1) % lattice) % obj % & + offset(distribcell_index, & + p % coord(i + 1) % lattice_x, & + p % coord(i + 1) % lattice_y, & + p % coord(i + 1) % lattice_z) + end if + end if + if (this % cell == p % coord(i) % cell) then + next_bin = offset + 1 + cell_found = .true. + exit + end if + end do + else + next_bin = NO_BIN_FOUND + end if + if (.not. cell_found) next_bin = NO_BIN_FOUND + weight = ONE + end subroutine get_next_bin_distribcell + + subroutine to_statepoint_distribcell(this, filter_group) + class(DistribcellFilter), intent(in) :: this + integer(HID_T), intent(in) :: filter_group + + call write_dataset(filter_group, "type", "distribcell") + call write_dataset(filter_group, "n_bins", this % n_bins) + call write_dataset(filter_group, "bins", this % cell ) + end subroutine to_statepoint_distribcell + + subroutine to_summary_distribcell(this, filter_group) + class(DistribcellFilter), intent(in) :: this + integer(HID_T), intent(in) :: filter_group + + integer :: offset, k + character(MAX_LINE_LEN), allocatable :: paths(:) + character(MAX_LINE_LEN) :: path + + call write_dataset(filter_group, "type", "distribcell") + call write_dataset(filter_group, "n_bins", this % n_bins) + call write_dataset(filter_group, "bins", this % cell ) + + ! Write paths to reach each distribcell instance + + ! Allocate array of strings for each distribcell path + allocate(paths(this % n_bins)) + + ! Store path for each distribcell instance + do k = 1, this % n_bins + path = '' + offset = 1 + call find_offset(this % cell, universes(BASE_UNIVERSE), k, offset, path) + paths(k) = path + end do + + ! Write array of distribcell paths to summary file + call write_dataset(filter_group, "paths", paths) + deallocate(paths) + end subroutine to_summary_distribcell + + subroutine initialize_distribcell(this) + class(DistribcellFilter), intent(inout) :: this + + integer :: id + + ! Convert id to index. + id = this % cell + if (cell_dict % has_key(id)) then + this % cell = cell_dict % get_key(id) + else + call fatal_error("Could not find cell " // trim(to_str(id)) & + &// " specified on tally filter.") + end if + end subroutine initialize_distribcell + + function text_label_distribcell(this, bin) result(label) + class(DistribcellFilter), intent(in) :: this + integer, intent(in) :: bin + character(MAX_LINE_LEN) :: label + + integer :: offset + type(Universe), pointer :: univ + + univ => universes(BASE_UNIVERSE) + offset = 0 + label = '' + call find_offset(this % cell, univ, bin-1, offset, label) + label = "Distributed Cell " // label + end function text_label_distribcell + +!=============================================================================== +! CellbornFilter methods +!=============================================================================== + subroutine get_next_bin_cellborn(this, p, estimator, current_bin, next_bin, & + weight) + class(CellbornFilter), intent(in) :: this + type(Particle), intent(in) :: p + integer, intent(in) :: estimator + integer, value, intent(in) :: current_bin + integer, intent(out) :: next_bin + real(8), intent(out) :: weight + + next_bin = NO_BIN_FOUND + if (current_bin == NO_BIN_FOUND) then + if (this % map % has_key(p % cell_born)) then + next_bin = this % map % get_key(p % cell_born) + end if + end if + weight = ONE + end subroutine get_next_bin_cellborn + + subroutine to_statepoint_cellborn(this, filter_group) + class(CellbornFilter), intent(in) :: this + integer(HID_T), intent(in) :: filter_group + + call write_dataset(filter_group, "type", "cellborn") + call write_dataset(filter_group, "n_bins", this % n_bins) + call write_dataset(filter_group, "bins", this % cells ) + end subroutine to_statepoint_cellborn + + subroutine initialize_cellborn(this) + class(CellbornFilter), intent(inout) :: this + + integer :: i, id + + ! Convert ids to indices. + do i = 1, this % n_bins + id = this % cells(i) + if (cell_dict % has_key(id)) then + this % cells(i) = cell_dict % get_key(id) + else + call fatal_error("Could not find cell " // trim(to_str(id)) & + &// " specified on tally filter.") + end if + end do + + ! Generate mapping from cell indices to filter bins. + do i = 1, this % n_bins + call this % map % add_key(this % cells(i), i) + end do + end subroutine initialize_cellborn + + function text_label_cellborn(this, bin) result(label) + class(CellbornFilter), intent(in) :: this + integer, intent(in) :: bin + character(MAX_LINE_LEN) :: label + + label = "Birth Cell " // to_str(cells(this % cells(bin)) % id) + end function text_label_cellborn + +!=============================================================================== +! SurfaceFilter methods +!=============================================================================== + subroutine get_next_bin_surface(this, p, estimator, current_bin, next_bin, & + weight) + class(SurfaceFilter), intent(in) :: this + type(Particle), intent(in) :: p + integer, intent(in) :: estimator + integer, value, intent(in) :: current_bin + integer, intent(out) :: next_bin + real(8), intent(out) :: weight + + integer :: i + + next_bin = NO_BIN_FOUND + if (current_bin == NO_BIN_FOUND) then + do i = 1, this % n_bins + if (p % surface == this % surfaces(i)) then + next_bin = i + exit + end if + end do + end if + weight = ONE + end subroutine get_next_bin_surface + + subroutine to_statepoint_surface(this, filter_group) + class(SurfaceFilter), intent(in) :: this + integer(HID_T), intent(in) :: filter_group + + call write_dataset(filter_group, "type", "surface") + call write_dataset(filter_group, "n_bins", this % n_bins) + call write_dataset(filter_group, "bins", this % surfaces ) + end subroutine to_statepoint_surface + + subroutine initialize_surface(this) + class(SurfaceFilter), intent(inout) :: this + + integer :: i, id + + ! Convert ids to indices. + do i = 1, this % n_bins + id = this % surfaces(i) + if (surface_dict % has_key(id)) then + this % surfaces(i) = surface_dict % get_key(id) + else + call fatal_error("Could not find surface " // trim(to_str(id)) & + &// " specified on tally filter.") + end if + end do + end subroutine initialize_surface + + function text_label_surface(this, bin) result(label) + class(SurfaceFilter), intent(in) :: this + integer, intent(in) :: bin + character(MAX_LINE_LEN) :: label + + label = "Surface " // to_str(surfaces(this % surfaces(bin)) % obj % id) + end function text_label_surface + +!=============================================================================== +! EnergyFilter methods +!=============================================================================== + subroutine get_next_bin_energy(this, p, estimator, current_bin, next_bin, & + weight) + class(EnergyFilter), intent(in) :: this + type(Particle), intent(in) :: p + integer, intent(in) :: estimator + integer, value, intent(in) :: current_bin + integer, intent(out) :: next_bin + real(8), intent(out) :: weight + + integer :: n + real(8) :: E + + if (current_bin == NO_BIN_FOUND) then + n = this % n_bins + + if ((.not. run_CE) .and. this % matches_transport_groups) then + if (estimator == ESTIMATOR_TRACKLENGTH) then + next_bin = p % g + else + next_bin = p % last_g + end if + + ! Tallies are ordered in increasing groups, group indices + ! however are the opposite, so switch + next_bin = energy_groups - next_bin + 1 + + else + ! Make sure the correct energy is used. + if (estimator == ESTIMATOR_TRACKLENGTH) then + E = p % E + else + E = p % last_E + end if + + ! Check if energy of the particle is within energy bins. + if (E < this % bins(1) .or. E > this % bins(n + 1)) then + next_bin = NO_BIN_FOUND + else + ! Search to find incoming energy bin. + next_bin = binary_search(this % bins, n + 1, E) + end if + end if + + else + next_bin = NO_BIN_FOUND + end if + weight = ONE + end subroutine get_next_bin_energy + + subroutine to_statepoint_energy(this, filter_group) + class(EnergyFilter), intent(in) :: this + integer(HID_T), intent(in) :: filter_group + + call write_dataset(filter_group, "type", "energy") + call write_dataset(filter_group, "n_bins", this % n_bins) + call write_dataset(filter_group, "bins", this % bins ) + end subroutine to_statepoint_energy + + function text_label_energy(this, bin) result(label) + class(EnergyFilter), intent(in) :: this + integer, intent(in) :: bin + character(MAX_LINE_LEN) :: label + + real(8) :: E0, E1 + + E0 = this % bins(bin) + E1 = this % bins(bin + 1) + label = "Incoming Energy [" // trim(to_str(E0)) // ", " & + // trim(to_str(E1)) // ")" + end function text_label_energy + +!=============================================================================== +! EnergyoutFilter methods +!=============================================================================== + subroutine get_next_bin_energyout(this, p, estimator, current_bin, next_bin, & + weight) + class(EnergyoutFilter), intent(in) :: this + type(Particle), intent(in) :: p + integer, intent(in) :: estimator + integer, value, intent(in) :: current_bin + integer, intent(out) :: next_bin + real(8), intent(out) :: weight + + integer :: n + + if (current_bin == NO_BIN_FOUND) then + n = this % n_bins + + if ((.not. run_CE) .and. this % matches_transport_groups) then + next_bin = p % g + + ! Tallies are ordered in increasing groups, group indices + ! however are the opposite, so switch + next_bin = energy_groups - next_bin + 1 + + else + ! Check if energy of the particle is within energy bins. + if (p % E < this % bins(1) .or. p % E > this % bins(n + 1)) then + next_bin = NO_BIN_FOUND + else + ! Search to find incoming energy bin. + next_bin = binary_search(this % bins, n + 1, p % E) + end if + end if + + else + next_bin = NO_BIN_FOUND + end if + weight = ONE + end subroutine get_next_bin_energyout + + subroutine to_statepoint_energyout(this, filter_group) + class(EnergyoutFilter), intent(in) :: this + integer(HID_T), intent(in) :: filter_group + + call write_dataset(filter_group, "type", "energyout") + call write_dataset(filter_group, "n_bins", this % n_bins) + call write_dataset(filter_group, "bins", this % bins ) + end subroutine to_statepoint_energyout + + function text_label_energyout(this, bin) result(label) + class(EnergyoutFilter), intent(in) :: this + integer, intent(in) :: bin + character(MAX_LINE_LEN) :: label + + real(8) :: E0, E1 + + E0 = this % bins(bin) + E1 = this % bins(bin + 1) + label = "Outgoing Energy [" // trim(to_str(E0)) // ", " & + // trim(to_str(E1)) // ")" + end function text_label_energyout + +!=============================================================================== +! DelayedGroupFilter methods +!=============================================================================== + subroutine get_next_bin_dg(this, p, estimator, current_bin, next_bin, weight) + class(DelayedGroupFilter), intent(in) :: this + type(Particle), intent(in) :: p + integer, intent(in) :: estimator + integer, value, intent(in) :: current_bin + integer, intent(out) :: next_bin + real(8), intent(out) :: weight + + if (current_bin == NO_BIN_FOUND) then + next_bin = 1 + else + next_bin = NO_BIN_FOUND + end if + weight = ONE + end subroutine get_next_bin_dg + + subroutine to_statepoint_dg(this, filter_group) + class(DelayedGroupFilter), intent(in) :: this + integer(HID_T), intent(in) :: filter_group + + call write_dataset(filter_group, "type", "delayedgroup") + call write_dataset(filter_group, "n_bins", this % n_bins) + call write_dataset(filter_group, "bins", this % groups ) + end subroutine to_statepoint_dg + + function text_label_dg(this, bin) result(label) + class(DelayedGroupFilter), intent(in) :: this + integer, intent(in) :: bin + character(MAX_LINE_LEN) :: label + + label = "Delayed Group " // to_str(this % groups(bin)) + end function text_label_dg + +!=============================================================================== +! MuFilter methods +!=============================================================================== + subroutine get_next_bin_mu(this, p, estimator, current_bin, next_bin, weight) + class(MuFilter), intent(in) :: this + type(Particle), intent(in) :: p + integer, intent(in) :: estimator + integer, value, intent(in) :: current_bin + integer, intent(out) :: next_bin + real(8), intent(out) :: weight + + integer :: n + + if (current_bin == NO_BIN_FOUND) then + n = this % n_bins + + ! Check if energy of the particle is within energy bins. + if (p % mu < this % bins(1) .or. p % mu > this % bins(n + 1)) then + next_bin = NO_BIN_FOUND + else + ! Search to find incoming energy bin. + next_bin = binary_search(this % bins, n + 1, p % mu) + end if + + else + next_bin = NO_BIN_FOUND + end if + weight = ONE + end subroutine get_next_bin_mu + + subroutine to_statepoint_mu(this, filter_group) + class(MuFilter), intent(in) :: this + integer(HID_T), intent(in) :: filter_group + + call write_dataset(filter_group, "type", "mu") + call write_dataset(filter_group, "n_bins", this % n_bins) + call write_dataset(filter_group, "bins", this % bins ) + end subroutine to_statepoint_mu + + function text_label_mu(this, bin) result(label) + class(MuFilter), intent(in) :: this + integer, intent(in) :: bin + character(MAX_LINE_LEN) :: label + + real(8) :: E0, E1 + + E0 = this % bins(bin) + E1 = this % bins(bin + 1) + label = "Change-in-Angle [" // trim(to_str(E0)) // ", " & + // trim(to_str(E1)) // ")" + end function text_label_mu + +!=============================================================================== +! PolarFilter methods +!=============================================================================== + subroutine get_next_bin_polar(this, p, estimator, current_bin, next_bin, & + weight) + class(PolarFilter), intent(in) :: this + type(Particle), intent(in) :: p + integer, intent(in) :: estimator + integer, value, intent(in) :: current_bin + integer, intent(out) :: next_bin + real(8), intent(out) :: weight + + integer :: n + real(8) :: theta + + if (current_bin == NO_BIN_FOUND) then + n = this % n_bins + + ! Make sure the correct direction vector is used. + if (estimator == ESTIMATOR_TRACKLENGTH) then + theta = acos(p % coord(1) % uvw(3)) + else + theta = acos(p % last_uvw(3)) + end if + + ! Check if particle is within polar angle bins. + if (theta < this % bins(1) .or. theta > this % bins(n + 1)) then + next_bin = NO_BIN_FOUND + else + ! Search to find polar angle bin. + next_bin = binary_search(this % bins, n + 1, theta) + end if + + else + next_bin = NO_BIN_FOUND + end if + weight = ONE + end subroutine get_next_bin_polar + + subroutine to_statepoint_polar(this, filter_group) + class(PolarFilter), intent(in) :: this + integer(HID_T), intent(in) :: filter_group + + call write_dataset(filter_group, "type", "polar") + call write_dataset(filter_group, "n_bins", this % n_bins) + call write_dataset(filter_group, "bins", this % bins ) + end subroutine to_statepoint_polar + + function text_label_polar(this, bin) result(label) + class(PolarFilter), intent(in) :: this + integer, intent(in) :: bin + character(MAX_LINE_LEN) :: label + + real(8) :: E0, E1 + + E0 = this % bins(bin) + E1 = this % bins(bin + 1) + label = "Polar Angle [" // trim(to_str(E0)) // ", " // trim(to_str(E1)) & + // ")" + end function text_label_polar + +!=============================================================================== +! AzimuthalFilter methods +!=============================================================================== + subroutine get_next_bin_azimuthal(this, p, estimator, current_bin, next_bin, & + weight) + class(AzimuthalFilter), intent(in) :: this + type(Particle), intent(in) :: p + integer, intent(in) :: estimator + integer, value, intent(in) :: current_bin + integer, intent(out) :: next_bin + real(8), intent(out) :: weight + + integer :: n + real(8) :: phi + + if (current_bin == NO_BIN_FOUND) then + n = this % n_bins + + ! Make sure the correct direction vector is used. + if (estimator == ESTIMATOR_TRACKLENGTH) then + phi = atan2(p % coord(1) % uvw(2), p % coord(1) % uvw(1)) + else + phi = atan2(p % last_uvw(2), p % last_uvw(1)) + end if + + ! Check if particle is within azimuthal angle bins. + if (phi < this % bins(1) .or. phi > this % bins(n + 1)) then + next_bin = NO_BIN_FOUND + else + ! Search to find azimuthal angle bin. + next_bin = binary_search(this % bins, n + 1, phi) + end if + + else + next_bin = NO_BIN_FOUND + end if + weight = ONE + end subroutine get_next_bin_azimuthal + + subroutine to_statepoint_azimuthal(this, filter_group) + class(AzimuthalFilter), intent(in) :: this + integer(HID_T), intent(in) :: filter_group + + call write_dataset(filter_group, "type", "azimuthal") + call write_dataset(filter_group, "n_bins", this % n_bins) + call write_dataset(filter_group, "bins", this % bins ) + end subroutine to_statepoint_azimuthal + + function text_label_azimuthal(this, bin) result(label) + class(AzimuthalFilter), intent(in) :: this + integer, intent(in) :: bin + character(MAX_LINE_LEN) :: label + + real(8) :: E0, E1 + + E0 = this % bins(bin) + E1 = this % bins(bin + 1) + label = "Azimuthal Angle [" // trim(to_str(E0)) // ", " & + // trim(to_str(E1)) // ")" + end function text_label_azimuthal + +!=============================================================================== +! FIND_OFFSET (for distribcell) uses a given map number, a target cell ID, and +! a target offset to build a string which is the path from the base universe to +! the target cell with the given offset +!=============================================================================== + + recursive subroutine find_offset(goal, univ, final, offset, path) + + integer, intent(in) :: goal ! The target cell index + type(Universe), intent(in) :: univ ! Universe to begin search + integer, intent(in) :: final ! Target offset + integer, intent(inout) :: offset ! Current offset + character(*), intent(inout) :: path ! Path to offset + + integer :: map ! Index in maps vector + integer :: i, j ! Index over cells + integer :: k, l, m ! Indices in lattice + integer :: old_k, old_l, old_m ! Previous indices in lattice + integer :: n_x, n_y, n_z ! Lattice cell array dimensions + integer :: n ! Number of cells to search + integer :: cell_index ! Index in cells array + integer :: lat_offset ! Offset from lattice + integer :: temp_offset ! Looped sum of offsets + logical :: this_cell = .false. ! Advance in this cell? + logical :: later_cell = .false. ! Fill cells after this one? + type(Cell), pointer :: c ! Pointer to current cell + type(Universe), pointer :: next_univ ! Next universe to loop through + class(Lattice), pointer :: lat ! Pointer to current lattice + + ! Get the distribcell index for this cell + map = cells(goal) % distribcell_index + + n = univ % n_cells + + ! Write to the geometry stack + if (univ%id == 0) then + path = trim(path) // to_str(univ%id) + else + path = trim(path) // "->" // to_str(univ%id) + end if + + ! Look through all cells in this universe + do i = 1, n + ! If the cell matches the goal and the offset matches final, write to the + ! geometry stack + if (univ % cells(i) == goal .and. offset == final) then + c => cells(univ % cells(i)) + path = trim(path) // "->" // to_str(c % id) + return + end if + end do + + ! Find the fill cell or lattice cell that we need to enter + do i = 1, n + + later_cell = .false. + + cell_index = univ % cells(i) + c => cells(cell_index) + + this_cell = .false. + + ! If we got here, we still think the target is in this universe + ! or further down, but it's not this exact cell. + ! Compare offset to next cell to see if we should enter this cell + if (i /= n) then + + do j = i+1, n + + cell_index = univ % cells(j) + c => cells(cell_index) + + ! Skip normal cells which do not have offsets + if (c % type == CELL_NORMAL) then + cycle + end if + + ! Break loop once we've found the next cell with an offset + exit + end do + + ! Ensure we didn't just end the loop by iteration + if (c % type /= CELL_NORMAL) then + + ! There are more cells in this universe that it could be in + later_cell = .true. + + ! Two cases, lattice or fill cell + if (c % type == CELL_FILL) then + temp_offset = c % offset(map) + + ! Get the offset of the first lattice location + else + lat => lattices(c % fill) % obj + temp_offset = lat % offset(map, 1, 1, 1) + end if + + ! If the final offset is in the range of offset - temp_offset+offset + ! then the goal is in this cell + if (final < temp_offset + offset) then + this_cell = .true. + end if + end if + end if + + if (n == 1 .and. c % type /= CELL_NORMAL) then + this_cell = .true. + end if + + if (.not. later_cell) then + this_cell = .true. + end if + + ! Get pointer to THIS cell because target must be in this cell + if (this_cell) then + + cell_index = univ % cells(i) + c => cells(cell_index) + + path = trim(path) // "->" // to_str(c%id) + + ! ==================================================================== + ! CELL CONTAINS LOWER UNIVERSE, RECURSIVELY FIND CELL + if (c % type == CELL_FILL) then + + ! Enter this cell to update the current offset + offset = c % offset(map) + offset + + next_univ => universes(c % fill) + call find_offset(goal, next_univ, final, offset, path) + return + + ! ==================================================================== + ! CELL CONTAINS LATTICE, RECURSIVELY FIND CELL + elseif (c % type == CELL_LATTICE) then + + ! Set current lattice + lat => lattices(c % fill) % obj + + select type (lat) + + ! ================================================================== + ! RECTANGULAR LATTICES + type is (RectLattice) + + ! Write to the geometry stack + path = trim(path) // "->" // to_str(lat%id) + + n_x = lat % n_cells(1) + n_y = lat % n_cells(2) + n_z = lat % n_cells(3) + old_m = 1 + old_l = 1 + old_k = 1 + + ! Loop over lattice coordinates + do k = 1, n_x + do l = 1, n_y + do m = 1, n_z + + if (final >= lat % offset(map, k, l, m) + offset) then + if (k == n_x .and. l == n_y .and. m == n_z) then + ! This is last lattice cell, so target must be here + lat_offset = lat % offset(map, k, l, m) + offset = offset + lat_offset + next_univ => universes(lat % universes(k, l, m)) + path = trim(path) // "(" // trim(to_str(k)) // & + "," // trim(to_str(l)) // "," // & + trim(to_str(m)) // ")" + call find_offset(goal, next_univ, final, offset, path) + return + else + old_m = m + old_l = l + old_k = k + cycle + end if + else + ! Target is at this lattice position + lat_offset = lat % offset(map, old_k, old_l, old_m) + offset = offset + lat_offset + next_univ => universes(lat % universes(old_k, old_l, old_m)) + path = trim(path) // "(" // trim(to_str(old_k)) // & + "," // trim(to_str(old_l)) // "," // & + trim(to_str(old_m)) // ")" + call find_offset(goal, next_univ, final, offset, path) + return + end if + + end do + end do + end do + + ! ================================================================== + ! HEXAGONAL LATTICES + type is (HexLattice) + + ! Write to the geometry stack + path = trim(path) // "->" // to_str(lat%id) + + n_z = lat % n_axial + n_y = 2 * lat % n_rings - 1 + n_x = 2 * lat % n_rings - 1 + old_m = 1 + old_l = 1 + old_k = 1 + + ! Loop over lattice coordinates + do m = 1, n_z + do l = 1, n_y + do k = 1, n_x + + ! This array position is never used + if (k + l < lat % n_rings + 1) then + cycle + ! This array position is never used + else if (k + l > 3*lat % n_rings - 1) then + cycle + end if + + if (final >= lat % offset(map, k, l, m) + offset) then + if (k == lat % n_rings .and. l == n_y .and. m == n_z) then + ! This is last lattice cell, so target must be here + lat_offset = lat % offset(map, k, l, m) + offset = offset + lat_offset + next_univ => universes(lat % universes(k, l, m)) + path = trim(path) // "(" // & + trim(to_str(k - lat % n_rings)) // "," // & + trim(to_str(l - lat % n_rings)) // "," // & + trim(to_str(m)) // ")" + call find_offset(goal, next_univ, final, offset, path) + return + else + old_m = m + old_l = l + old_k = k + cycle + end if + else + ! Target is at this lattice position + lat_offset = lat % offset(map, old_k, old_l, old_m) + offset = offset + lat_offset + next_univ => universes(lat % universes(old_k, old_l, old_m)) + path = trim(path) // "(" // & + trim(to_str(old_k - lat % n_rings)) // "," // & + trim(to_str(old_l - lat % n_rings)) // "," // & + trim(to_str(old_m)) // ")" + call find_offset(goal, next_univ, final, offset, path) + return + end if + + end do + end do + end do + + end select + + end if + end if + end do + end subroutine find_offset + +end module tally_filter diff --git a/src/tally_filter_header.F90 b/src/tally_filter_header.F90 new file mode 100644 index 000000000..43ef846e3 --- /dev/null +++ b/src/tally_filter_header.F90 @@ -0,0 +1,105 @@ +module tally_filter_header + + use constants, only: MAX_LINE_LEN + use particle_header, only: Particle + + use hdf5 + + implicit none + +!=============================================================================== +! TALLYFILTER describes a filter that limits what events score to a tally. For +! example, a cell filter indicates that only particles in a specified cell +! should score to the tally. +!=============================================================================== + + type, abstract :: TallyFilter + integer :: n_bins = 0 + contains + procedure(get_next_bin_), deferred :: get_next_bin + procedure(to_statepoint_), deferred :: to_statepoint + procedure :: to_summary => filter_to_summary + procedure(text_label_), deferred :: text_label + procedure :: initialize => filter_initialize + end type TallyFilter + + abstract interface + +!=============================================================================== +! GET_NEXT_BIN gives the index for the next valid filter bin and a weight that +! will be applied to the flux. +! +! In principle, a filter can have multiple valid bins. If current_bin = +! NO_BIN_FOUND, then this method should give the first valid bin. Providing the +! first valid bin should then give the second valid bin, and so on. When there +! are no valid bins left, the next_bin should be NO_VALID_BIN. + + subroutine get_next_bin_(this, p, estimator, current_bin, next_bin, weight) + import TallyFilter + import Particle + class(TallyFilter), intent(in) :: this + type(Particle), intent(in) :: p + integer, intent(in) :: estimator + integer, value, intent(in) :: current_bin + integer, intent(out) :: next_bin + real(8), intent(out) :: weight + end subroutine get_next_bin_ + +!=============================================================================== +! TO_STATEPOINT writes all the information needed to reconstruct the filter to +! the given filter_group. + + subroutine to_statepoint_(this, filter_group) + import TallyFilter + import HID_T + class(TallyFilter), intent(in) :: this + integer(HID_T), intent(in) :: filter_group + end subroutine to_statepoint_ + +!=============================================================================== +! TEXT_LABEL returns a string describing the given filter bin. For example, an +! energy filter might return the string "Incoming Energy [0.625E-6, 20.0)". +! This is used to write the tallies.out file. + + function text_label_(this, bin) result(label) + import TallyFilter + import MAX_LINE_LEN + class(TallyFilter), intent(in) :: this + integer, intent(in) :: bin + character(MAX_LINE_LEN) :: label + end function text_label_ + + end interface + +!=============================================================================== +! TALLYFILTERCONTAINER contains an allocatable TallyFilter object for arrays of +! TallyFilters +!=============================================================================== + + type TallyFilterContainer + class(TallyFilter), allocatable :: obj + end type TallyFilterContainer + +contains + +!=============================================================================== +! TO_SUMMARY writes all the information needed to reconstruct the filter to the +! given filter_group. If this procedure is not overridden by the derived class, +! then it will call to_statepoint by default. + + subroutine filter_to_summary(this, filter_group) + class(TallyFilter), intent(in) :: this + integer(HID_T), intent(in) :: filter_group + + call this % to_statepoint(filter_group) + end subroutine filter_to_summary + +!=============================================================================== +! INITIALIZE sets up any internal data, as necessary. If this procedure is not +! overriden by the derived class, then it will do nothing by default. + + subroutine filter_initialize(this) + class(TallyFilter), intent(inout) :: this + end subroutine filter_initialize + +end module tally_filter_header diff --git a/src/tally_header.F90 b/src/tally_header.F90 index 8db9ab0e1..fe385458b 100644 --- a/src/tally_header.F90 +++ b/src/tally_header.F90 @@ -1,41 +1,13 @@ module tally_header - use constants, only: NONE, N_FILTER_TYPES - use trigger_header, only: TriggerObject + use constants, only: NONE, N_FILTER_TYPES + use tally_filter_header, only: TallyFilterContainer + use trigger_header, only: TriggerObject + use, intrinsic :: ISO_C_BINDING implicit none -!=============================================================================== -! TALLYMAPELEMENT gives an index to a tally which is to be scored and the -! corresponding bin for the filter variable -!=============================================================================== - - type TallyMapElement - integer :: index_tally - integer :: index_bin - end type TallyMapElement - -!=============================================================================== -! TALLYMAPITEM contains a list of tally/bin combinations for each mappable -! filter bin specified. -!=============================================================================== - - type TallyMapItem - type(TallyMapElement), allocatable :: elements(:) - end type TallyMapItem - -!=============================================================================== -! TALLYMAP contains a list of pairs of indices to tallies and the corresponding -! bin for a given filter. There is one TallyMap for each mappable filter -! type. The items array is as long as the corresponding array for that filter, -! e.g. for tally_maps(FILTER_CELL), items is n_cells long. -!=============================================================================== - - type TallyMap - type(TallyMapItem), allocatable :: items(:) - end type TallyMap - !=============================================================================== ! TALLYRESULT provides accumulation of results in a particular tally bin !=============================================================================== @@ -46,19 +18,6 @@ module tally_header real(C_DOUBLE) :: sum_sq = 0. end type TallyResult -!=============================================================================== -! TALLYFILTER describes a filter that limits what events score to a tally. For -! example, a cell filter indicates that only particles in a specified cell -! should score to the tally. -!=============================================================================== - - type TallyFilter - integer :: type = NONE - integer :: n_bins = 0 - integer, allocatable :: int_bins(:) - real(8), allocatable :: real_bins(:) ! Only used for energy filters - end type TallyFilter - !=============================================================================== ! TALLYOBJECT describes a user-specified tally. The region of phase space to ! tally in is given by the TallyFilters and the results are stored in a @@ -73,11 +32,7 @@ module tally_header integer :: type ! volume, surface current integer :: estimator ! collision, track-length real(8) :: volume ! volume of region - - ! Information about what filters should be used - - integer :: n_filters ! Number of filters - type(TallyFilter), allocatable :: filters(:) ! Filter data (type/bins) + type(TallyFilterContainer), allocatable :: filters(:) ! The stride attribute is used for determining the index in the results ! array for a matching_bin combination. Since multiple dimensions are @@ -124,10 +79,6 @@ module tally_header ! Tally precision triggers integer :: n_triggers = 0 ! # of triggers type(TriggerObject), allocatable :: triggers(:) ! Array of triggers - - ! Multi-Group Specific Information To Enable Rapid Tallying - logical :: energy_matches_groups = .false. - logical :: energyout_matches_groups = .false. end type TallyObject end module tally_header diff --git a/src/tally_initialize.F90 b/src/tally_initialize.F90 index 73aa18c60..1ae403bd6 100644 --- a/src/tally_initialize.F90 +++ b/src/tally_initialize.F90 @@ -2,7 +2,7 @@ module tally_initialize use constants use global - use tally_header, only: TallyObject, TallyMapElement, TallyMapItem + use tally_header, only: TallyObject implicit none private @@ -23,7 +23,6 @@ contains allocate(global_tallies(N_GLOBAL_TALLIES)) call setup_tally_arrays() - call setup_tally_maps() end subroutine configure_tallies @@ -45,17 +44,17 @@ contains t => tallies(i) ! Allocate stride and matching_bins arrays - allocate(t % stride(t % n_filters)) - max_n_filters = max(max_n_filters, t % n_filters) + allocate(t % stride(size(t % filters))) + max_n_filters = max(max_n_filters, size(t % filters)) ! The filters are traversed in opposite order so that the last filter has ! the shortest stride in memory and the first filter has the largest ! stride n = 1 - STRIDE: do j = t % n_filters, 1, -1 + STRIDE: do j = size(t % filters), 1, -1 t % stride(j) = n - n = n * t % filters(j) % n_bins + n = n * t % filters(j) % obj % n_bins end do STRIDE ! Set total number of filter and scoring bins @@ -70,101 +69,11 @@ contains ! Allocate array for matching filter bins !$omp parallel allocate(matching_bins(max_n_filters)) + allocate(filter_weights(max_n_filters)) !$omp end parallel end subroutine setup_tally_arrays -!=============================================================================== -! SETUP_TALLY_MAPS creates a map that allows a quick determination of which -! tallies and bins need to be scored to when a particle makes a collision. This -! subroutine also sets the stride attribute for each tally as well as allocating -! storage for the results array. -!=============================================================================== - - subroutine setup_tally_maps() - - integer :: i ! loop index for tallies - integer :: j ! loop index for filters - integer :: k ! loop index for bins - integer :: bin ! filter bin entries - integer :: type ! type of tally filter - type(TallyObject), pointer :: t - - ! allocate tally map array -- note that we don't need a tally map for the - ! energy_in and energy_out filters - allocate(tally_maps(N_FILTER_TYPES - 3)) - - ! allocate list of items for each different filter type - allocate(tally_maps(FILTER_UNIVERSE) % items(n_universes)) - allocate(tally_maps(FILTER_MATERIAL) % items(n_materials)) - allocate(tally_maps(FILTER_CELL) % items(n_cells)) - allocate(tally_maps(FILTER_CELLBORN) % items(n_cells)) - allocate(tally_maps(FILTER_SURFACE) % items(n_surfaces)) - - TALLY_LOOP: do i = 1, n_tallies - ! Get pointer to tally - t => tallies(i) - - ! No need to set up tally maps for surface current tallies - if (t % type == TALLY_SURFACE_CURRENT) cycle - - FILTER_LOOP: do j = 1, t % n_filters - ! Determine type of filter - type = t % filters(j) % type - - if (type == FILTER_CELL .or. type == FILTER_SURFACE .or. & - type == FILTER_MATERIAL .or. type == FILTER_UNIVERSE .or. & - type == FILTER_CELLBORN) then - - ! Add map elements - BIN_LOOP: do k = 1, t % filters(j) % n_bins - bin = t % filters(j) % int_bins(k) - call add_map_element(tally_maps(type) % items(bin), i, k) - end do BIN_LOOP - end if - - end do FILTER_LOOP - - end do TALLY_LOOP - - end subroutine setup_tally_maps - -!=============================================================================== -! ADD_MAP_ELEMENT adds a pair of tally and bin indices to the list for a given -! cell/surface/etc. -!=============================================================================== - - subroutine add_map_element(item, index_tally, index_bin) - - type(TallyMapItem), intent(inout) :: item - integer, intent(in) :: index_tally ! index in tallies array - integer, intent(in) :: index_bin ! index in bins array - - integer :: n ! size of elements array - type(TallyMapElement), allocatable :: temp(:) - - if (.not. allocated(item % elements)) then - allocate(item % elements(1)) - item % elements(1) % index_tally = index_tally - item % elements(1) % index_bin = index_bin - else - ! determine size of elements array - n = size(item % elements) - - ! allocate temporary storage and copy elements - allocate(temp(n+1)) - temp(1:n) = item % elements - - ! move allocation back to main array - call move_alloc(FROM=temp, TO=item%elements) - - ! set new element - item % elements(n+1) % index_tally = index_tally - item % elements(n+1) % index_bin = index_bin - end if - - end subroutine add_map_element - !=============================================================================== ! ADD_TALLIES extends the tallies array with a new group of tallies and assigns ! pointers to each group. This is called once for user tallies, once for CMFD diff --git a/src/tracking.F90 b/src/tracking.F90 index d52b09a75..69fb78c35 100644 --- a/src/tracking.F90 +++ b/src/tracking.F90 @@ -191,7 +191,7 @@ contains p % fission = .false. ! Save coordinates for tallying purposes - p % last_xyz = p % coord(1) % xyz + p % last_xyz_current = p % coord(1) % xyz ! Set last material to none since cross sections will need to be ! re-evaluated @@ -211,6 +211,9 @@ contains end do end if + ! Save coordinates for tallying purposes + p % last_xyz = p % coord(1) % xyz + ! If particle has too many events, display warning and kill it n_event = n_event + 1 if (n_event == MAX_EVENTS) then diff --git a/src/trigger.F90 b/src/trigger.F90 index 7d488acb7..570493a5d 100644 --- a/src/trigger.F90 +++ b/src/trigger.F90 @@ -12,6 +12,7 @@ module trigger use mesh_header, only: RegularMesh use trigger_header, only: TriggerObject use tally, only: TallyObject + use tally_filter, only: MeshFilter implicit none @@ -165,7 +166,7 @@ contains else ! Initialize bins, filter level - matching_bins(1:t % n_filters) = 0 + matching_bins(1:size(t % filters)) = 0 FILTER_LOOP: do filter_index = 1, t % total_filter_bins @@ -265,7 +266,7 @@ contains end if end if end do NUCLIDE_LOOP - if (t % n_filters == 0) exit FILTER_LOOP + if (size(t % filters) == 0) exit FILTER_LOOP end do FILTER_LOOP end if end do TRIGGER_LOOP @@ -300,16 +301,19 @@ contains ! Get pointer to mesh i_filter_mesh = t % find_filter(FILTER_MESH) i_filter_surf = t % find_filter(FILTER_SURFACE) - m => meshes(t % filters(i_filter_mesh) % int_bins(1)) + select type(filt => t % filters(i_filter_mesh) % obj) + type is (MeshFilter) + m => meshes(filt % mesh) + end select ! initialize bins array - matching_bins(1:t % n_filters) = 1 + matching_bins(1:size(t % filters)) = 1 ! determine how many energyin bins there are i_filter_ein = t % find_filter(FILTER_ENERGYIN) if (i_filter_ein > 0) then print_ebin = .true. - n = t % filters(i_filter_ein) % n_bins + n = t % filters(i_filter_ein) % obj % n_bins else print_ebin = .false. n = 1 @@ -330,7 +334,7 @@ contains ! Left Surface matching_bins(i_filter_surf) = OUT_LEFT filter_index = & - sum((matching_bins(1:t % n_filters) - 1) * t % stride) + 1 + sum((matching_bins(1:size(t % filters)) - 1) * t % stride) + 1 call get_trigger_uncertainty(std_dev, rel_err, 1, filter_index, t) if (trigger % std_dev < std_dev) then trigger % std_dev = std_dev @@ -343,7 +347,7 @@ contains ! Right Surface matching_bins(i_filter_surf) = OUT_RIGHT filter_index = & - sum((matching_bins(1:t % n_filters) - 1) * t % stride) + 1 + sum((matching_bins(1:size(t % filters)) - 1) * t % stride) + 1 call get_trigger_uncertainty(std_dev, rel_err, 1, filter_index, t) if (trigger % std_dev < std_dev) then trigger % std_dev = std_dev @@ -356,7 +360,7 @@ contains ! Back Surface matching_bins(i_filter_surf) = OUT_BACK filter_index = & - sum((matching_bins(1:t % n_filters) - 1) * t % stride) + 1 + sum((matching_bins(1:size(t % filters)) - 1) * t % stride) + 1 call get_trigger_uncertainty(std_dev, rel_err, 1, filter_index, t) if (trigger % std_dev < std_dev) then trigger % std_dev = std_dev @@ -369,7 +373,7 @@ contains ! Front Surface matching_bins(i_filter_surf) = OUT_FRONT filter_index = & - sum((matching_bins(1:t % n_filters) - 1) * t % stride) + 1 + sum((matching_bins(1:size(t % filters)) - 1) * t % stride) + 1 call get_trigger_uncertainty(std_dev, rel_err, 1, filter_index, t) if (trigger % std_dev < std_dev) then trigger % std_dev = std_dev @@ -382,7 +386,7 @@ contains ! Bottom Surface matching_bins(i_filter_surf) = OUT_BOTTOM filter_index = & - sum((matching_bins(1:t % n_filters) - 1) * t % stride) + 1 + sum((matching_bins(1:size(t % filters)) - 1) * t % stride) + 1 call get_trigger_uncertainty(std_dev, rel_err, 1, filter_index, t) if (trigger % std_dev < std_dev) then trigger % std_dev = std_dev @@ -395,7 +399,7 @@ contains ! Top Surface matching_bins(i_filter_surf) = OUT_TOP filter_index = & - sum((matching_bins(1:t % n_filters) - 1) * t % stride) + 1 + sum((matching_bins(1:size(t % filters)) - 1) * t % stride) + 1 call get_trigger_uncertainty(std_dev, rel_err, 1, filter_index, t) if (trigger % std_dev < std_dev) then trigger % std_dev = std_dev diff --git a/tests/test_mgxs_library_mesh/results_true.dat b/tests/test_mgxs_library_mesh/results_true.dat index e3d7ca647..03019cfd3 100644 --- a/tests/test_mgxs_library_mesh/results_true.dat +++ b/tests/test_mgxs_library_mesh/results_true.dat @@ -126,7 +126,7 @@ 3 2 2 1 1 total 3.799163e-07 1.806470e-07 mesh 1 group in nuclide mean std. dev. x y z -0 1 1 1 1 total 0.025735 0.002840 -1 1 2 1 1 total 0.028773 0.006349 -2 2 1 1 1 total 0.022306 0.004010 -3 2 2 1 1 total 0.024549 0.009379 +0 1 1 1 1 total 0.025920 0.002893 +1 1 2 1 1 total 0.028922 0.006394 +2 2 1 1 1 total 0.022467 0.004039 +3 2 2 1 1 total 0.024923 0.009632 diff --git a/tests/test_score_current/results_true.dat b/tests/test_score_current/results_true.dat index d3ac03a70..5d26226ae 100644 --- a/tests/test_score_current/results_true.dat +++ b/tests/test_score_current/results_true.dat @@ -1 +1 @@ -a9310752363eb059ff40f16ac9716b41ccab6ec6607d29f498069318745e485d18d784264304cc2586865bd58cef7587203cc22a1d485c58ddd63c14c0defdb9 \ No newline at end of file +bafab1921a12146abb2bb29603b52b9cc28a5a950a7a6bb1e3f012c05891c310fad643760d4f148b04d0fef3d1f3e141d146e3a278d81cc6fc8187c37717c5e7 \ No newline at end of file diff --git a/tests/test_tallies/inputs_true.dat b/tests/test_tallies/inputs_true.dat index 17f04238c..9d67bc0d0 100644 --- a/tests/test_tallies/inputs_true.dat +++ b/tests/test_tallies/inputs_true.dat @@ -1 +1 @@ -ca47172a42f6c13b244a763c990cbe4811662708ee03307d810a4542ee34bb5db7cc29d66aea313dad95b9f38a4ff7943ded527cfd0c7c8825372fec40cfc0d0 \ No newline at end of file +930af242a043f2676a000dbc5a2db6b148edcb31ed8c87dbaa35a8efb37a3be8cff30cdf4dc03f9c5c7eb4021f7e4c3327e64681cdd8fd8722c95c69db850227 \ No newline at end of file diff --git a/tests/test_tallies/results_true.dat b/tests/test_tallies/results_true.dat index 76ad489ca..7aa65e1c1 100644 --- a/tests/test_tallies/results_true.dat +++ b/tests/test_tallies/results_true.dat @@ -1 +1 @@ -7d085e38f331083a8c3f7814eb6025f9457a1a8e3899e7d9a65af1ba92ea75c0182fcc605d4eadd3eb7edfc0949df688d39a3f45683e10cbdcfb6d7954f34129 \ No newline at end of file +a51db2a4efc681805f85968e04411dc33beee0532c202f5179b9a82880ab60a75e53fa9141c81045ea1d2842372f2d8da900326f09382ea61dd80a3c9b43bba1 \ No newline at end of file diff --git a/tests/test_tallies/test_tallies.py b/tests/test_tallies/test_tallies.py index 0e8d62268..ba0098513 100644 --- a/tests/test_tallies/test_tallies.py +++ b/tests/test_tallies/test_tallies.py @@ -113,10 +113,11 @@ class TalliesTestHarness(PyAPITestHarness): polar_tally4.estimator = 'tracklength' universe_tally = Tally() - universe_tally.filters = [Filter(type='universe', bins=(1, 2, 3, 4))] + universe_tally.filters = [ + Filter(type='universe', bins=(1, 2, 3, 4, 6, 8))] universe_tally.scores = ['total'] - cell_filter = Filter(type='cell', bins=(10, 21, 22, 23)) + cell_filter = Filter(type='cell', bins=(10, 21, 22, 23, 60)) score_tallies = [Tally(), Tally(), Tally()] for t in score_tallies: t.filters = [cell_filter] @@ -128,7 +129,7 @@ class TalliesTestHarness(PyAPITestHarness): score_tallies[1].estimator = 'analog' score_tallies[2].estimator = 'collision' - cell_filter2 = Filter(type='cell', bins=(21, 22, 23, 27, 28, 29)) + cell_filter2 = Filter(type='cell', bins=(21, 22, 23, 27, 28, 29, 60)) flux_tallies = [Tally() for i in range(4)] for t in flux_tallies: t.filters = [cell_filter2]