mirror of
https://github.com/openmc-dev/openmc.git
synced 2026-07-29 06:35:48 -04:00
Merge remote-tracking branch 'upstream/develop' into multipole
This commit is contained in:
commit
27f28f827f
209 changed files with 33278 additions and 12023 deletions
|
|
@ -44,7 +44,7 @@ before_script:
|
|||
- git clone --branch=master git://github.com/bhermanmit/nndc_xs nndc_xs
|
||||
- cat nndc_xs/nndc.tar.gza* | tar xzvf -
|
||||
- rm -rf nndc_xs
|
||||
- export CROSS_SECTIONS=$PWD/nndc/cross_sections.xml
|
||||
- export OPENMC_CROSS_SECTIONS=$PWD/nndc/cross_sections.xml
|
||||
- wget http://web.mit.edu/smharper/Public/multipole_lib.tar.gz
|
||||
- tar -xzf multipole_lib.tar.gz
|
||||
- export MULTIPOLE_LIBRARY=$PWD/multipole_lib
|
||||
|
|
|
|||
|
|
@ -4,9 +4,10 @@ The OpenMC Monte Carlo Code
|
|||
|
||||
OpenMC is a Monte Carlo particle transport simulation code focused on neutron
|
||||
criticality calculations. It is capable of simulating 3D models based on
|
||||
constructive solid geometry with second-order surfaces. The particle interaction
|
||||
data is based on ACE format cross sections, also used in the MCNP and Serpent
|
||||
Monte Carlo codes.
|
||||
constructive solid geometry with second-order surfaces. OpenMC supports either
|
||||
continuous-energy or multi-group transport. The continuous-energy
|
||||
particle interaction data is based on ACE format cross sections, also used
|
||||
in the MCNP and Serpent Monte Carlo codes.
|
||||
|
||||
OpenMC was originally developed by members of the `Computational Reactor Physics
|
||||
Group`_ at the `Massachusetts Institute of Technology`_ starting
|
||||
|
|
|
|||
|
|
@ -84,10 +84,10 @@ to fully define the surface.
|
|||
| Plane perpendicular | x-plane | :math:`x - x_0 = 0` | :math:`x_0` |
|
||||
| to :math:`x`-axis | | | |
|
||||
+----------------------+------------+------------------------------+-------------------------+
|
||||
| Plane perpendicular | y-plane | :math:`x - x_0 = 0` | :math:`y_0` |
|
||||
| Plane perpendicular | y-plane | :math:`y - y_0 = 0` | :math:`y_0` |
|
||||
| to :math:`y`-axis | | | |
|
||||
+----------------------+------------+------------------------------+-------------------------+
|
||||
| Plane perpendicular | z-plane | :math:`x - x_0 = 0` | :math:`z_0` |
|
||||
| Plane perpendicular | z-plane | :math:`z - z_0 = 0` | :math:`z_0` |
|
||||
| to :math:`z`-axis | | | |
|
||||
+----------------------+------------+------------------------------+-------------------------+
|
||||
| Arbitrary plane | plane | :math:`Ax + By + Cz = D` | :math:`A\;B\;C\;D` |
|
||||
|
|
|
|||
|
|
@ -341,7 +341,7 @@
|
|||
"settings_file.batches = batches\n",
|
||||
"settings_file.inactive = inactive\n",
|
||||
"settings_file.particles = particles\n",
|
||||
"settings_file.output = {'tallies': True, 'summary': True}\n",
|
||||
"settings_file.output = {'tallies': True}\n",
|
||||
"bounds = [-0.63, -0.63, -0.63, 0.63, 0.63, 0.63]\n",
|
||||
"settings_file.source = Source(space=Box(\n",
|
||||
" bounds[:3], bounds[3:], only_fissionable=True))\n",
|
||||
|
|
@ -518,8 +518,9 @@
|
|||
" Copyright: 2011-2015 Massachusetts Institute of Technology\n",
|
||||
" License: http://mit-crpg.github.io/openmc/license.html\n",
|
||||
" Version: 0.7.1\n",
|
||||
" Git SHA1: ea9fb637f63f9374c7436456141afa850b84acf9\n",
|
||||
" Date/Time: 2016-01-14 07:16:05\n",
|
||||
" Git SHA1: 34381b40a9445a727e360873aaa6ef892af1cb6a\n",
|
||||
" Date/Time: 2016-02-07 15:58:16\n",
|
||||
" MPI Processes: 1\n",
|
||||
"\n",
|
||||
" ===========================================================================\n",
|
||||
" ========================> INITIALIZATION <=========================\n",
|
||||
|
|
@ -604,20 +605,20 @@
|
|||
"\n",
|
||||
" =======================> TIMING STATISTICS <=======================\n",
|
||||
"\n",
|
||||
" Total time for initialization = 1.1720E+00 seconds\n",
|
||||
" Reading cross sections = 9.0300E-01 seconds\n",
|
||||
" Total time in simulation = 1.7319E+01 seconds\n",
|
||||
" Time in transport only = 1.7310E+01 seconds\n",
|
||||
" Time in inactive batches = 1.9120E+00 seconds\n",
|
||||
" Time in active batches = 1.5407E+01 seconds\n",
|
||||
" Time synchronizing fission bank = 2.0000E-03 seconds\n",
|
||||
" Sampling source sites = 2.0000E-03 seconds\n",
|
||||
" SEND/RECV source sites = 0.0000E+00 seconds\n",
|
||||
" Total time for initialization = 3.2100E-01 seconds\n",
|
||||
" Reading cross sections = 7.4000E-02 seconds\n",
|
||||
" Total time in simulation = 8.3830E+00 seconds\n",
|
||||
" Time in transport only = 8.3670E+00 seconds\n",
|
||||
" Time in inactive batches = 1.0330E+00 seconds\n",
|
||||
" Time in active batches = 7.3500E+00 seconds\n",
|
||||
" Time synchronizing fission bank = 4.0000E-03 seconds\n",
|
||||
" Sampling source sites = 1.0000E-03 seconds\n",
|
||||
" SEND/RECV source sites = 3.0000E-03 seconds\n",
|
||||
" Time accumulating tallies = 0.0000E+00 seconds\n",
|
||||
" Total time for finalization = 1.0000E-03 seconds\n",
|
||||
" Total time elapsed = 1.8507E+01 seconds\n",
|
||||
" Calculation Rate (inactive) = 13075.3 neutrons/second\n",
|
||||
" Calculation Rate (active) = 6490.56 neutrons/second\n",
|
||||
" Total time elapsed = 8.7140E+00 seconds\n",
|
||||
" Calculation Rate (inactive) = 24201.4 neutrons/second\n",
|
||||
" Calculation Rate (active) = 13605.4 neutrons/second\n",
|
||||
"\n",
|
||||
" ============================> RESULTS <============================\n",
|
||||
"\n",
|
||||
|
|
@ -794,19 +795,19 @@
|
|||
" <tbody>\n",
|
||||
" <tr>\n",
|
||||
" <th>1</th>\n",
|
||||
" <td>1</td>\n",
|
||||
" <td>1</td>\n",
|
||||
" <td>total</td>\n",
|
||||
" <td>0.668323</td>\n",
|
||||
" <td>0.001264</td>\n",
|
||||
" <td> 1</td>\n",
|
||||
" <td> 1</td>\n",
|
||||
" <td> total</td>\n",
|
||||
" <td> 0.668323</td>\n",
|
||||
" <td> 0.001264</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>0</th>\n",
|
||||
" <td>1</td>\n",
|
||||
" <td>2</td>\n",
|
||||
" <td>total</td>\n",
|
||||
" <td>1.293258</td>\n",
|
||||
" <td>0.007624</td>\n",
|
||||
" <td> 1</td>\n",
|
||||
" <td> 2</td>\n",
|
||||
" <td> total</td>\n",
|
||||
" <td> 1.293258</td>\n",
|
||||
" <td> 0.007624</td>\n",
|
||||
" </tr>\n",
|
||||
" </tbody>\n",
|
||||
"</table>\n",
|
||||
|
|
@ -896,7 +897,8 @@
|
|||
" <tr style=\"text-align: right;\">\n",
|
||||
" <th></th>\n",
|
||||
" <th>cell</th>\n",
|
||||
" <th>energy [MeV]</th>\n",
|
||||
" <th>energy low [MeV]</th>\n",
|
||||
" <th>energy high [MeV]</th>\n",
|
||||
" <th>nuclide</th>\n",
|
||||
" <th>score</th>\n",
|
||||
" <th>mean</th>\n",
|
||||
|
|
@ -906,34 +908,36 @@
|
|||
" <tbody>\n",
|
||||
" <tr>\n",
|
||||
" <th>0</th>\n",
|
||||
" <td>1</td>\n",
|
||||
" <td>(0.0e+00 - 6.3e-07)</td>\n",
|
||||
" <td>total</td>\n",
|
||||
" <td>(((total / flux) - (absorption / flux)) - (sca...</td>\n",
|
||||
" <td>4.884981e-15</td>\n",
|
||||
" <td>0.011274</td>\n",
|
||||
" <td> 1</td>\n",
|
||||
" <td> 0.000000</td>\n",
|
||||
" <td> 0.000001</td>\n",
|
||||
" <td> total</td>\n",
|
||||
" <td> (((total / flux) - (absorption / flux)) - (sca...</td>\n",
|
||||
" <td> 4.884981e-15</td>\n",
|
||||
" <td> 0.011274</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>1</th>\n",
|
||||
" <td>1</td>\n",
|
||||
" <td>(6.3e-07 - 2.0e+01)</td>\n",
|
||||
" <td>total</td>\n",
|
||||
" <td>(((total / flux) - (absorption / flux)) - (sca...</td>\n",
|
||||
" <td>1.221245e-15</td>\n",
|
||||
" <td>0.001802</td>\n",
|
||||
" <td> 1</td>\n",
|
||||
" <td> 0.000001</td>\n",
|
||||
" <td> 20.000000</td>\n",
|
||||
" <td> total</td>\n",
|
||||
" <td> (((total / flux) - (absorption / flux)) - (sca...</td>\n",
|
||||
" <td> 1.221245e-15</td>\n",
|
||||
" <td> 0.001802</td>\n",
|
||||
" </tr>\n",
|
||||
" </tbody>\n",
|
||||
"</table>\n",
|
||||
"</div>"
|
||||
],
|
||||
"text/plain": [
|
||||
" cell energy [MeV] nuclide \\\n",
|
||||
"0 1 (0.0e+00 - 6.3e-07) total \n",
|
||||
"1 1 (6.3e-07 - 2.0e+01) total \n",
|
||||
" cell energy low [MeV] energy high [MeV] nuclide \\\n",
|
||||
"0 1 0.00e+00 6.25e-07 total \n",
|
||||
"1 1 6.25e-07 2.00e+01 total \n",
|
||||
"\n",
|
||||
" score mean std. dev. \n",
|
||||
"0 (((total / flux) - (absorption / flux)) - (sca... 4.884981e-15 0.011274 \n",
|
||||
"1 (((total / flux) - (absorption / flux)) - (sca... 1.221245e-15 0.001802 "
|
||||
" score mean std. dev. \n",
|
||||
"0 (((total / flux) - (absorption / flux)) - (sca... 4.88e-15 1.13e-02 \n",
|
||||
"1 (((total / flux) - (absorption / flux)) - (sca... 1.22e-15 1.80e-03 "
|
||||
]
|
||||
},
|
||||
"execution_count": 23,
|
||||
|
|
@ -972,7 +976,8 @@
|
|||
" <tr style=\"text-align: right;\">\n",
|
||||
" <th></th>\n",
|
||||
" <th>cell</th>\n",
|
||||
" <th>energy [MeV]</th>\n",
|
||||
" <th>energy low [MeV]</th>\n",
|
||||
" <th>energy high [MeV]</th>\n",
|
||||
" <th>nuclide</th>\n",
|
||||
" <th>score</th>\n",
|
||||
" <th>mean</th>\n",
|
||||
|
|
@ -982,34 +987,36 @@
|
|||
" <tbody>\n",
|
||||
" <tr>\n",
|
||||
" <th>0</th>\n",
|
||||
" <td>1</td>\n",
|
||||
" <td>(0.0e+00 - 6.3e-07)</td>\n",
|
||||
" <td>total</td>\n",
|
||||
" <td>((absorption / flux) / (total / flux))</td>\n",
|
||||
" <td>0.076219</td>\n",
|
||||
" <td>0.000651</td>\n",
|
||||
" <td> 1</td>\n",
|
||||
" <td> 0.000000</td>\n",
|
||||
" <td> 0.000001</td>\n",
|
||||
" <td> total</td>\n",
|
||||
" <td> ((absorption / flux) / (total / flux))</td>\n",
|
||||
" <td> 0.076219</td>\n",
|
||||
" <td> 0.000651</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>1</th>\n",
|
||||
" <td>1</td>\n",
|
||||
" <td>(6.3e-07 - 2.0e+01)</td>\n",
|
||||
" <td>total</td>\n",
|
||||
" <td>((absorption / flux) / (total / flux))</td>\n",
|
||||
" <td>0.019319</td>\n",
|
||||
" <td>0.000086</td>\n",
|
||||
" <td> 1</td>\n",
|
||||
" <td> 0.000001</td>\n",
|
||||
" <td> 20.000000</td>\n",
|
||||
" <td> total</td>\n",
|
||||
" <td> ((absorption / flux) / (total / flux))</td>\n",
|
||||
" <td> 0.019319</td>\n",
|
||||
" <td> 0.000086</td>\n",
|
||||
" </tr>\n",
|
||||
" </tbody>\n",
|
||||
"</table>\n",
|
||||
"</div>"
|
||||
],
|
||||
"text/plain": [
|
||||
" cell energy [MeV] nuclide score \\\n",
|
||||
"0 1 (0.0e+00 - 6.3e-07) total ((absorption / flux) / (total / flux)) \n",
|
||||
"1 1 (6.3e-07 - 2.0e+01) total ((absorption / flux) / (total / flux)) \n",
|
||||
" cell energy low [MeV] energy high [MeV] nuclide \\\n",
|
||||
"0 1 0.00e+00 6.25e-07 total \n",
|
||||
"1 1 6.25e-07 2.00e+01 total \n",
|
||||
"\n",
|
||||
" mean std. dev. \n",
|
||||
"0 0.076219 0.000651 \n",
|
||||
"1 0.019319 0.000086 "
|
||||
" score mean std. dev. \n",
|
||||
"0 ((absorption / flux) / (total / flux)) 7.62e-02 6.51e-04 \n",
|
||||
"1 ((absorption / flux) / (total / flux)) 1.93e-02 8.65e-05 "
|
||||
]
|
||||
},
|
||||
"execution_count": 24,
|
||||
|
|
@ -1041,7 +1048,8 @@
|
|||
" <tr style=\"text-align: right;\">\n",
|
||||
" <th></th>\n",
|
||||
" <th>cell</th>\n",
|
||||
" <th>energy [MeV]</th>\n",
|
||||
" <th>energy low [MeV]</th>\n",
|
||||
" <th>energy high [MeV]</th>\n",
|
||||
" <th>nuclide</th>\n",
|
||||
" <th>score</th>\n",
|
||||
" <th>mean</th>\n",
|
||||
|
|
@ -1051,34 +1059,36 @@
|
|||
" <tbody>\n",
|
||||
" <tr>\n",
|
||||
" <th>0</th>\n",
|
||||
" <td>1</td>\n",
|
||||
" <td>(0.0e+00 - 6.3e-07)</td>\n",
|
||||
" <td>total</td>\n",
|
||||
" <td>((scatter / flux) / (total / flux))</td>\n",
|
||||
" <td>0.923781</td>\n",
|
||||
" <td>0.007714</td>\n",
|
||||
" <td> 1</td>\n",
|
||||
" <td> 0.000000</td>\n",
|
||||
" <td> 0.000001</td>\n",
|
||||
" <td> total</td>\n",
|
||||
" <td> ((scatter / flux) / (total / flux))</td>\n",
|
||||
" <td> 0.923781</td>\n",
|
||||
" <td> 0.007714</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>1</th>\n",
|
||||
" <td>1</td>\n",
|
||||
" <td>(6.3e-07 - 2.0e+01)</td>\n",
|
||||
" <td>total</td>\n",
|
||||
" <td>((scatter / flux) / (total / flux))</td>\n",
|
||||
" <td>0.980681</td>\n",
|
||||
" <td>0.002617</td>\n",
|
||||
" <td> 1</td>\n",
|
||||
" <td> 0.000001</td>\n",
|
||||
" <td> 20.000000</td>\n",
|
||||
" <td> total</td>\n",
|
||||
" <td> ((scatter / flux) / (total / flux))</td>\n",
|
||||
" <td> 0.980681</td>\n",
|
||||
" <td> 0.002617</td>\n",
|
||||
" </tr>\n",
|
||||
" </tbody>\n",
|
||||
"</table>\n",
|
||||
"</div>"
|
||||
],
|
||||
"text/plain": [
|
||||
" cell energy [MeV] nuclide score \\\n",
|
||||
"0 1 (0.0e+00 - 6.3e-07) total ((scatter / flux) / (total / flux)) \n",
|
||||
"1 1 (6.3e-07 - 2.0e+01) total ((scatter / flux) / (total / flux)) \n",
|
||||
" cell energy low [MeV] energy high [MeV] nuclide \\\n",
|
||||
"0 1 0.00e+00 6.25e-07 total \n",
|
||||
"1 1 6.25e-07 2.00e+01 total \n",
|
||||
"\n",
|
||||
" mean std. dev. \n",
|
||||
"0 0.923781 0.007714 \n",
|
||||
"1 0.980681 0.002617 "
|
||||
" score mean std. dev. \n",
|
||||
"0 ((scatter / flux) / (total / flux)) 9.24e-01 7.71e-03 \n",
|
||||
"1 ((scatter / flux) / (total / flux)) 9.81e-01 2.62e-03 "
|
||||
]
|
||||
},
|
||||
"execution_count": 25,
|
||||
|
|
@ -1117,7 +1127,8 @@
|
|||
" <tr style=\"text-align: right;\">\n",
|
||||
" <th></th>\n",
|
||||
" <th>cell</th>\n",
|
||||
" <th>energy [MeV]</th>\n",
|
||||
" <th>energy low [MeV]</th>\n",
|
||||
" <th>energy high [MeV]</th>\n",
|
||||
" <th>nuclide</th>\n",
|
||||
" <th>score</th>\n",
|
||||
" <th>mean</th>\n",
|
||||
|
|
@ -1127,34 +1138,36 @@
|
|||
" <tbody>\n",
|
||||
" <tr>\n",
|
||||
" <th>0</th>\n",
|
||||
" <td>1</td>\n",
|
||||
" <td>(0.0e+00 - 6.3e-07)</td>\n",
|
||||
" <td>total</td>\n",
|
||||
" <td>(((absorption / flux) / (total / flux)) + ((sc...</td>\n",
|
||||
" <td>1</td>\n",
|
||||
" <td>0.007741</td>\n",
|
||||
" <td> 1</td>\n",
|
||||
" <td> 0.000000</td>\n",
|
||||
" <td> 0.000001</td>\n",
|
||||
" <td> total</td>\n",
|
||||
" <td> (((absorption / flux) / (total / flux)) + ((sc...</td>\n",
|
||||
" <td> 1</td>\n",
|
||||
" <td> 0.007741</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>1</th>\n",
|
||||
" <td>1</td>\n",
|
||||
" <td>(6.3e-07 - 2.0e+01)</td>\n",
|
||||
" <td>total</td>\n",
|
||||
" <td>(((absorption / flux) / (total / flux)) + ((sc...</td>\n",
|
||||
" <td>1</td>\n",
|
||||
" <td>0.002619</td>\n",
|
||||
" <td> 1</td>\n",
|
||||
" <td> 0.000001</td>\n",
|
||||
" <td> 20.000000</td>\n",
|
||||
" <td> total</td>\n",
|
||||
" <td> (((absorption / flux) / (total / flux)) + ((sc...</td>\n",
|
||||
" <td> 1</td>\n",
|
||||
" <td> 0.002619</td>\n",
|
||||
" </tr>\n",
|
||||
" </tbody>\n",
|
||||
"</table>\n",
|
||||
"</div>"
|
||||
],
|
||||
"text/plain": [
|
||||
" cell energy [MeV] nuclide \\\n",
|
||||
"0 1 (0.0e+00 - 6.3e-07) total \n",
|
||||
"1 1 (6.3e-07 - 2.0e+01) total \n",
|
||||
" cell energy low [MeV] energy high [MeV] nuclide \\\n",
|
||||
"0 1 0.00e+00 6.25e-07 total \n",
|
||||
"1 1 6.25e-07 2.00e+01 total \n",
|
||||
"\n",
|
||||
" score mean std. dev. \n",
|
||||
"0 (((absorption / flux) / (total / flux)) + ((sc... 1 0.007741 \n",
|
||||
"1 (((absorption / flux) / (total / flux)) + ((sc... 1 0.002619 "
|
||||
" score mean std. dev. \n",
|
||||
"0 (((absorption / flux) / (total / flux)) + ((sc... 1.00e+00 7.74e-03 \n",
|
||||
"1 (((absorption / flux) / (total / flux)) + ((sc... 1.00e+00 2.62e-03 "
|
||||
]
|
||||
},
|
||||
"execution_count": 26,
|
||||
|
|
|
|||
|
|
@ -286,7 +286,7 @@
|
|||
"settings_file.batches = batches\n",
|
||||
"settings_file.inactive = inactive\n",
|
||||
"settings_file.particles = particles\n",
|
||||
"settings_file.output = {'tallies': True, 'summary': True}\n",
|
||||
"settings_file.output = {'tallies': True}\n",
|
||||
"bounds = [-0.63, -0.63, -0.63, 0.63, 0.63, 0.63]\n",
|
||||
"settings_file.source = Source(space=Box(\n",
|
||||
" bounds[:3], bounds[3:], only_fissionable=True))\n",
|
||||
|
|
|
|||
|
|
@ -392,7 +392,7 @@
|
|||
"settings_file.batches = batches\n",
|
||||
"settings_file.inactive = inactive\n",
|
||||
"settings_file.particles = particles\n",
|
||||
"settings_file.output = {'tallies': False, 'summary': True}\n",
|
||||
"settings_file.output = {'tallies': False}\n",
|
||||
"source_bounds = [-10.71, -10.71, -10, 10.71, 10.71, 10.]\n",
|
||||
"settings_file.source = Source(Box(\n",
|
||||
" source_bounds[:3], source_bounds[3:], only_fissionable=True))\n",
|
||||
|
|
@ -689,9 +689,8 @@
|
|||
"\n",
|
||||
"# Instantiate the Tally\n",
|
||||
"tally = openmc.Tally(name='mesh tally')\n",
|
||||
"tally.add_filter(mesh_filter)\n",
|
||||
"tally.add_score('fission')\n",
|
||||
"tally.add_score('nu-fission')\n",
|
||||
"tally.filters = [mesh_filter]\n",
|
||||
"tally.scores = ['fission', 'nu-fission']\n",
|
||||
"\n",
|
||||
"# Add mesh and Tally to TalliesFile\n",
|
||||
"tallies_file.add_mesh(mesh)\n",
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -408,9 +408,8 @@
|
|||
"\n",
|
||||
"# Create mesh tally to score flux and fission rate\n",
|
||||
"tally = openmc.Tally(name='flux')\n",
|
||||
"tally.add_filter(mesh_filter)\n",
|
||||
"tally.add_score('flux')\n",
|
||||
"tally.add_score('fission')\n",
|
||||
"tally.filters = [mesh_filter]\n",
|
||||
"tally.scores = ['flux', 'fission']\n",
|
||||
"tallies_file.add_tally(tally)"
|
||||
]
|
||||
},
|
||||
|
|
|
|||
|
|
@ -288,7 +288,7 @@
|
|||
"settings_file.batches = batches\n",
|
||||
"settings_file.inactive = inactive\n",
|
||||
"settings_file.particles = particles\n",
|
||||
"settings_file.output = {'tallies': True, 'summary': True}\n",
|
||||
"settings_file.output = {'tallies': True}\n",
|
||||
"source_bounds = [-0.63, -0.63, -0.63, 0.63, 0.63, 0.63]\n",
|
||||
"settings_file.source = Source(space=Box(\n",
|
||||
" source_bounds[:3], source_bounds[3:]))\n",
|
||||
|
|
@ -366,7 +366,7 @@
|
|||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAPoAAAD6AgMAAAD1grKuAAAABGdBTUEAALGPC/xhBQAAACBjSFJN\nAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAADFBMVEX///9yEhLpgJFNv8Tq\nQYT7AAAAAWJLR0QAiAUdSAAAAAd0SU1FB+ABDg0ADuhPUfUAAALKSURBVGje7dpLcqQwDAbgHHE2\nYeEj+D4cwQucBUfo+3CEXoSp8OhuhF70T4qpKXmdr21LogK2Pj7A8QmNP+HDhw8fPnz48Kf6VH9G\n+66vy+je8k19jnf8C5dXIPv86ms56lPdjvaYbyodx3ze+XLE76cXFiD4zPji99z0/AJ4n1lfvJ6f\nnl0A6x+578efMSg1wPr172/jPO5yFXM+Ef78gdblM+WPHyguP//t1/g6pA0wfln+ho/fwgYYn19C\n/xwDvwHGc9OvC+hs37DTrwuwfWanXxdQTC9Mvyygs3wjTL8uwPJpn/tNDbSGz7T0SBEWw4vLXzbQ\n6b6RoveIoO6TvPxlA63qs7z8ZQPF9F+SH22vbX8OQKf5Rtv+EgDNJ3X58wZaxWd1+fMGiuFvir8b\nvjp8J/tGy/6jAmRvhW8fwL3vVT+o3grfPoB7r/IpALI3tz8FoJN84/NV873hB8UnM3xzANtf8nb4\ndwmg3grfFEDJO8JPE0i9Ff4pAYL3pI8mkHor/HMCeO9JH00g9SafEsh7T/ppARBvp48UwJnelT5S\nACd7O31TAlnvKx9SQCd7B58KgPO+8iMFuPWe9E8F8BveWX7bAjzX9y4//Jve+fhsH6Ctv7n8PTzj\nvY/v9gEOHz58+PBX+6v/f/wPvnd54f3j6venE/yl769Xv7+j3x/o98/V32/o9+fl389Xnx+g5x/o\n+Qt6/oOeP6HnX+j5G3z+h54/ouefV5/foufP6Pk3ev4On/+j9w/o/Qd6/4Le/6D3T/D9V67Y/ZsV\nQBq+s+8f0ftP+P41axXguP9NWgDuu/Cdfv+N3r/D9/9TAID+A7T/Ae2/gPs/0P4TtP8F7r9J3AIO\n9P+g/Udw/9Oygbf7r9D+L7j/DO1/Q/vv4P4/tP8Q7n9E+y/h/k+0/xTuf4X7b+H+X7T/+BPuf3aM\n8OHDhw8fPnz4w/4vzcvgeY10sY0AAAAldEVYdGRhdGU6Y3JlYXRlADIwMTYtMDEtMTRUMDc6MDA6\nMTQtMDY6MDA6WZzHAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE2LTAxLTE0VDA3OjAwOjE0LTA2OjAw\nSwQkewAAAABJRU5ErkJggg==\n",
|
||||
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAPoAAAD6AgMAAAD1grKuAAAABGdBTUEAALGPC/xhBQAAACBjSFJN\nAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAADFBMVEX///9yEhLpgJFNv8Tq\nQYT7AAAAAWJLR0QAiAUdSAAAAAd0SU1FB+ACBxUFD8qiUrQAAALKSURBVGje7dpLcqQwDAbgHHE2\nYeEj+D4cwQucBUfo+3CEXoSp8OhuhF70T4qpKXmdr21LogK2Pj7A8QmNP+HDhw8fPnz48Kf6VH9G\n+66vy+je8k19jnf8C5dXIPv86ms56lPdjvaYbyodx3ze+XLE76cXFiD4zPji99z0/AJ4n1lfvJ6f\nnl0A6x+578efMSg1wPr172/jPO5yFXM+Ef78gdblM+WPHyguP//t1/g6pA0wfln+ho/fwgYYn19C\n/xwDvwHGc9OvC+hs37DTrwuwfWanXxdQTC9Mvyygs3wjTL8uwPJpn/tNDbSGz7T0SBEWw4vLXzbQ\n6b6RoveIoO6TvPxlA63qs7z8ZQPF9F+SH22vbX8OQKf5Rtv+EgDNJ3X58wZaxWd1+fMGiuFvir8b\nvjp8J/tGy/6jAmRvhW8fwL3vVT+o3grfPoB7r/IpALI3tz8FoJN84/NV873hB8UnM3xzANtf8nb4\ndwmg3grfFEDJO8JPE0i9Ff4pAYL3pI8mkHor/HMCeO9JH00g9SafEsh7T/ppARBvp48UwJnelT5S\nACd7O31TAlnvKx9SQCd7B58KgPO+8iMFuPWe9E8F8BveWX7bAjzX9y4//Jve+fhsH6Ctv7n8PTzj\nvY/v9gEOHz58+PBX+6v/f/wPvnd54f3j6venE/yl769Xv7+j3x/o98/V32/o9+fl389Xnx+g5x/o\n+Qt6/oOeP6HnX+j5G3z+h54/ouefV5/foufP6Pk3ev4On/+j9w/o/Qd6/4Le/6D3T/D9V67Y/ZsV\nQBq+s+8f0ftP+P41axXguP9NWgDuu/Cdfv+N3r/D9/9TAID+A7T/Ae2/gPs/0P4TtP8F7r9J3AIO\n9P+g/Udw/9Oygbf7r9D+L7j/DO1/Q/vv4P4/tP8Q7n9E+y/h/k+0/xTuf4X7b+H+X7T/+BPuf3aM\n8OHDhw8fPnz4w/4vzcvgeY10sY0AAAAldEVYdGRhdGU6Y3JlYXRlADIwMTYtMDItMDdUMTY6MDU6\nMTUtMDU6MDAlEzIyAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE2LTAyLTA3VDE2OjA1OjE1LTA1OjAw\nVE6KjgAAAABJRU5ErkJggg==\n",
|
||||
"text/plain": [
|
||||
"<IPython.core.display.Image object>"
|
||||
]
|
||||
|
|
@ -418,29 +418,25 @@
|
|||
"\n",
|
||||
"# Instantiate flux Tally in moderator and fuel\n",
|
||||
"tally = openmc.Tally(name='flux')\n",
|
||||
"tally.add_filter(openmc.Filter(type='cell', bins=[fuel_cell.id, moderator_cell.id]))\n",
|
||||
"tally.add_filter(energy_filter)\n",
|
||||
"tally.add_score('flux')\n",
|
||||
"tally.filters = [openmc.Filter(type='cell', bins=[fuel_cell.id, moderator_cell.id])]\n",
|
||||
"tally.filters.append(energy_filter)\n",
|
||||
"tally.scores = ['flux']\n",
|
||||
"tallies_file.add_tally(tally)\n",
|
||||
"\n",
|
||||
"# Instantiate reaction rate Tally in fuel\n",
|
||||
"tally = openmc.Tally(name='fuel rxn rates')\n",
|
||||
"tally.add_filter(openmc.Filter(type='cell', bins=[fuel_cell.id]))\n",
|
||||
"tally.add_filter(energy_filter)\n",
|
||||
"tally.add_score('nu-fission')\n",
|
||||
"tally.add_score('scatter')\n",
|
||||
"tally.add_nuclide(u238)\n",
|
||||
"tally.add_nuclide(u235)\n",
|
||||
"tally.filters = [openmc.Filter(type='cell', bins=[fuel_cell.id])]\n",
|
||||
"tally.filters.append(energy_filter)\n",
|
||||
"tally.scores = ['nu-fission', 'scatter']\n",
|
||||
"tally.nuclides = [u238, u235]\n",
|
||||
"tallies_file.add_tally(tally)\n",
|
||||
"\n",
|
||||
"# Instantiate reaction rate Tally in moderator\n",
|
||||
"tally = openmc.Tally(name='moderator rxn rates')\n",
|
||||
"tally.add_filter(openmc.Filter(type='cell', bins=[moderator_cell.id]))\n",
|
||||
"tally.add_filter(energy_filter)\n",
|
||||
"tally.add_score('absorption')\n",
|
||||
"tally.add_score('total')\n",
|
||||
"tally.add_nuclide(o16)\n",
|
||||
"tally.add_nuclide(h1)\n",
|
||||
"tally.filters = [openmc.Filter(type='cell', bins=[moderator_cell.id])]\n",
|
||||
"tally.filters.append(energy_filter)\n",
|
||||
"tally.scores = ['absorption', 'total']\n",
|
||||
"tally.nuclides = [o16, h1]\n",
|
||||
"tallies_file.add_tally(tally)"
|
||||
]
|
||||
},
|
||||
|
|
@ -455,8 +451,8 @@
|
|||
"# K-Eigenvalue (infinity) tallies\n",
|
||||
"fiss_rate = openmc.Tally(name='fiss. rate')\n",
|
||||
"abs_rate = openmc.Tally(name='abs. rate')\n",
|
||||
"fiss_rate.add_score('nu-fission')\n",
|
||||
"abs_rate.add_score('absorption')\n",
|
||||
"fiss_rate.scores = ['nu-fission']\n",
|
||||
"abs_rate.scores = ['absorption']\n",
|
||||
"tallies_file.add_tally(fiss_rate)\n",
|
||||
"tallies_file.add_tally(abs_rate)"
|
||||
]
|
||||
|
|
@ -471,8 +467,8 @@
|
|||
"source": [
|
||||
"# Resonance Escape Probability tallies\n",
|
||||
"therm_abs_rate = openmc.Tally(name='therm. abs. rate')\n",
|
||||
"therm_abs_rate.add_score('absorption')\n",
|
||||
"therm_abs_rate.add_filter(openmc.Filter(type='energy', bins=[0., 0.625]))\n",
|
||||
"therm_abs_rate.scores = ['absorption']\n",
|
||||
"therm_abs_rate.filters = [openmc.Filter(type='energy', bins=[0., 0.625e-6])]\n",
|
||||
"tallies_file.add_tally(therm_abs_rate)"
|
||||
]
|
||||
},
|
||||
|
|
@ -486,9 +482,9 @@
|
|||
"source": [
|
||||
"# Thermal Flux Utilization tallies\n",
|
||||
"fuel_therm_abs_rate = openmc.Tally(name='fuel therm. abs. rate')\n",
|
||||
"fuel_therm_abs_rate.add_score('absorption')\n",
|
||||
"fuel_therm_abs_rate.add_filter(openmc.Filter(type='energy', bins=[0., 0.625]))\n",
|
||||
"fuel_therm_abs_rate.add_filter(openmc.Filter(type='cell', bins=[fuel_cell.id]))\n",
|
||||
"fuel_therm_abs_rate.scores = ['absorption']\n",
|
||||
"fuel_therm_abs_rate.filters = [openmc.Filter(type='energy', bins=[0., 0.625e-6]),\n",
|
||||
" openmc.Filter(type='cell', bins=[fuel_cell.id])]\n",
|
||||
"tallies_file.add_tally(fuel_therm_abs_rate)"
|
||||
]
|
||||
},
|
||||
|
|
@ -502,8 +498,8 @@
|
|||
"source": [
|
||||
"# Fast Fission Factor tallies\n",
|
||||
"therm_fiss_rate = openmc.Tally(name='therm. fiss. rate')\n",
|
||||
"therm_fiss_rate.add_score('nu-fission')\n",
|
||||
"therm_fiss_rate.add_filter(openmc.Filter(type='energy', bins=[0., 0.625]))\n",
|
||||
"therm_fiss_rate.scores = ['nu-fission']\n",
|
||||
"therm_fiss_rate.filters = [openmc.Filter(type='energy', bins=[0., 0.625e-6])]\n",
|
||||
"tallies_file.add_tally(therm_fiss_rate)"
|
||||
]
|
||||
},
|
||||
|
|
@ -520,12 +516,10 @@
|
|||
"\n",
|
||||
"# Instantiate flux Tally in moderator and fuel\n",
|
||||
"tally = openmc.Tally(name='need-to-slice')\n",
|
||||
"tally.add_filter(openmc.Filter(type='cell', bins=[fuel_cell.id, moderator_cell.id]))\n",
|
||||
"tally.add_filter(energy_filter)\n",
|
||||
"tally.add_score('nu-fission')\n",
|
||||
"tally.add_score('scatter')\n",
|
||||
"tally.add_nuclide(h1)\n",
|
||||
"tally.add_nuclide(u238)\n",
|
||||
"tally.filters = [openmc.Filter(type='cell', bins=[fuel_cell.id, moderator_cell.id])]\n",
|
||||
"tally.filters.append(energy_filter)\n",
|
||||
"tally.scores = ['nu-fission', 'scatter']\n",
|
||||
"tally.nuclides = [h1, u238]\n",
|
||||
"tallies_file.add_tally(tally)"
|
||||
]
|
||||
},
|
||||
|
|
@ -533,7 +527,7 @@
|
|||
"cell_type": "code",
|
||||
"execution_count": 22,
|
||||
"metadata": {
|
||||
"collapsed": true
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
|
|
@ -576,8 +570,8 @@
|
|||
" Copyright: 2011-2015 Massachusetts Institute of Technology\n",
|
||||
" License: http://mit-crpg.github.io/openmc/license.html\n",
|
||||
" Version: 0.7.1\n",
|
||||
" Git SHA1: ea9fb637f63f9374c7436456141afa850b84acf9\n",
|
||||
" Date/Time: 2016-01-14 07:00:14\n",
|
||||
" Git SHA1: b9efc990c7eb58f4a41524d59ae73396c9929436\n",
|
||||
" Date/Time: 2016-02-23 10:52:44\n",
|
||||
"\n",
|
||||
" ===========================================================================\n",
|
||||
" ========================> INITIALIZATION <=========================\n",
|
||||
|
|
@ -633,20 +627,20 @@
|
|||
"\n",
|
||||
" =======================> TIMING STATISTICS <=======================\n",
|
||||
"\n",
|
||||
" Total time for initialization = 1.2510E+00 seconds\n",
|
||||
" Reading cross sections = 9.7600E-01 seconds\n",
|
||||
" Total time in simulation = 1.5844E+01 seconds\n",
|
||||
" Time in transport only = 1.5834E+01 seconds\n",
|
||||
" Time in inactive batches = 2.2840E+00 seconds\n",
|
||||
" Time in active batches = 1.3560E+01 seconds\n",
|
||||
" Time synchronizing fission bank = 3.0000E-03 seconds\n",
|
||||
" Sampling source sites = 2.0000E-03 seconds\n",
|
||||
" Total time for initialization = 8.4700E-01 seconds\n",
|
||||
" Reading cross sections = 5.8300E-01 seconds\n",
|
||||
" Total time in simulation = 1.6037E+01 seconds\n",
|
||||
" Time in transport only = 1.6026E+01 seconds\n",
|
||||
" Time in inactive batches = 2.3070E+00 seconds\n",
|
||||
" Time in active batches = 1.3730E+01 seconds\n",
|
||||
" Time synchronizing fission bank = 5.0000E-03 seconds\n",
|
||||
" Sampling source sites = 4.0000E-03 seconds\n",
|
||||
" SEND/RECV source sites = 1.0000E-03 seconds\n",
|
||||
" Time accumulating tallies = 0.0000E+00 seconds\n",
|
||||
" Total time for finalization = 1.0000E-03 seconds\n",
|
||||
" Total time elapsed = 1.7110E+01 seconds\n",
|
||||
" Calculation Rate (inactive) = 5472.85 neutrons/second\n",
|
||||
" Calculation Rate (active) = 2765.49 neutrons/second\n",
|
||||
" Total time for finalization = 3.0000E-03 seconds\n",
|
||||
" Total time elapsed = 1.6899E+01 seconds\n",
|
||||
" Calculation Rate (inactive) = 5418.29 neutrons/second\n",
|
||||
" Calculation Rate (active) = 2731.25 neutrons/second\n",
|
||||
"\n",
|
||||
" ============================> RESULTS <============================\n",
|
||||
"\n",
|
||||
|
|
@ -768,8 +762,8 @@
|
|||
"</div>"
|
||||
],
|
||||
"text/plain": [
|
||||
" nuclide score mean std. dev.\n",
|
||||
"0 total (nu-fission / absorption) 1.040166 0.009069"
|
||||
" nuclide score mean std. dev.\n",
|
||||
"0 total (nu-fission / absorption) 1.04e+00 9.07e-03"
|
||||
]
|
||||
},
|
||||
"execution_count": 26,
|
||||
|
|
@ -809,7 +803,8 @@
|
|||
" <thead>\n",
|
||||
" <tr style=\"text-align: right;\">\n",
|
||||
" <th></th>\n",
|
||||
" <th>energy [MeV]</th>\n",
|
||||
" <th>energy low [MeV]</th>\n",
|
||||
" <th>energy high [MeV]</th>\n",
|
||||
" <th>nuclide</th>\n",
|
||||
" <th>score</th>\n",
|
||||
" <th>mean</th>\n",
|
||||
|
|
@ -819,19 +814,20 @@
|
|||
" <tbody>\n",
|
||||
" <tr>\n",
|
||||
" <th>0</th>\n",
|
||||
" <td>(0.0e+00 - 6.2e-01)</td>\n",
|
||||
" <td>0</td>\n",
|
||||
" <td>0.000001</td>\n",
|
||||
" <td>total</td>\n",
|
||||
" <td>absorption</td>\n",
|
||||
" <td>0.95938</td>\n",
|
||||
" <td>0.008187</td>\n",
|
||||
" <td>0.694707</td>\n",
|
||||
" <td>0.006699</td>\n",
|
||||
" </tr>\n",
|
||||
" </tbody>\n",
|
||||
"</table>\n",
|
||||
"</div>"
|
||||
],
|
||||
"text/plain": [
|
||||
" energy [MeV] nuclide score mean std. dev.\n",
|
||||
"0 (0.0e+00 - 6.2e-01) total absorption 0.95938 0.008187"
|
||||
" energy low [MeV] energy high [MeV] nuclide score mean std. dev.\n",
|
||||
"0 0.00e+00 6.25e-07 total absorption 6.95e-01 6.70e-03"
|
||||
]
|
||||
},
|
||||
"execution_count": 27,
|
||||
|
|
@ -869,7 +865,8 @@
|
|||
" <thead>\n",
|
||||
" <tr style=\"text-align: right;\">\n",
|
||||
" <th></th>\n",
|
||||
" <th>energy [MeV]</th>\n",
|
||||
" <th>energy low [MeV]</th>\n",
|
||||
" <th>energy high [MeV]</th>\n",
|
||||
" <th>nuclide</th>\n",
|
||||
" <th>score</th>\n",
|
||||
" <th>mean</th>\n",
|
||||
|
|
@ -879,19 +876,20 @@
|
|||
" <tbody>\n",
|
||||
" <tr>\n",
|
||||
" <th>0</th>\n",
|
||||
" <td>(0.0e+00 - 6.2e-01)</td>\n",
|
||||
" <td>0</td>\n",
|
||||
" <td>0.000001</td>\n",
|
||||
" <td>total</td>\n",
|
||||
" <td>nu-fission</td>\n",
|
||||
" <td>1.090899</td>\n",
|
||||
" <td>0.010602</td>\n",
|
||||
" <td>1.201216</td>\n",
|
||||
" <td>0.012288</td>\n",
|
||||
" </tr>\n",
|
||||
" </tbody>\n",
|
||||
"</table>\n",
|
||||
"</div>"
|
||||
],
|
||||
"text/plain": [
|
||||
" energy [MeV] nuclide score mean std. dev.\n",
|
||||
"0 (0.0e+00 - 6.2e-01) total nu-fission 1.090899 0.010602"
|
||||
" energy low [MeV] energy high [MeV] nuclide score mean std. dev.\n",
|
||||
"0 0.00e+00 6.25e-07 total nu-fission 1.20e+00 1.23e-02"
|
||||
]
|
||||
},
|
||||
"execution_count": 28,
|
||||
|
|
@ -930,7 +928,8 @@
|
|||
" <thead>\n",
|
||||
" <tr style=\"text-align: right;\">\n",
|
||||
" <th></th>\n",
|
||||
" <th>energy [MeV]</th>\n",
|
||||
" <th>energy low [MeV]</th>\n",
|
||||
" <th>energy high [MeV]</th>\n",
|
||||
" <th>cell</th>\n",
|
||||
" <th>nuclide</th>\n",
|
||||
" <th>score</th>\n",
|
||||
|
|
@ -941,20 +940,24 @@
|
|||
" <tbody>\n",
|
||||
" <tr>\n",
|
||||
" <th>0</th>\n",
|
||||
" <td>(0.0e+00 - 6.2e-01)</td>\n",
|
||||
" <td>0</td>\n",
|
||||
" <td>0.000001</td>\n",
|
||||
" <td>10000</td>\n",
|
||||
" <td>total</td>\n",
|
||||
" <td>absorption</td>\n",
|
||||
" <td>0.803413</td>\n",
|
||||
" <td>0.007031</td>\n",
|
||||
" <td>0.74925</td>\n",
|
||||
" <td>0.008257</td>\n",
|
||||
" </tr>\n",
|
||||
" </tbody>\n",
|
||||
"</table>\n",
|
||||
"</div>"
|
||||
],
|
||||
"text/plain": [
|
||||
" energy [MeV] cell nuclide score mean std. dev.\n",
|
||||
"0 (0.0e+00 - 6.2e-01) 10000 total absorption 0.803413 0.007031"
|
||||
" energy low [MeV] energy high [MeV] cell nuclide score mean \\\n",
|
||||
"0 0.00e+00 6.25e-07 10000 total absorption 7.49e-01 \n",
|
||||
"\n",
|
||||
" std. dev. \n",
|
||||
"0 8.26e-03 "
|
||||
]
|
||||
},
|
||||
"execution_count": 29,
|
||||
|
|
@ -991,7 +994,8 @@
|
|||
" <thead>\n",
|
||||
" <tr style=\"text-align: right;\">\n",
|
||||
" <th></th>\n",
|
||||
" <th>energy [MeV]</th>\n",
|
||||
" <th>energy low [MeV]</th>\n",
|
||||
" <th>energy high [MeV]</th>\n",
|
||||
" <th>cell</th>\n",
|
||||
" <th>nuclide</th>\n",
|
||||
" <th>score</th>\n",
|
||||
|
|
@ -1002,23 +1006,24 @@
|
|||
" <tbody>\n",
|
||||
" <tr>\n",
|
||||
" <th>0</th>\n",
|
||||
" <td>(0.0e+00 - 6.2e-01)</td>\n",
|
||||
" <td>0</td>\n",
|
||||
" <td>0.000001</td>\n",
|
||||
" <td>10000</td>\n",
|
||||
" <td>total</td>\n",
|
||||
" <td>(nu-fission / absorption)</td>\n",
|
||||
" <td>1.237053</td>\n",
|
||||
" <td>0.011765</td>\n",
|
||||
" <td>1.663616</td>\n",
|
||||
" <td>0.018624</td>\n",
|
||||
" </tr>\n",
|
||||
" </tbody>\n",
|
||||
"</table>\n",
|
||||
"</div>"
|
||||
],
|
||||
"text/plain": [
|
||||
" energy [MeV] cell nuclide score mean \\\n",
|
||||
"0 (0.0e+00 - 6.2e-01) 10000 total (nu-fission / absorption) 1.237053 \n",
|
||||
" energy low [MeV] energy high [MeV] cell nuclide \\\n",
|
||||
"0 0.00e+00 6.25e-07 10000 total \n",
|
||||
"\n",
|
||||
" std. dev. \n",
|
||||
"0 0.011765 "
|
||||
" score mean std. dev. \n",
|
||||
"0 (nu-fission / absorption) 1.66e+00 1.86e-02 "
|
||||
]
|
||||
},
|
||||
"execution_count": 30,
|
||||
|
|
@ -1054,7 +1059,8 @@
|
|||
" <thead>\n",
|
||||
" <tr style=\"text-align: right;\">\n",
|
||||
" <th></th>\n",
|
||||
" <th>energy [MeV]</th>\n",
|
||||
" <th>energy low [MeV]</th>\n",
|
||||
" <th>energy high [MeV]</th>\n",
|
||||
" <th>cell</th>\n",
|
||||
" <th>nuclide</th>\n",
|
||||
" <th>score</th>\n",
|
||||
|
|
@ -1065,23 +1071,24 @@
|
|||
" <tbody>\n",
|
||||
" <tr>\n",
|
||||
" <th>0</th>\n",
|
||||
" <td>(0.0e+00 - 6.2e-01)</td>\n",
|
||||
" <td>0</td>\n",
|
||||
" <td>0.000001</td>\n",
|
||||
" <td>10000</td>\n",
|
||||
" <td>total</td>\n",
|
||||
" <td>(((absorption * nu-fission) * absorption) * (n...</td>\n",
|
||||
" <td>1.040166</td>\n",
|
||||
" <td>0.019018</td>\n",
|
||||
" <td>0.021928</td>\n",
|
||||
" </tr>\n",
|
||||
" </tbody>\n",
|
||||
"</table>\n",
|
||||
"</div>"
|
||||
],
|
||||
"text/plain": [
|
||||
" energy [MeV] cell nuclide \\\n",
|
||||
"0 (0.0e+00 - 6.2e-01) 10000 total \n",
|
||||
" energy low [MeV] energy high [MeV] cell nuclide \\\n",
|
||||
"0 0.00e+00 6.25e-07 10000 total \n",
|
||||
"\n",
|
||||
" score mean std. dev. \n",
|
||||
"0 (((absorption * nu-fission) * absorption) * (n... 1.040166 0.019018 "
|
||||
" score mean std. dev. \n",
|
||||
"0 (((absorption * nu-fission) * absorption) * (n... 1.04e+00 2.19e-02 "
|
||||
]
|
||||
},
|
||||
"execution_count": 31,
|
||||
|
|
@ -1135,7 +1142,8 @@
|
|||
" <tr style=\"text-align: right;\">\n",
|
||||
" <th></th>\n",
|
||||
" <th>cell</th>\n",
|
||||
" <th>energy [MeV]</th>\n",
|
||||
" <th>energy low [MeV]</th>\n",
|
||||
" <th>energy high [MeV]</th>\n",
|
||||
" <th>nuclide</th>\n",
|
||||
" <th>score</th>\n",
|
||||
" <th>mean</th>\n",
|
||||
|
|
@ -1146,7 +1154,8 @@
|
|||
" <tr>\n",
|
||||
" <th>0</th>\n",
|
||||
" <td>10000</td>\n",
|
||||
" <td>(0.0e+00 - 6.3e-07)</td>\n",
|
||||
" <td>0.000000</td>\n",
|
||||
" <td>0.000001</td>\n",
|
||||
" <td>(U-238 / total)</td>\n",
|
||||
" <td>(nu-fission / flux)</td>\n",
|
||||
" <td>0.000001</td>\n",
|
||||
|
|
@ -1155,7 +1164,8 @@
|
|||
" <tr>\n",
|
||||
" <th>1</th>\n",
|
||||
" <td>10000</td>\n",
|
||||
" <td>(0.0e+00 - 6.3e-07)</td>\n",
|
||||
" <td>0.000000</td>\n",
|
||||
" <td>0.000001</td>\n",
|
||||
" <td>(U-238 / total)</td>\n",
|
||||
" <td>(scatter / flux)</td>\n",
|
||||
" <td>0.209989</td>\n",
|
||||
|
|
@ -1164,7 +1174,8 @@
|
|||
" <tr>\n",
|
||||
" <th>2</th>\n",
|
||||
" <td>10000</td>\n",
|
||||
" <td>(0.0e+00 - 6.3e-07)</td>\n",
|
||||
" <td>0.000000</td>\n",
|
||||
" <td>0.000001</td>\n",
|
||||
" <td>(U-235 / total)</td>\n",
|
||||
" <td>(nu-fission / flux)</td>\n",
|
||||
" <td>0.356420</td>\n",
|
||||
|
|
@ -1173,7 +1184,8 @@
|
|||
" <tr>\n",
|
||||
" <th>3</th>\n",
|
||||
" <td>10000</td>\n",
|
||||
" <td>(0.0e+00 - 6.3e-07)</td>\n",
|
||||
" <td>0.000000</td>\n",
|
||||
" <td>0.000001</td>\n",
|
||||
" <td>(U-235 / total)</td>\n",
|
||||
" <td>(scatter / flux)</td>\n",
|
||||
" <td>0.005555</td>\n",
|
||||
|
|
@ -1182,7 +1194,8 @@
|
|||
" <tr>\n",
|
||||
" <th>4</th>\n",
|
||||
" <td>10000</td>\n",
|
||||
" <td>(6.3e-07 - 2.0e+01)</td>\n",
|
||||
" <td>0.000001</td>\n",
|
||||
" <td>20.000000</td>\n",
|
||||
" <td>(U-238 / total)</td>\n",
|
||||
" <td>(nu-fission / flux)</td>\n",
|
||||
" <td>0.007155</td>\n",
|
||||
|
|
@ -1191,7 +1204,8 @@
|
|||
" <tr>\n",
|
||||
" <th>5</th>\n",
|
||||
" <td>10000</td>\n",
|
||||
" <td>(6.3e-07 - 2.0e+01)</td>\n",
|
||||
" <td>0.000001</td>\n",
|
||||
" <td>20.000000</td>\n",
|
||||
" <td>(U-238 / total)</td>\n",
|
||||
" <td>(scatter / flux)</td>\n",
|
||||
" <td>0.227770</td>\n",
|
||||
|
|
@ -1200,7 +1214,8 @@
|
|||
" <tr>\n",
|
||||
" <th>6</th>\n",
|
||||
" <td>10000</td>\n",
|
||||
" <td>(6.3e-07 - 2.0e+01)</td>\n",
|
||||
" <td>0.000001</td>\n",
|
||||
" <td>20.000000</td>\n",
|
||||
" <td>(U-235 / total)</td>\n",
|
||||
" <td>(nu-fission / flux)</td>\n",
|
||||
" <td>0.008067</td>\n",
|
||||
|
|
@ -1209,7 +1224,8 @@
|
|||
" <tr>\n",
|
||||
" <th>7</th>\n",
|
||||
" <td>10000</td>\n",
|
||||
" <td>(6.3e-07 - 2.0e+01)</td>\n",
|
||||
" <td>0.000001</td>\n",
|
||||
" <td>20.000000</td>\n",
|
||||
" <td>(U-235 / total)</td>\n",
|
||||
" <td>(scatter / flux)</td>\n",
|
||||
" <td>0.003367</td>\n",
|
||||
|
|
@ -1220,25 +1236,25 @@
|
|||
"</div>"
|
||||
],
|
||||
"text/plain": [
|
||||
" cell energy [MeV] nuclide score mean \\\n",
|
||||
"0 10000 (0.0e+00 - 6.3e-07) (U-238 / total) (nu-fission / flux) 0.000001 \n",
|
||||
"1 10000 (0.0e+00 - 6.3e-07) (U-238 / total) (scatter / flux) 0.209989 \n",
|
||||
"2 10000 (0.0e+00 - 6.3e-07) (U-235 / total) (nu-fission / flux) 0.356420 \n",
|
||||
"3 10000 (0.0e+00 - 6.3e-07) (U-235 / total) (scatter / flux) 0.005555 \n",
|
||||
"4 10000 (6.3e-07 - 2.0e+01) (U-238 / total) (nu-fission / flux) 0.007155 \n",
|
||||
"5 10000 (6.3e-07 - 2.0e+01) (U-238 / total) (scatter / flux) 0.227770 \n",
|
||||
"6 10000 (6.3e-07 - 2.0e+01) (U-235 / total) (nu-fission / flux) 0.008067 \n",
|
||||
"7 10000 (6.3e-07 - 2.0e+01) (U-235 / total) (scatter / flux) 0.003367 \n",
|
||||
" cell energy low [MeV] energy high [MeV] nuclide \\\n",
|
||||
"0 10000 0.00e+00 6.25e-07 (U-238 / total) \n",
|
||||
"1 10000 0.00e+00 6.25e-07 (U-238 / total) \n",
|
||||
"2 10000 0.00e+00 6.25e-07 (U-235 / total) \n",
|
||||
"3 10000 0.00e+00 6.25e-07 (U-235 / total) \n",
|
||||
"4 10000 6.25e-07 2.00e+01 (U-238 / total) \n",
|
||||
"5 10000 6.25e-07 2.00e+01 (U-238 / total) \n",
|
||||
"6 10000 6.25e-07 2.00e+01 (U-235 / total) \n",
|
||||
"7 10000 6.25e-07 2.00e+01 (U-235 / total) \n",
|
||||
"\n",
|
||||
" std. dev. \n",
|
||||
"0 7.377419e-09 \n",
|
||||
"1 2.303838e-03 \n",
|
||||
"2 3.951669e-03 \n",
|
||||
"3 6.101004e-05 \n",
|
||||
"4 8.053460e-05 \n",
|
||||
"5 1.079289e-03 \n",
|
||||
"6 5.254797e-05 \n",
|
||||
"7 1.647058e-05 "
|
||||
" score mean std. dev. \n",
|
||||
"0 (nu-fission / flux) 6.66e-07 7.38e-09 \n",
|
||||
"1 (scatter / flux) 2.10e-01 2.30e-03 \n",
|
||||
"2 (nu-fission / flux) 3.56e-01 3.95e-03 \n",
|
||||
"3 (scatter / flux) 5.56e-03 6.10e-05 \n",
|
||||
"4 (nu-fission / flux) 7.15e-03 8.05e-05 \n",
|
||||
"5 (scatter / flux) 2.28e-01 1.08e-03 \n",
|
||||
"6 (nu-fission / flux) 8.07e-03 5.25e-05 \n",
|
||||
"7 (scatter / flux) 3.37e-03 1.65e-05 "
|
||||
]
|
||||
},
|
||||
"execution_count": 33,
|
||||
|
|
@ -1361,7 +1377,8 @@
|
|||
" <tr style=\"text-align: right;\">\n",
|
||||
" <th></th>\n",
|
||||
" <th>cell</th>\n",
|
||||
" <th>energy [MeV]</th>\n",
|
||||
" <th>energy low [MeV]</th>\n",
|
||||
" <th>energy high [MeV]</th>\n",
|
||||
" <th>nuclide</th>\n",
|
||||
" <th>score</th>\n",
|
||||
" <th>mean</th>\n",
|
||||
|
|
@ -1372,7 +1389,8 @@
|
|||
" <tr>\n",
|
||||
" <th>0</th>\n",
|
||||
" <td>10000</td>\n",
|
||||
" <td>(0.0e+00 - 6.3e-07)</td>\n",
|
||||
" <td>0.000000</td>\n",
|
||||
" <td>0.000001</td>\n",
|
||||
" <td>U-238</td>\n",
|
||||
" <td>nu-fission</td>\n",
|
||||
" <td>0.000002</td>\n",
|
||||
|
|
@ -1381,7 +1399,8 @@
|
|||
" <tr>\n",
|
||||
" <th>1</th>\n",
|
||||
" <td>10000</td>\n",
|
||||
" <td>(0.0e+00 - 6.3e-07)</td>\n",
|
||||
" <td>0.000000</td>\n",
|
||||
" <td>0.000001</td>\n",
|
||||
" <td>U-235</td>\n",
|
||||
" <td>nu-fission</td>\n",
|
||||
" <td>0.868553</td>\n",
|
||||
|
|
@ -1390,7 +1409,8 @@
|
|||
" <tr>\n",
|
||||
" <th>2</th>\n",
|
||||
" <td>10000</td>\n",
|
||||
" <td>(6.3e-07 - 2.0e+01)</td>\n",
|
||||
" <td>0.000001</td>\n",
|
||||
" <td>20.000000</td>\n",
|
||||
" <td>U-238</td>\n",
|
||||
" <td>nu-fission</td>\n",
|
||||
" <td>0.082149</td>\n",
|
||||
|
|
@ -1399,7 +1419,8 @@
|
|||
" <tr>\n",
|
||||
" <th>3</th>\n",
|
||||
" <td>10000</td>\n",
|
||||
" <td>(6.3e-07 - 2.0e+01)</td>\n",
|
||||
" <td>0.000001</td>\n",
|
||||
" <td>20.000000</td>\n",
|
||||
" <td>U-235</td>\n",
|
||||
" <td>nu-fission</td>\n",
|
||||
" <td>0.092618</td>\n",
|
||||
|
|
@ -1410,11 +1431,17 @@
|
|||
"</div>"
|
||||
],
|
||||
"text/plain": [
|
||||
" cell energy [MeV] nuclide score mean std. dev.\n",
|
||||
"0 10000 (0.0e+00 - 6.3e-07) U-238 nu-fission 0.000002 1.283958e-08\n",
|
||||
"1 10000 (0.0e+00 - 6.3e-07) U-235 nu-fission 0.868553 6.880390e-03\n",
|
||||
"2 10000 (6.3e-07 - 2.0e+01) U-238 nu-fission 0.082149 8.837250e-04\n",
|
||||
"3 10000 (6.3e-07 - 2.0e+01) U-235 nu-fission 0.092618 5.195308e-04"
|
||||
" cell energy low [MeV] energy high [MeV] nuclide score mean \\\n",
|
||||
"0 10000 0.00e+00 6.25e-07 U-238 nu-fission 1.62e-06 \n",
|
||||
"1 10000 0.00e+00 6.25e-07 U-235 nu-fission 8.69e-01 \n",
|
||||
"2 10000 6.25e-07 2.00e+01 U-238 nu-fission 8.21e-02 \n",
|
||||
"3 10000 6.25e-07 2.00e+01 U-235 nu-fission 9.26e-02 \n",
|
||||
"\n",
|
||||
" std. dev. \n",
|
||||
"0 1.28e-08 \n",
|
||||
"1 6.88e-03 \n",
|
||||
"2 8.84e-04 \n",
|
||||
"3 5.20e-04 "
|
||||
]
|
||||
},
|
||||
"execution_count": 37,
|
||||
|
|
@ -1444,7 +1471,8 @@
|
|||
" <tr style=\"text-align: right;\">\n",
|
||||
" <th></th>\n",
|
||||
" <th>cell</th>\n",
|
||||
" <th>energy [MeV]</th>\n",
|
||||
" <th>energy low [MeV]</th>\n",
|
||||
" <th>energy high [MeV]</th>\n",
|
||||
" <th>nuclide</th>\n",
|
||||
" <th>score</th>\n",
|
||||
" <th>mean</th>\n",
|
||||
|
|
@ -1455,7 +1483,8 @@
|
|||
" <tr>\n",
|
||||
" <th>0</th>\n",
|
||||
" <td>10002</td>\n",
|
||||
" <td>(1.0e-08 - 1.1e-07)</td>\n",
|
||||
" <td>1.000000e-08</td>\n",
|
||||
" <td>0.000000</td>\n",
|
||||
" <td>H-1</td>\n",
|
||||
" <td>scatter</td>\n",
|
||||
" <td>4.619398</td>\n",
|
||||
|
|
@ -1464,7 +1493,8 @@
|
|||
" <tr>\n",
|
||||
" <th>1</th>\n",
|
||||
" <td>10002</td>\n",
|
||||
" <td>(1.1e-07 - 1.2e-06)</td>\n",
|
||||
" <td>1.080060e-07</td>\n",
|
||||
" <td>0.000001</td>\n",
|
||||
" <td>H-1</td>\n",
|
||||
" <td>scatter</td>\n",
|
||||
" <td>2.030757</td>\n",
|
||||
|
|
@ -1473,7 +1503,8 @@
|
|||
" <tr>\n",
|
||||
" <th>2</th>\n",
|
||||
" <td>10002</td>\n",
|
||||
" <td>(1.2e-06 - 1.3e-05)</td>\n",
|
||||
" <td>1.166529e-06</td>\n",
|
||||
" <td>0.000013</td>\n",
|
||||
" <td>H-1</td>\n",
|
||||
" <td>scatter</td>\n",
|
||||
" <td>1.658488</td>\n",
|
||||
|
|
@ -1482,7 +1513,8 @@
|
|||
" <tr>\n",
|
||||
" <th>3</th>\n",
|
||||
" <td>10002</td>\n",
|
||||
" <td>(1.3e-05 - 1.4e-04)</td>\n",
|
||||
" <td>1.259921e-05</td>\n",
|
||||
" <td>0.000136</td>\n",
|
||||
" <td>H-1</td>\n",
|
||||
" <td>scatter</td>\n",
|
||||
" <td>1.853002</td>\n",
|
||||
|
|
@ -1491,7 +1523,8 @@
|
|||
" <tr>\n",
|
||||
" <th>4</th>\n",
|
||||
" <td>10002</td>\n",
|
||||
" <td>(1.4e-04 - 1.5e-03)</td>\n",
|
||||
" <td>1.360790e-04</td>\n",
|
||||
" <td>0.001470</td>\n",
|
||||
" <td>H-1</td>\n",
|
||||
" <td>scatter</td>\n",
|
||||
" <td>2.050773</td>\n",
|
||||
|
|
@ -1500,7 +1533,8 @@
|
|||
" <tr>\n",
|
||||
" <th>5</th>\n",
|
||||
" <td>10002</td>\n",
|
||||
" <td>(1.5e-03 - 1.6e-02)</td>\n",
|
||||
" <td>1.469734e-03</td>\n",
|
||||
" <td>0.015874</td>\n",
|
||||
" <td>H-1</td>\n",
|
||||
" <td>scatter</td>\n",
|
||||
" <td>2.131759</td>\n",
|
||||
|
|
@ -1509,7 +1543,8 @@
|
|||
" <tr>\n",
|
||||
" <th>6</th>\n",
|
||||
" <td>10002</td>\n",
|
||||
" <td>(1.6e-02 - 1.7e-01)</td>\n",
|
||||
" <td>1.587401e-02</td>\n",
|
||||
" <td>0.171449</td>\n",
|
||||
" <td>H-1</td>\n",
|
||||
" <td>scatter</td>\n",
|
||||
" <td>2.213710</td>\n",
|
||||
|
|
@ -1518,7 +1553,8 @@
|
|||
" <tr>\n",
|
||||
" <th>7</th>\n",
|
||||
" <td>10002</td>\n",
|
||||
" <td>(1.7e-01 - 1.9e+00)</td>\n",
|
||||
" <td>1.714488e-01</td>\n",
|
||||
" <td>1.851749</td>\n",
|
||||
" <td>H-1</td>\n",
|
||||
" <td>scatter</td>\n",
|
||||
" <td>2.011925</td>\n",
|
||||
|
|
@ -1527,7 +1563,8 @@
|
|||
" <tr>\n",
|
||||
" <th>8</th>\n",
|
||||
" <td>10002</td>\n",
|
||||
" <td>(1.9e+00 - 2.0e+01)</td>\n",
|
||||
" <td>1.851749e+00</td>\n",
|
||||
" <td>20.000000</td>\n",
|
||||
" <td>H-1</td>\n",
|
||||
" <td>scatter</td>\n",
|
||||
" <td>0.371280</td>\n",
|
||||
|
|
@ -1538,16 +1575,27 @@
|
|||
"</div>"
|
||||
],
|
||||
"text/plain": [
|
||||
" cell energy [MeV] nuclide score mean std. dev.\n",
|
||||
"0 10002 (1.0e-08 - 1.1e-07) H-1 scatter 4.619398 0.040124\n",
|
||||
"1 10002 (1.1e-07 - 1.2e-06) H-1 scatter 2.030757 0.011239\n",
|
||||
"2 10002 (1.2e-06 - 1.3e-05) H-1 scatter 1.658488 0.009777\n",
|
||||
"3 10002 (1.3e-05 - 1.4e-04) H-1 scatter 1.853002 0.007378\n",
|
||||
"4 10002 (1.4e-04 - 1.5e-03) H-1 scatter 2.050773 0.012484\n",
|
||||
"5 10002 (1.5e-03 - 1.6e-02) H-1 scatter 2.131759 0.007821\n",
|
||||
"6 10002 (1.6e-02 - 1.7e-01) H-1 scatter 2.213710 0.015159\n",
|
||||
"7 10002 (1.7e-01 - 1.9e+00) H-1 scatter 2.011925 0.009406\n",
|
||||
"8 10002 (1.9e+00 - 2.0e+01) H-1 scatter 0.371280 0.003949"
|
||||
" cell energy low [MeV] energy high [MeV] nuclide score mean \\\n",
|
||||
"0 10002 1.00e-08 1.08e-07 H-1 scatter 4.62e+00 \n",
|
||||
"1 10002 1.08e-07 1.17e-06 H-1 scatter 2.03e+00 \n",
|
||||
"2 10002 1.17e-06 1.26e-05 H-1 scatter 1.66e+00 \n",
|
||||
"3 10002 1.26e-05 1.36e-04 H-1 scatter 1.85e+00 \n",
|
||||
"4 10002 1.36e-04 1.47e-03 H-1 scatter 2.05e+00 \n",
|
||||
"5 10002 1.47e-03 1.59e-02 H-1 scatter 2.13e+00 \n",
|
||||
"6 10002 1.59e-02 1.71e-01 H-1 scatter 2.21e+00 \n",
|
||||
"7 10002 1.71e-01 1.85e+00 H-1 scatter 2.01e+00 \n",
|
||||
"8 10002 1.85e+00 2.00e+01 H-1 scatter 3.71e-01 \n",
|
||||
"\n",
|
||||
" std. dev. \n",
|
||||
"0 4.01e-02 \n",
|
||||
"1 1.12e-02 \n",
|
||||
"2 9.78e-03 \n",
|
||||
"3 7.38e-03 \n",
|
||||
"4 1.25e-02 \n",
|
||||
"5 7.82e-03 \n",
|
||||
"6 1.52e-02 \n",
|
||||
"7 9.41e-03 \n",
|
||||
"8 3.95e-03 "
|
||||
]
|
||||
},
|
||||
"execution_count": 38,
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ In a nutshell, OpenMC simulates neutrons moving around randomly in a `nuclear
|
|||
reactor`_ (or other fissile system). This is what's known as `Monte Carlo`_
|
||||
simulation. Neutrons are important in nuclear reactors because they are the
|
||||
particles that induce `fission`_ in uranium and other nuclides. Knowing the
|
||||
behavior of neutrons allows you to figure out how often and where fission
|
||||
behavior of neutrons allows you to determine how often and where fission
|
||||
occurs. The amount of energy released is then directly proportional to the
|
||||
fission reaction rate since most heat is produced by fission. By simulating many
|
||||
neutrons (millions or billions), it is possible to determine the average
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ essential aspects of using OpenMC to perform simulations.
|
|||
beginners
|
||||
install
|
||||
input
|
||||
mgxs_library
|
||||
output/index
|
||||
processing
|
||||
troubleshoot
|
||||
|
|
|
|||
|
|
@ -112,9 +112,11 @@ standard deviation.
|
|||
|
||||
The ``<cross_sections>`` element has no attributes and simply indicates the path
|
||||
to an XML cross section listing file (usually named cross_sections.xml). If this
|
||||
element is absent from the settings.xml file, the :envvar:`CROSS_SECTIONS`
|
||||
environment variable will be used to find the path to the XML cross section
|
||||
listing.
|
||||
element is absent from the settings.xml file, the
|
||||
:envvar:`OPENMC_CROSS_SECTIONS` environment variable will be used to find the
|
||||
path to the XML cross section listing when in continuous-energy mode, and the
|
||||
:envvar:`OPENMC_MG_CROSS_SECTIONS` environment variable will be used in
|
||||
multi-group mode.
|
||||
|
||||
``<cutoff>`` Element
|
||||
--------------------
|
||||
|
|
@ -212,8 +214,21 @@ cross section values between.
|
|||
|
||||
*Default*: logarithm
|
||||
|
||||
.. note:: This element is not used in the multi-group :ref:`energy_mode`.
|
||||
|
||||
.. _LA-UR-14-24530: https://laws.lanl.gov/vhosts/mcnp.lanl.gov/pdf_files/la-ur-14-24530.pdf
|
||||
|
||||
.. _energy_mode:
|
||||
|
||||
``<energy_mode>`` Element
|
||||
-------------------------
|
||||
|
||||
The ``<energy_mode>`` element tells OpenMC if the run-mode should be
|
||||
continuous-energy or multi-group. Options for entry are: ``continuous-energy``
|
||||
or ``multi-group``.
|
||||
|
||||
*Default*: continuous-energy
|
||||
|
||||
``<entropy>`` Element
|
||||
---------------------
|
||||
|
||||
|
|
@ -264,6 +279,8 @@ based on the recommended value in LA-UR-14-24530_.
|
|||
|
||||
*Default*: 8000
|
||||
|
||||
.. note:: This element is not used in the multi-group :ref:`energy_mode`.
|
||||
|
||||
``<multipole_library>`` Element
|
||||
-------------------------------
|
||||
|
||||
|
|
@ -276,6 +293,19 @@ cross sections. If this element is absent from the settings.xml file, the
|
|||
.. note:: The <use_windowed_multipole> element must also be set to "True"
|
||||
for windowed multipole functionality.
|
||||
|
||||
``<max_order>`` Element
|
||||
---------------------------
|
||||
|
||||
The ``<max_order>`` element allows the user to set a maximum scattering order
|
||||
to apply to every nuclide/material in the problem. That is, if the data
|
||||
library has :math:`P_3` data available, but ``<max_order>`` was set to ``1``,
|
||||
then, OpenMC will only use up to the :math:`P_1` data.
|
||||
|
||||
*Default*: Use the maximum order in the data library
|
||||
|
||||
.. note:: This element is not used in the continuous-energy
|
||||
:ref:`energy_mode`.
|
||||
|
||||
.. _natural_elements:
|
||||
|
||||
``<natural_elements>`` Element
|
||||
|
|
@ -324,10 +354,10 @@ out the file and "false" will not.
|
|||
*Default*: false
|
||||
|
||||
:summary:
|
||||
Writes out an ASCII summary file describing all of the user input files that
|
||||
Writes out an HDF5 summary file describing all of the user input files that
|
||||
were read in.
|
||||
|
||||
*Default*: false
|
||||
*Default*: true
|
||||
|
||||
:tallies:
|
||||
Write out an ASCII file of tally results.
|
||||
|
|
@ -355,6 +385,8 @@ or sub-elements and can be set to either "false" or "true".
|
|||
|
||||
*Default*: true
|
||||
|
||||
.. note:: This element is not used in the multi-group :ref:`energy_mode`.
|
||||
|
||||
``<resonance_scattering>`` Element
|
||||
----------------------------------
|
||||
|
||||
|
|
@ -414,6 +446,8 @@ attributes or sub-elements:
|
|||
|
||||
*Defaults*: None (scatterer), ARES (method), 0.01 eV (E_min), 1.0 keV (E_max)
|
||||
|
||||
.. note:: This element is not used in the multi-group :ref:`energy_mode`.
|
||||
|
||||
``<run_cmfd>`` Element
|
||||
----------------------
|
||||
|
||||
|
|
@ -591,6 +625,8 @@ variable and whose sub-elements/attributes are as follows:
|
|||
number :math:`a` that parameterizes the distribution :math:`p(x) dx = c x
|
||||
e^{-x/a} dx`.
|
||||
|
||||
.. note:: The above format should be used even when using the multi-group
|
||||
:ref:`energy_mode`.
|
||||
:interpolation:
|
||||
For a "tabular" distribution, ``interpolation`` can be set to "histogram" or
|
||||
"linear-linear" thereby specifying how tabular points are to be interpolated.
|
||||
|
|
@ -1216,10 +1252,18 @@ Each ``material`` element can have the following attributes or sub-elements:
|
|||
``<element>`` sub-elements are to be interpreted as nuclide/element
|
||||
densities in atom/b-cm, and the total density of the material is taken as
|
||||
the sum of all nuclides/elements. The "sum" option cannot be used in
|
||||
conjunction with weight percents.
|
||||
conjunction with weight percents. The "macro" unit is used with
|
||||
a ``macroscopic`` quantity to indicate that the density is already included
|
||||
in the library and thus not needed here. However, if a value is provided
|
||||
for the ``value``, then this is treated as a number density multiplier on
|
||||
the macroscopic cross sections in the multi-group data. This can be used,
|
||||
for example, when perturbing the density slightly.
|
||||
|
||||
*Default*: None
|
||||
|
||||
.. note:: A ``macroscopic`` quantity can not be used in conjunction with a
|
||||
``nuclide``, ``element``, or ``sab`` quantity.
|
||||
|
||||
:nuclide:
|
||||
An element with attributes/sub-elements called ``name``, ``xs``, and ``ao``
|
||||
or ``wo``. The ``name`` attribute is the name of the cross-section for a
|
||||
|
|
@ -1247,6 +1291,9 @@ Each ``material`` element can have the following attributes or sub-elements:
|
|||
|
||||
*Default*: None
|
||||
|
||||
.. note:: The ``scattering`` attribute/sub-element is not used in the
|
||||
multi-group :ref:`energy_mode`.
|
||||
|
||||
:element:
|
||||
|
||||
Specifies that a natural element is present in the material. The natural
|
||||
|
|
@ -1282,6 +1329,9 @@ Each ``material`` element can have the following attributes or sub-elements:
|
|||
|
||||
*Default*: None
|
||||
|
||||
.. note:: The ``scattering`` attribute/sub-element is not used in the
|
||||
multi-group :ref:`energy_mode`.
|
||||
|
||||
:sab:
|
||||
Associates an S(a,b) table with the material. This element has
|
||||
attributes/sub-elements called ``name`` and ``xs``. The ``name`` attribute
|
||||
|
|
@ -1290,6 +1340,26 @@ Each ``material`` element can have the following attributes or sub-elements:
|
|||
|
||||
*Default*: None
|
||||
|
||||
.. note:: This element is not used in the multi-group :ref:`energy_mode`.
|
||||
|
||||
:macroscopic:
|
||||
The ``macroscopic`` element is similar to the ``nuclide`` element, but,
|
||||
recognizes that some multi-group libraries may be providing material
|
||||
specific macroscopic cross sections instead of always providing nuclide
|
||||
specific data like in the continuous-energy case. To that end, the
|
||||
macroscopic element has attributes/sub-elements called ``name``, and ``xs``.
|
||||
The ``name`` attribute is the name of the cross-section for a
|
||||
desired nuclide while the ``xs`` attribute is the cross-section
|
||||
identifier. One example would be as follows:
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<macroscopic name="UO2" xs="71c" />
|
||||
|
||||
.. note:: This element is only used in the multi-group :ref:`energy_mode`.
|
||||
|
||||
*Default*: None
|
||||
|
||||
.. _IUPAC Isotopic Compositions of the Elements 2009:
|
||||
http://pac.iupac.org/publications/pac/pdf/2011/pdf/8302x0397.pdf
|
||||
|
||||
|
|
@ -1376,7 +1446,8 @@ The ``<tally>`` element accepts the following sub-elements:
|
|||
A list of universes for which the tally should be accumulated.
|
||||
|
||||
:energy:
|
||||
A monotonically increasing list of bounding **pre-collision** energies
|
||||
In continuous-energy mode, this filter should be provided as a
|
||||
monotonically increasing list of bounding **pre-collision** energies
|
||||
for a number of groups. For example, if this filter is specified as
|
||||
|
||||
.. code-block:: xml
|
||||
|
|
@ -1386,17 +1457,24 @@ The ``<tally>`` element accepts the following sub-elements:
|
|||
then two energy bins will be created, one with energies between 0 and
|
||||
1 MeV and the other with energies between 1 and 20 MeV.
|
||||
|
||||
In multi-group mode the bins provided must match group edges
|
||||
defined in the multi-group library.
|
||||
|
||||
:energyout:
|
||||
A monotonically increasing list of bounding **post-collision**
|
||||
energies for a number of groups. For example, if this filter is
|
||||
specified as
|
||||
In continuous-energy mode, this filter should be provided as a
|
||||
monotonically increasing list of bounding **post-collision** energies
|
||||
for a number of groups. For example, if this filter is specified as
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<filter type="energyout" bins="0.0 1.0 20.0" />
|
||||
|
||||
then two post-collision energy bins will be created, one with energies
|
||||
between 0 and 1 MeV and the other with energies between 1 and 20 MeV.
|
||||
then two post-collision energy bins will be created, one with
|
||||
energies between 0 and 1 MeV and the other with energies between
|
||||
1 and 20 MeV.
|
||||
|
||||
In multi-group mode the bins provided must match group edges
|
||||
defined in the multi-group library.
|
||||
|
||||
:mu:
|
||||
A monotonically increasing list of bounding **post-collision** cosines
|
||||
|
|
@ -1480,6 +1558,8 @@ The ``<tally>`` element accepts the following sub-elements:
|
|||
|
||||
<filter type="delayedgroup" bins="1 2 3 4 5 6" />
|
||||
|
||||
.. note:: This filter type is not used in the multi-group :ref:`energy_mode`.
|
||||
|
||||
:nuclides:
|
||||
If specified, the scores listed will be for particular nuclides, not the
|
||||
summation of reactions from all nuclides. The format for nuclides should be
|
||||
|
|
@ -1659,7 +1739,8 @@ The ``<tally>`` element accepts the following sub-elements:
|
|||
|Score | Description |
|
||||
+======================+===================================================+
|
||||
|delayed-nu-fission |Total production of delayed neutrons due to |
|
||||
| |fission. |
|
||||
| |fission. This score type is not used in the |
|
||||
| |multi-group :ref:`energy_mode`. |
|
||||
+----------------------+---------------------------------------------------+
|
||||
|nu-fission |Total production of neutrons due to fission. |
|
||||
+----------------------+---------------------------------------------------+
|
||||
|
|
@ -1688,6 +1769,8 @@ The ``<tally>`` element accepts the following sub-elements:
|
|||
+----------------------+---------------------------------------------------+
|
||||
|inverse-velocity |The flux-weighted inverse velocity where the |
|
||||
| |velocity is in units of centimeters per second. |
|
||||
| |This score type is not used in the |
|
||||
| |multi-group :ref:`energy_mode`. |
|
||||
+----------------------+---------------------------------------------------+
|
||||
|kappa-fission |The recoverable energy production rate due to |
|
||||
| |fission. The recoverable energy is defined as the |
|
||||
|
|
|
|||
|
|
@ -227,6 +227,7 @@ your PATH environment variable and subsequently uses it to determine library
|
|||
locations and compile flags. If you have multiple installations of HDF5 or one
|
||||
that does not appear on your PATH, you can set the HDF5_ROOT environment
|
||||
variable to the root directory of the HDF5 installation, e.g.
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
export HDF5_ROOT=/opt/hdf5/1.8.15
|
||||
|
|
@ -355,7 +356,7 @@ Testing Build
|
|||
-------------
|
||||
|
||||
If you have ENDF/B-VII.1 cross sections from NNDC_ you can test your build.
|
||||
Make sure the **CROSS_SECTIONS** environmental variable is set to the
|
||||
Make sure the **OPENMC_CROSS_SECTIONS** environmental variable is set to the
|
||||
*cross_sections.xml* file in the *data/nndc* directory.
|
||||
There are two ways to run tests. The first is to use the Makefile present in
|
||||
the source directory and run the following:
|
||||
|
|
@ -380,11 +381,17 @@ Cross Section Configuration
|
|||
---------------------------
|
||||
|
||||
In order to run a simulation with OpenMC, you will need cross section data for
|
||||
each nuclide in your problem. Since OpenMC uses ACE format cross sections, you
|
||||
can use nuclear data that was processed with NJOY_, such as that distributed
|
||||
with MCNP_ or Serpent_. Several sources provide free processed ACE data as
|
||||
described below. The TALYS-based evaluated nuclear data library, TENDL_, is also
|
||||
openly available in ACE format.
|
||||
each nuclide or material in your problem. OpenMC can be run in
|
||||
continuous-energy or multi-group mode.
|
||||
|
||||
In continuous-energy mode OpenMC uses ACE format cross sections; in this case
|
||||
you can use nuclear data that was processed with NJOY_, such as that
|
||||
distributed with MCNP_ or Serpent_. Several sources provide free processed
|
||||
ACE data as described below. The TALYS-based evaluated nuclear data library,
|
||||
TENDL_, is also openly available in ACE format.
|
||||
|
||||
In multi-group mode, OpenMC utilizes an XML-based library format which can be
|
||||
used to describe nuclide- or material-specific quantities.
|
||||
|
||||
Using ENDF/B-VII.1 Cross Sections from NNDC
|
||||
-------------------------------------------
|
||||
|
|
@ -399,9 +406,10 @@ extract, and set up a confiuration file:
|
|||
cd openmc/data
|
||||
python get_nndc_data.py
|
||||
|
||||
At this point, you should set the :envvar:`CROSS_SECTIONS` environment variable
|
||||
to the absolute path of the file ``openmc/data/nndc/cross_sections.xml``. This
|
||||
cross section set is used by the test suite.
|
||||
At this point, you should set the :envvar:`OPENMC_CROSS_SECTIONS` environment
|
||||
variable to the absolute path of the file
|
||||
``openmc/data/nndc/cross_sections.xml``. This cross section set is used by the
|
||||
test suite.
|
||||
|
||||
Using JEFF Cross Sections from OECD/NEA
|
||||
---------------------------------------
|
||||
|
|
@ -427,8 +435,8 @@ the following steps must be taken:
|
|||
4. Additionally, you may need to change any occurrences of upper-case "ACE"
|
||||
within the ``cross_sections.xml`` file to lower-case.
|
||||
5. Either set the :ref:`cross_sections` in a settings.xml file or the
|
||||
:envvar:`CROSS_SECTIONS` environment variable to the absolute path of the
|
||||
``cross_sections.xml`` file.
|
||||
:envvar:`OPENMC_CROSS_SECTIONS` environment variable to the absolute path of
|
||||
the ``cross_sections.xml`` file.
|
||||
|
||||
Using Cross Sections from MCNP
|
||||
------------------------------
|
||||
|
|
@ -436,8 +444,9 @@ Using Cross Sections from MCNP
|
|||
To use cross sections distributed with MCNP, change the <directory> element in
|
||||
the ``cross_sections.xml`` file in the root directory of the OpenMC distribution
|
||||
to the location of the MCNP cross sections. Then, either set the
|
||||
:ref:`cross_sections` in a settings.xml file or the :envvar:`CROSS_SECTIONS`
|
||||
environment variable to the absolute path of the ``cross_sections.xml`` file.
|
||||
:ref:`cross_sections` in a settings.xml file or the
|
||||
:envvar:`OPENMC_CROSS_SECTIONS` environment variable to the absolute path of
|
||||
the ``cross_sections.xml`` file.
|
||||
|
||||
Using Cross Sections from Serpent
|
||||
---------------------------------
|
||||
|
|
@ -445,10 +454,21 @@ Using Cross Sections from Serpent
|
|||
To use cross sections distributed with Serpent, change the <directory> element
|
||||
in the ``cross_sections_serpent.xml`` file in the root directory of the OpenMC
|
||||
distribution to the location of the Serpent cross sections. Then, either set the
|
||||
:ref:`cross_sections` in a settings.xml file or the :envvar:`CROSS_SECTIONS`
|
||||
environment variable to the absolute path of the ``cross_sections_serpent.xml``
|
||||
:ref:`cross_sections` in a settings.xml file or the
|
||||
:envvar:`OPENMC_CROSS_SECTIONS` environment variable to the absolute path of
|
||||
the ``cross_sections_serpent.xml``
|
||||
file.
|
||||
|
||||
Using Multi-Group Cross Sections
|
||||
--------------------------------
|
||||
|
||||
Multi-group cross section libraries are generally tailored to the specific
|
||||
calculation to be performed. Therefore, at this point in time, OpenMC is not
|
||||
distributed with any pre-existing multi-group cross section libraries.
|
||||
However, if the user has obtained or generated their own library, the user
|
||||
should set the :envvar:`OPENMC_MG_CROSS_SECTIONS` environment variable
|
||||
to the absolute path of the file library expected to used most frequently.
|
||||
|
||||
.. _NJOY: http://t2.lanl.gov/nis/codes.shtml
|
||||
.. _NNDC: http://www.nndc.bnl.gov/endf/b7.1/acefiles.html
|
||||
.. _NEA: http://www.oecd-nea.org
|
||||
|
|
|
|||
302
docs/source/usersguide/mgxs_library.rst
Normal file
302
docs/source/usersguide/mgxs_library.rst
Normal file
|
|
@ -0,0 +1,302 @@
|
|||
.. _usersguide_mgxs_library:
|
||||
|
||||
========================================
|
||||
Multi-Group Cross Section Library Format
|
||||
========================================
|
||||
|
||||
OpenMC can be run in continuous-energy mode or multi-group mode, provided the
|
||||
nuclear data is available. In continuous-energy mode, the
|
||||
``cross_sections.xml`` file contains necessary meta-data for each data set,
|
||||
including the name and a file system location where the complete library
|
||||
can be found. In multi-group mode, this ``cross_sections.xml`` file contains
|
||||
this same meta-data describing the nuclide or material, but also contains the
|
||||
group-wise nuclear data. This portion of the manual describes the format of
|
||||
the multi-group data library required to be used in the ``cross_sections.xml``
|
||||
file.
|
||||
|
||||
Similar to the other input file types, the multi-group library is provided in
|
||||
the XML_ format. This library must provide some meta-data about the library
|
||||
itself (such as the number of groups and the group structure, etc.) as well as
|
||||
the actual cross section data itself for each of the necessary nuclides or
|
||||
materials.
|
||||
|
||||
.. _XML: http://www.w3.org/XML/
|
||||
|
||||
------------------------------------------------
|
||||
MGXS Library Specification -- cross_sections.xml
|
||||
------------------------------------------------
|
||||
|
||||
The multi-group library meta-data is contained within the groups_,
|
||||
group_structure_, and inverse_velocities_ elements.
|
||||
The actual multi-group data itself is contained within the xsdata_ element.
|
||||
|
||||
.. _groups:
|
||||
|
||||
``<groups>`` Element
|
||||
----------------------------------
|
||||
|
||||
The ``<groups>`` element has no attributes and simply provides the number of
|
||||
energy groups contained within the library.
|
||||
|
||||
*Default*: None, this must be provided.
|
||||
|
||||
.. _group_structure:
|
||||
|
||||
``<group_structure>`` Element
|
||||
-----------------------------
|
||||
|
||||
The ``<group_structure>`` element has no attributes and should be provided as a
|
||||
monotonically increasing list of bounding energies, in MeV, for a number of
|
||||
groups. To provide proper energy boundaries, the length of the data within the
|
||||
``<group_structure>`` element should be one more than the number of groups in
|
||||
the problem. For example, a two-group problem could be specified as:
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<group_structure> 0.0 0.625E-6 20.0 </group_structure>
|
||||
|
||||
*Default*: None, this must be provided.
|
||||
|
||||
.. _inverse_velocities:
|
||||
|
||||
``<inverse_velocities>`` Element
|
||||
--------------------------------
|
||||
|
||||
The ``<inverse_velocities>`` element optionally indicates the average
|
||||
inverse velocity corresponding to each of the groups in the problem.
|
||||
This element should therefore be an array with a length which matches the
|
||||
number of groups set in the groups_ element.
|
||||
|
||||
*Default*: Should this be needed by the presence of an ``inverse-velocity``
|
||||
score in the ``tallies.xml`` file and not provided in this element, OpenMC
|
||||
will simply convert the group mid-point energy to an inverse of the velocity
|
||||
and use this information for tallying.
|
||||
|
||||
.. _xsdata:
|
||||
|
||||
``<xsdata>`` Element
|
||||
--------------------
|
||||
|
||||
The ``<xsdata>`` element contains the nuclide or material-specific meta-data as
|
||||
well as the actual cross section data. The following are the
|
||||
attributes/sub-elements required to describe the meta-data:
|
||||
|
||||
:name:
|
||||
The name of the microscopic or macroscopic data set. An extension to the
|
||||
name must be provided (e.g., the ``.300K`` in ``UO2.300K``). The name and
|
||||
extension together must be twelve or less characters in length. This
|
||||
extension must follow a period and be five characters or less in length.
|
||||
similar to the equivalent in the continuous-energy ``cross_sections.xml``
|
||||
file, is used to denote variants of the particular nuclide or material of
|
||||
interest (i.e. the ``UO2`` data in this example could have been generated
|
||||
at a temperature of 300K).
|
||||
|
||||
*Default*: None, this must be provided.
|
||||
|
||||
:alias:
|
||||
An alternative name to use for the microscopic or macroscopic data set.
|
||||
|
||||
*Default*: If no alias is provided, it will adopt the value of ``name``.
|
||||
|
||||
:kT:
|
||||
The temperature times Boltzmann's constant (in units of MeV) at which the
|
||||
data was generated.
|
||||
|
||||
*Default*: Room temperature, 2.53E-8 MeV
|
||||
|
||||
:fissionable:
|
||||
This element states whether or not the data in question is fissionable.
|
||||
Accepted values are "true" or "false".
|
||||
|
||||
*Default*: None, this element must be provided.
|
||||
|
||||
:representation:
|
||||
This element provides the method used to generate and represent the
|
||||
multi-group cross sections. That is, whether they were generated with
|
||||
scalar flux weighting (or reduced to an equivalent representation)
|
||||
and thus are angle-independent, or if the data was generated with angular
|
||||
dependent fluxes and thus the data is angle-dependent. The options are
|
||||
either "isotropic" or "angle".
|
||||
|
||||
*Default*: "isotropic"
|
||||
|
||||
:num_azimuthal:
|
||||
This element provides the number of equal width angular bins that the
|
||||
azimuthal angular domain is subdivided in the case of angle-dependent
|
||||
cross sections (i.e., "angle" is passed to the ``representation`` element).
|
||||
Note that these bins are equal in azimuthal angle widths, not equal in the
|
||||
cosine of the azimuthal angle widths.
|
||||
|
||||
*Default*: If ``representation`` is "angle", this must be provided. This
|
||||
parameter is not used for other ``representation`` types.
|
||||
|
||||
:num_polar:
|
||||
This element provides the number of equal width angular bins that the
|
||||
polar angular domain is subdivided in the case of angle-dependent
|
||||
cross sections (i.e., "angle" is passed to the ``representation`` element).
|
||||
Note that these bins are equal in polar angle widths, not equal in the
|
||||
cosine of the polar angle widths.
|
||||
|
||||
|
||||
*Default*: If ``representation`` is "angle", this must be provided. This
|
||||
parameter is not used for other ``representation`` types.
|
||||
|
||||
:scatt_type:
|
||||
This element provides the representation of the angular distribution
|
||||
associated with each group-to-group transfer probability. The options are
|
||||
either "legendre", "histogram", or "tabular".
|
||||
The "legendre" option means the angular distribution has been
|
||||
expanded via Legendre polynomials of the order provided in the "order"
|
||||
element.
|
||||
The "histogram" option means the angular distribution is provided in
|
||||
an equi-width histogram format with a number of bins as provided in the
|
||||
"order" element. This is useful when the angular distribution was
|
||||
obtained from a Monte Carlo tally and thus is natively in the histogram
|
||||
format.
|
||||
The "tabular" option means the angular distribution is provided in an
|
||||
equi-spaced point-wise representation.
|
||||
|
||||
*Default*: "legendre"
|
||||
|
||||
:order:
|
||||
This element provides either the Legendre order, number of bins, or number
|
||||
of points used to describe the angular distribution associated with each
|
||||
group-to-group transfer probability. The specific meaning of this bin
|
||||
depends upon the value of ``scatt_type`` as discussed above.
|
||||
|
||||
*Default*: None, this element must be provided.
|
||||
|
||||
:tabular_legendre:
|
||||
This optional element is used to set how the Legendre scattering kernel, if
|
||||
provided via the ``scatt_type`` element above, is represented and thus used
|
||||
during the scattering process. Specifically, the options are to either
|
||||
convert the Legendre expansion to a tabular representation or leave it as
|
||||
a set of Legendre coefficients. Converting to a tabular representation will
|
||||
cost memory but is likely to decrease runtime compared to leaving as a
|
||||
set of Legendre coefficients. This element has the following
|
||||
attributes/sub-elements:
|
||||
|
||||
:enable:
|
||||
This attribute/sub-element denotes whether or not the conversion to the
|
||||
tabular format should be performed or not. A value of "true" means
|
||||
the conversion should be performed, "false" means it should not.
|
||||
|
||||
*Default*: "true"
|
||||
|
||||
:num_points:
|
||||
If the conversion is to take place the number of tabular points is
|
||||
required. This attribute/sub-element allows the user to set the desired
|
||||
number of points.
|
||||
|
||||
*Default*: 33
|
||||
|
||||
The following attributes/sub-elements are the cross section values to
|
||||
be used during the transport process.
|
||||
|
||||
:total:
|
||||
This element requires the group-wise total cross section ordered by
|
||||
increasing group index (i.e., fast to thermal). If ``representation`` is
|
||||
"isotropic", then the length of this list should equal the number of
|
||||
groups described in the ``groups`` element. If ``representation`` is
|
||||
"angle", then the length of this list should equal the number of groups
|
||||
times the number of azimuthal angles times the number of polar angles,
|
||||
with the inner-dimension being groups, intermediate-dimension being
|
||||
azimuthal angles and outer-dimension being the polar angles.
|
||||
|
||||
*Default*: If not provided, it will be determined by summing the
|
||||
absorption and scattering cross sections.
|
||||
|
||||
:absorption:
|
||||
This element requires the group-wise absorption cross section ordered by
|
||||
increasing group index (i.e., fast to thermal). If ``representation`` is
|
||||
"isotropic", then the length of this list should equal the number of
|
||||
groups described in the ``groups`` element. If ``representation`` is
|
||||
"angle", then the length of this list should equal the number of groups
|
||||
times the number of azimuthal angles times the number of polar angles,
|
||||
with the inner-dimension being groups, intermediate-dimension being
|
||||
azimuthal angles and outer-dimension being the polar angles.
|
||||
|
||||
*Default*: None, this must be provided.
|
||||
|
||||
:scatter:
|
||||
This element requires the scattering moment matrices presented with the
|
||||
columns representing incoming group and rows representing the outgoing
|
||||
group. That is, down-scatter will be above the diagonal of the resultant
|
||||
matrix. This matrix is repeated for every Legendre order (in order of
|
||||
increasing orders) if ``scatt_type`` is "legendre"; otherwise, this
|
||||
matrix is repeated for every bin of the histogram or tabular
|
||||
representation. Finally, if ``representation`` is "angle", the above
|
||||
is repeated for every azimuthal angle and every polar angle, in that
|
||||
order.
|
||||
|
||||
*Default*: None, this must be provided.
|
||||
|
||||
:multiplicity:
|
||||
This element provides the ratio of neutrons produced in scattering
|
||||
collisions to the neutrons which undergo scattering collisions; that is,
|
||||
the multiplicity provides the code with a scaling factor to account for
|
||||
neutrons being produced in (n,xn) reactions. This information is assumed
|
||||
isotropic and therefore does not need to be repeated for every Legendre
|
||||
moment or histogram/tabular bin. This matrix follows the same arrangement
|
||||
as described for the ``scatter`` element, with the exception of the
|
||||
data needed to provide the scattering type information.
|
||||
|
||||
*Default*: Multiplicities of 1.0 are assumed (i.e., (n,xn) reactions are
|
||||
neglected).
|
||||
|
||||
The following fission-specific data are only needed should ``fissionable``
|
||||
be "true".
|
||||
|
||||
:fission:
|
||||
This element requires the group-wise fission cross section ordered by
|
||||
increasing group index (i.e., fast to thermal). If ``representation`` is
|
||||
"isotropic", then the length of this list should equal the number of
|
||||
groups described in the ``groups`` element. If ``representation`` is
|
||||
"angle", then the length of this list should equal the number of groups
|
||||
times the number of azimuthal angles times the number of polar angles,
|
||||
with the inner-dimension being groups, intermediate-dimension being
|
||||
azimuthal angles and outer-dimension being the polar angles.
|
||||
|
||||
*Default*: None, this is required only if fission tallies are
|
||||
requested and the material is fissionable.
|
||||
|
||||
:kappa_fission:
|
||||
This element requires the group-wise kappa-fission cross section ordered by
|
||||
increasing group index (i.e., fast to thermal). If ``representation`` is
|
||||
"isotropic", then the length of this list should equal the number of
|
||||
groups described in the ``groups`` element. If ``representation`` is
|
||||
"angle", then the length of this list should equal the number of groups
|
||||
times the number of azimuthal angles times the number of polar angles,
|
||||
with the inner-dimension being groups, intermediate-dimension being
|
||||
azimuthal angles and outer-dimension being the polar angles.
|
||||
|
||||
*Default*: None, this is required only if kappa_fission tallies are
|
||||
requested and the material is fissionable.
|
||||
|
||||
:chi:
|
||||
This element requires the group-wise fission spectra ordered by
|
||||
increasing group index (i.e., fast to thermal). This element should be
|
||||
used if making the common approximation that the fission spectra does
|
||||
not depend on incoming energy. If the user does not wish to make this
|
||||
approximation, then this should not be provided and this information
|
||||
included in the ``nu_fission`` element instead. If ``representation`` is
|
||||
"isotropic", then the length of this list should equal the number of
|
||||
groups described in the ``groups`` element. If ``representation`` is
|
||||
"angle", then the length of this list should equal the number of groups
|
||||
times the number of azimuthal angles times the number of polar angles,
|
||||
with the inner-dimension being groups, intermediate-dimension being
|
||||
azimuthal angles and outer-dimension being the polar angles.
|
||||
|
||||
*Default*: None, either this element is provided or ``nu_fission`` is
|
||||
provided in fission matrix form, or the material is not fissionable.
|
||||
|
||||
:nu_fission:
|
||||
This element provides either the group-wise fission production cross
|
||||
section vector (i.e., if ``chi`` is provided), or is the group-wise fission
|
||||
production matrix. If providing the vector, it should be ordered the same
|
||||
as the ``fission`` data. If providing the matrix, it should be ordered
|
||||
the same as the ``multiplicity`` matrix.
|
||||
|
||||
*Default*: None, either this element must be provided if the material
|
||||
is fissionable.
|
||||
|
||||
|
|
@ -46,7 +46,8 @@ The current revision of the particle restart file format is 1.
|
|||
|
||||
**/energy** (*double*)
|
||||
|
||||
Energy of the particle in MeV.
|
||||
Energy of the particle in MeV for continuous-energy mode, or the energy
|
||||
group of the particle for multi-group mode.
|
||||
|
||||
**/xyz** (*double[3]*)
|
||||
|
||||
|
|
|
|||
|
|
@ -15,5 +15,6 @@ is that documented here.
|
|||
**/source_bank** (Compound type)
|
||||
|
||||
Source bank information for each particle. The compound type has fields
|
||||
``wgt``, ``xyz``, ``uvw``, and ``E`` which represent the weight, position,
|
||||
direction, and energy of the source particle, respectively.
|
||||
``wgt``, ``xyz``, ``uvw``, ``E``, and ``delayed_group``, which
|
||||
represent the weight, position, direction, energy, energy group, and
|
||||
delayed_group of the source particle, respectively.
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
State Point File Format
|
||||
=======================
|
||||
|
||||
The current revision of the statepoint file format is 14.
|
||||
The current revision of the statepoint file format is 15.
|
||||
|
||||
**/filetype** (*char[]*)
|
||||
|
||||
|
|
@ -39,6 +39,12 @@ The current revision of the statepoint file format is 14.
|
|||
|
||||
Pseudo-random number generator seed.
|
||||
|
||||
**/run_CE** (*int*)
|
||||
|
||||
Flag to denote continuous-energy or multi-group mode. A value of 1
|
||||
indicates a continuous-energy run while a value of 0 indicates a
|
||||
multi-group run.
|
||||
|
||||
**/run_mode** (*char[]*)
|
||||
|
||||
Run mode used. A value of 1 indicates a fixed-source run and a value of 2
|
||||
|
|
@ -251,5 +257,6 @@ if (run_mode == 'k-eigenvalue' and source_present > 0)
|
|||
**/source_bank** (Compound type)
|
||||
|
||||
Source bank information for each particle. The compound type has fields
|
||||
``wgt``, ``xyz``, ``uvw``, and ``E`` which represent the weight,
|
||||
position, direction, and energy of the source particle, respectively.
|
||||
``wgt``, ``xyz``, ``uvw``, ``E``, ``g``, and ``delayed_group``, which
|
||||
represent the weight, position, direction, energy, energy group, and
|
||||
delayed_group of the source particle, respectively.
|
||||
|
|
|
|||
|
|
@ -109,29 +109,20 @@ energyout_filter = openmc.Filter(type='energyout', bins=[0., 20.])
|
|||
|
||||
# Instantiate the first Tally
|
||||
first_tally = openmc.Tally(tally_id=1, name='first tally')
|
||||
first_tally.add_filter(cell_filter)
|
||||
scores = ['total', 'scatter', 'nu-scatter', \
|
||||
first_tally.filters = [cell_filter]
|
||||
scores = ['total', 'scatter', 'nu-scatter',
|
||||
'absorption', 'fission', 'nu-fission']
|
||||
for score in scores:
|
||||
first_tally.add_score(score)
|
||||
first_tally.scores = scores
|
||||
|
||||
# Instantiate the second Tally
|
||||
second_tally = openmc.Tally(tally_id=2, name='second tally')
|
||||
second_tally.add_filter(cell_filter)
|
||||
second_tally.add_filter(energy_filter)
|
||||
scores = ['total', 'scatter', 'nu-scatter', \
|
||||
'absorption', 'fission', 'nu-fission']
|
||||
for score in scores:
|
||||
second_tally.add_score(score)
|
||||
second_tally.filters = [cell_filter, energy_filter]
|
||||
second_tally.scores = scores
|
||||
|
||||
# Instantiate the third Tally
|
||||
third_tally = openmc.Tally(tally_id=3, name='third tally')
|
||||
third_tally.add_filter(cell_filter)
|
||||
third_tally.add_filter(energy_filter)
|
||||
third_tally.add_filter(energyout_filter)
|
||||
scores = ['scatter', 'nu-scatter', 'nu-fission']
|
||||
for score in scores:
|
||||
third_tally.add_score(score)
|
||||
third_tally.filters = [cell_filter, energy_filter, energyout_filter]
|
||||
third_tally.scores = ['scatter', 'nu-scatter', 'nu-fission']
|
||||
|
||||
# Instantiate a TalliesFile, register all Tallies, and export to XML
|
||||
tallies_file = openmc.TalliesFile()
|
||||
|
|
|
|||
|
|
@ -166,8 +166,8 @@ plot_file.export_to_xml()
|
|||
|
||||
# Instantiate a distribcell Tally
|
||||
tally = openmc.Tally(tally_id=1)
|
||||
tally.add_filter(openmc.Filter(type='distribcell', bins=[cell2.id]))
|
||||
tally.add_score('total')
|
||||
tally.filters = [openmc.Filter(type='distribcell', bins=[cell2.id])]
|
||||
tally.scores = ['total']
|
||||
|
||||
# Instantiate a TalliesFile, register Tally/Mesh, and export to XML
|
||||
tallies_file = openmc.TalliesFile()
|
||||
|
|
|
|||
|
|
@ -175,8 +175,8 @@ mesh_filter.mesh = mesh
|
|||
|
||||
# Instantiate the Tally
|
||||
tally = openmc.Tally(tally_id=1)
|
||||
tally.add_filter(mesh_filter)
|
||||
tally.add_score('total')
|
||||
tally.filters = [mesh_filter]
|
||||
tally.scores = ['total']
|
||||
|
||||
# Instantiate a TalliesFile, register Tally/Mesh, and export to XML
|
||||
tallies_file = openmc.TalliesFile()
|
||||
|
|
|
|||
|
|
@ -167,13 +167,13 @@ mesh_filter.mesh = mesh
|
|||
|
||||
# Instantiate tally Trigger
|
||||
trigger = openmc.Trigger(trigger_type='rel_err', threshold=1E-2)
|
||||
trigger.add_score('all')
|
||||
trigger.scores = ['all']
|
||||
|
||||
# Instantiate the Tally
|
||||
tally = openmc.Tally(tally_id=1)
|
||||
tally.add_filter(mesh_filter)
|
||||
tally.add_score('total')
|
||||
tally.add_trigger(trigger)
|
||||
tally.filters = [mesh_filter]
|
||||
tally.scores = ['total']
|
||||
tally.triggers = [trigger]
|
||||
|
||||
# Instantiate a TalliesFile, register Tally/Mesh, and export to XML
|
||||
tallies_file = openmc.TalliesFile()
|
||||
|
|
|
|||
|
|
@ -196,11 +196,8 @@ mesh_filter.mesh = mesh
|
|||
|
||||
# Instantiate the Tally
|
||||
tally = openmc.Tally(tally_id=1, name='tally 1')
|
||||
tally.add_filter(energy_filter)
|
||||
tally.add_filter(mesh_filter)
|
||||
tally.add_score('flux')
|
||||
tally.add_score('fission')
|
||||
tally.add_score('nu-fission')
|
||||
tally.filters = [energy_filter, mesh_filter]
|
||||
tally.scores = ['flux', 'fission', 'nu-fission']
|
||||
|
||||
# Instantiate a TalliesFile, register all Tallies, and export to XML
|
||||
tallies_file = openmc.TalliesFile()
|
||||
|
|
|
|||
180
examples/python/pincell_multigroup/build-xml.py
Normal file
180
examples/python/pincell_multigroup/build-xml.py
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
import openmc
|
||||
import openmc.mgxs
|
||||
from openmc.source import Source
|
||||
from openmc.stats import Box
|
||||
import numpy as np
|
||||
|
||||
###############################################################################
|
||||
# Simulation Input File Parameters
|
||||
###############################################################################
|
||||
|
||||
# OpenMC simulation parameters
|
||||
batches = 100
|
||||
inactive = 10
|
||||
particles = 1000
|
||||
|
||||
###############################################################################
|
||||
# Exporting to OpenMC mg_cross_sections.xml File
|
||||
###############################################################################
|
||||
|
||||
# Instantiate the energy group data
|
||||
groups = openmc.mgxs.EnergyGroups(group_edges=[1E-11, 0.0635E-6, 10.0E-6,
|
||||
1.0E-4, 1.0E-3, 0.5, 1.0, 20.0])
|
||||
|
||||
# Instantiate the 7-group (C5G7) cross section data
|
||||
uo2_xsdata = openmc.XSdata('UO2.300K', groups)
|
||||
uo2_xsdata.order = 0
|
||||
uo2_xsdata.total = np.array([0.1779492, 0.3298048, 0.4803882, 0.5543674,
|
||||
0.3118013, 0.3951678, 0.5644058])
|
||||
uo2_xsdata.absorption = np.array([8.0248E-03, 3.7174E-03, 2.6769E-02, 9.6236E-02,
|
||||
3.0020E-02, 1.1126E-01, 2.8278E-01])
|
||||
scatter = [[[0.1275370, 0.0423780, 0.0000094, 0.0000000, 0.0000000, 0.0000000, 0.0000000],
|
||||
[0.0000000, 0.3244560, 0.0016314, 0.0000000, 0.0000000, 0.0000000, 0.0000000],
|
||||
[0.0000000, 0.0000000, 0.4509400, 0.0026792, 0.0000000, 0.0000000, 0.0000000],
|
||||
[0.0000000, 0.0000000, 0.0000000, 0.4525650, 0.0055664, 0.0000000, 0.0000000],
|
||||
[0.0000000, 0.0000000, 0.0000000, 0.0001253, 0.2714010, 0.0102550, 0.0000000],
|
||||
[0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0012968, 0.2658020, 0.0168090],
|
||||
[0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0085458, 0.2730800]]]
|
||||
uo2_xsdata.scatter = np.array(scatter[:][:])
|
||||
uo2_xsdata.fission = np.array([7.21206E-03, 8.19301E-04, 6.45320E-03,
|
||||
1.85648E-02, 1.78084E-02, 8.30348E-02,
|
||||
2.16004E-01])
|
||||
uo2_xsdata.nu_fission = np.array([2.005998E-02, 2.027303E-03, 1.570599E-02,
|
||||
4.518301E-02, 4.334208E-02, 2.020901E-01,
|
||||
5.257105E-01])
|
||||
uo2_xsdata.chi = np.array([5.8791E-01, 4.1176E-01, 3.3906E-04, 1.1761E-07,
|
||||
0.0000E+00, 0.0000E+00, 0.0000E+00])
|
||||
|
||||
h2o_xsdata = openmc.XSdata('LWTR.300K', groups)
|
||||
h2o_xsdata.order = 0
|
||||
h2o_xsdata.total = np.array([0.15920605, 0.412969593, 0.59030986, 0.58435,
|
||||
0.718, 1.2544497, 2.650379])
|
||||
h2o_xsdata.absorption = np.array([6.0105E-04, 1.5793E-05, 3.3716E-04,
|
||||
1.9406E-03, 5.7416E-03, 1.5001E-02,
|
||||
3.7239E-02])
|
||||
scatter = [[[0.0444777, 0.1134000, 0.0007235, 0.0000037, 0.0000001, 0.0000000, 0.0000000],
|
||||
[0.0000000, 0.2823340, 0.1299400, 0.0006234, 0.0000480, 0.0000074, 0.0000010],
|
||||
[0.0000000, 0.0000000, 0.3452560, 0.2245700, 0.0169990, 0.0026443, 0.0005034],
|
||||
[0.0000000, 0.0000000, 0.0000000, 0.0910284, 0.4155100, 0.0637320, 0.0121390],
|
||||
[0.0000000, 0.0000000, 0.0000000, 0.0000714, 0.1391380, 0.5118200, 0.0612290],
|
||||
[0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0022157, 0.6999130, 0.5373200],
|
||||
[0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.1324400, 2.4807000]]]
|
||||
h2o_xsdata.scatter = np.array(scatter)
|
||||
|
||||
mg_cross_sections_file = openmc.MGXSLibraryFile(groups)
|
||||
mg_cross_sections_file.add_xsdatas([uo2_xsdata,h2o_xsdata])
|
||||
mg_cross_sections_file.export_to_xml()
|
||||
|
||||
|
||||
###############################################################################
|
||||
# Exporting to OpenMC materials.xml File
|
||||
###############################################################################
|
||||
|
||||
# Instantiate some Macroscopic Data
|
||||
uo2_data = openmc.Macroscopic('UO2', '300K')
|
||||
h2o_data = openmc.Macroscopic('LWTR', '300K')
|
||||
|
||||
# Instantiate some Materials and register the appropriate Macroscopic objects
|
||||
uo2 = openmc.Material(material_id=1, name='UO2 fuel')
|
||||
uo2.set_density('macro', 1.0)
|
||||
uo2.add_macroscopic(uo2_data)
|
||||
|
||||
water = openmc.Material(material_id=2, name='Water')
|
||||
water.set_density('macro', 1.0)
|
||||
water.add_macroscopic(h2o_data)
|
||||
|
||||
# Instantiate a MaterialsFile, register all Materials, and export to XML
|
||||
materials_file = openmc.MaterialsFile()
|
||||
materials_file.default_xs = '300K'
|
||||
materials_file.add_materials([uo2, water])
|
||||
materials_file.export_to_xml()
|
||||
|
||||
|
||||
###############################################################################
|
||||
# Exporting to OpenMC geometry.xml File
|
||||
###############################################################################
|
||||
|
||||
# Instantiate ZCylinder surfaces
|
||||
fuel_or = openmc.ZCylinder(surface_id=1, x0=0, y0=0, R=0.54, name='Fuel OR')
|
||||
left = openmc.XPlane(surface_id=4, x0=-0.63, name='left')
|
||||
right = openmc.XPlane(surface_id=5, x0=0.63, name='right')
|
||||
bottom = openmc.YPlane(surface_id=6, y0=-0.63, name='bottom')
|
||||
top = openmc.YPlane(surface_id=7, y0=0.63, name='top')
|
||||
|
||||
left.boundary_type = 'reflective'
|
||||
right.boundary_type = 'reflective'
|
||||
top.boundary_type = 'reflective'
|
||||
bottom.boundary_type = 'reflective'
|
||||
|
||||
# Instantiate Cells
|
||||
fuel = openmc.Cell(cell_id=1, name='cell 1')
|
||||
moderator = openmc.Cell(cell_id=2, name='cell 2')
|
||||
|
||||
# Use surface half-spaces to define regions
|
||||
fuel.region = -fuel_or
|
||||
moderator.region = +fuel_or & +left & -right & +bottom & -top
|
||||
|
||||
# Register Materials with Cells
|
||||
fuel.fill = uo2
|
||||
moderator.fill = water
|
||||
|
||||
# Instantiate Universe
|
||||
root = openmc.Universe(universe_id=0, name='root universe')
|
||||
|
||||
# Register Cells with Universe
|
||||
root.add_cells([fuel, moderator])
|
||||
|
||||
# Instantiate a Geometry and register the root Universe
|
||||
geometry = openmc.Geometry()
|
||||
geometry.root_universe = root
|
||||
|
||||
# Instantiate a GeometryFile, register Geometry, and export to XML
|
||||
geometry_file = openmc.GeometryFile()
|
||||
geometry_file.geometry = geometry
|
||||
geometry_file.export_to_xml()
|
||||
|
||||
|
||||
###############################################################################
|
||||
# Exporting to OpenMC settings.xml File
|
||||
###############################################################################
|
||||
|
||||
# Instantiate a SettingsFile, set all runtime parameters, and export to XML
|
||||
settings_file = openmc.SettingsFile()
|
||||
settings_file.energy_mode = "multi-group"
|
||||
settings_file.cross_sections = "./mg_cross_sections.xml"
|
||||
settings_file.batches = batches
|
||||
settings_file.inactive = inactive
|
||||
settings_file.particles = particles
|
||||
settings_file.source = Source(space=Box([-0.63, -0.63, -1.], [0.63, 0.63, 1.]))
|
||||
|
||||
###############################################################################
|
||||
# Exporting to OpenMC tallies.xml File
|
||||
###############################################################################
|
||||
|
||||
# Instantiate a tally mesh
|
||||
mesh = openmc.Mesh(mesh_id=1)
|
||||
mesh.type = 'regular'
|
||||
mesh.dimension = [100, 100, 1]
|
||||
mesh.lower_left = [-0.63, -0.63, -1.e50]
|
||||
mesh.upper_right = [0.63, 0.63, 1.e50]
|
||||
|
||||
# Instantiate some tally Filters
|
||||
energy_filter = openmc.Filter(type='energy',
|
||||
bins=[1E-11, 0.0635E-6, 10.0E-6, 1.0E-4, 1.0E-3,
|
||||
0.5, 1.0, 20.0])
|
||||
mesh_filter = openmc.Filter()
|
||||
mesh_filter.mesh = mesh
|
||||
|
||||
# Instantiate the Tally
|
||||
tally = openmc.Tally(tally_id=1, name='tally 1')
|
||||
tally.add_filter(energy_filter)
|
||||
tally.add_filter(mesh_filter)
|
||||
tally.add_score('flux')
|
||||
tally.add_score('fission')
|
||||
tally.add_score('nu-fission')
|
||||
|
||||
# Instantiate a TalliesFile, register all Tallies, and export to XML
|
||||
tallies_file = openmc.TalliesFile()
|
||||
tallies_file.add_mesh(mesh)
|
||||
tallies_file.add_tally(tally)
|
||||
tallies_file.export_to_xml()
|
||||
16
examples/xml/pincell_multigroup/geometry.xml
Normal file
16
examples/xml/pincell_multigroup/geometry.xml
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<geometry>
|
||||
|
||||
|
||||
|
||||
<surface coeffs="0. 0. 0.540" id="1" type="z-cylinder" />
|
||||
|
||||
<surface boundary="reflective" coeffs="-0.63" id="20" type="x-plane" />
|
||||
<surface boundary="reflective" coeffs=" 0.63" id="21" type="x-plane" />
|
||||
<surface boundary="reflective" coeffs="-0.63" id="22" type="y-plane" />
|
||||
<surface boundary="reflective" coeffs=" 0.63" id="23" type="y-plane" />
|
||||
|
||||
|
||||
<cell id="1" material="1" region=" -1" />
|
||||
<cell id="2" material="7" region="1 20 -21 22 -23" />
|
||||
|
||||
</geometry>
|
||||
54
examples/xml/pincell_multigroup/materials.xml
Normal file
54
examples/xml/pincell_multigroup/materials.xml
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
<?xml version="1.0"?>
|
||||
<materials>
|
||||
<!-- Set default xs set to use 300K data -->
|
||||
<default_xs>300K</default_xs>
|
||||
|
||||
<!-- UO2 -->
|
||||
<material id="1">
|
||||
<density units="macro" value="1.0" />
|
||||
<macroscopic name="UO2"/>
|
||||
</material>
|
||||
|
||||
<!-- 4.3% MOX -->
|
||||
<material id="2">
|
||||
<density units="macro" value="1.0" />
|
||||
<macroscopic name="MOX1"/>
|
||||
</material>
|
||||
|
||||
<!-- 7.0 MOX -->
|
||||
<material id="3">
|
||||
<density units="macro" value="1.0" />
|
||||
<macroscopic name="MOX2"/>
|
||||
</material>
|
||||
|
||||
<!-- 8.0% MOX -->
|
||||
<material id="4">
|
||||
<density units="macro" value="1.0" />
|
||||
<macroscopic name="MOX3"/>
|
||||
</material>
|
||||
|
||||
<!-- Fission Chamber -->
|
||||
<material id="5">
|
||||
<density units="macro" value="1.0" />
|
||||
<macroscopic name="FC"/>
|
||||
</material>
|
||||
|
||||
<!-- Guide Tube -->
|
||||
<material id="6">
|
||||
<density units="macro" value="1.0" />
|
||||
<macroscopic name="GT"/>
|
||||
</material>
|
||||
|
||||
<!-- Water -->
|
||||
<material id="7">
|
||||
<density units="macro" value="1.0" />
|
||||
<macroscopic name="LWTR"/>
|
||||
</material>
|
||||
|
||||
<!-- Control Rod -->
|
||||
<material id="8">
|
||||
<density units="macro" value="1.0" />
|
||||
<macroscopic name="CR"/>
|
||||
</material>
|
||||
|
||||
</materials>
|
||||
383
examples/xml/pincell_multigroup/mg_cross_sections.xml
Normal file
383
examples/xml/pincell_multigroup/mg_cross_sections.xml
Normal file
|
|
@ -0,0 +1,383 @@
|
|||
<?xml version="1.0"?>
|
||||
<library>
|
||||
<!-- Before getting to the data, set common information -->
|
||||
<groups> 7 </groups>
|
||||
<group_structure>
|
||||
1E-11 0.0635E-6 10.0E-6 1.0E-4 1.0E-3 0.5 1.0 20.0
|
||||
</group_structure>
|
||||
|
||||
<!--
|
||||
Move on to the data. Each <xsdata> has a unique id and label.
|
||||
-->
|
||||
<xsdata>
|
||||
<!-- Meta data for this data -->
|
||||
<name>UO2.300K</name>
|
||||
<alias>UO2.300K</alias>
|
||||
<kT> 2.53E-8 </kT> <!-- in MeV -->
|
||||
<order>0</order>
|
||||
<fissionable>true</fissionable>
|
||||
<!-- Optional (default is isotropic) -->
|
||||
<representation>isotropic</representation>
|
||||
|
||||
<!-- The data itself, like tallies,
|
||||
goes from low energies (groups) to high energies
|
||||
-->
|
||||
<absorption>
|
||||
8.0248E-03 3.7174E-03 2.6769E-02 9.6236E-02 3.0020E-02 1.1126E-01 2.8278E-01
|
||||
</absorption>
|
||||
|
||||
<nu_fission>
|
||||
2.005998E-02 2.027303E-03 1.570599E-02 4.518301E-02 4.334208E-02 2.020901E-01 5.257105E-01
|
||||
</nu_fission>
|
||||
|
||||
<chi>
|
||||
5.8791E-01 4.1176E-01 3.3906E-04 1.1761E-07 0.0000E+00 0.0000E+00 0.0000E+00
|
||||
</chi>
|
||||
<fission>
|
||||
7.21206E-03 8.19301E-04 6.45320E-03 1.85648E-02 1.78084E-02 8.30348E-02 2.16004E-01
|
||||
</fission>
|
||||
|
||||
<!-- units of MeV/cm -->
|
||||
<!-- If no kappa fission tallies, this is not needed; it will not be loaded
|
||||
if there is no kappa fission scores anyways -->
|
||||
<k_fission>
|
||||
1.0 1.0 1.0 1.0 1.0 1.0 1.0
|
||||
</k_fission>
|
||||
|
||||
<!-- for consistency must include nu-scatter -->
|
||||
<!-- will be a matrix of (order+1) x g_in x g_out -->
|
||||
<scatter>
|
||||
0.1275370 0.0423780 0.0000094 0.0000000 0.0000000 0.0000000 0.0000000
|
||||
0.0000000 0.3244560 0.0016314 0.0000000 0.0000000 0.0000000 0.0000000
|
||||
0.0000000 0.0000000 0.4509400 0.0026792 0.0000000 0.0000000 0.0000000
|
||||
0.0000000 0.0000000 0.0000000 0.4525650 0.0055664 0.0000000 0.0000000
|
||||
0.0000000 0.0000000 0.0000000 0.0001253 0.2714010 0.0102550 0.0000000
|
||||
0.0000000 0.0000000 0.0000000 0.0000000 0.0012968 0.2658020 0.0168090
|
||||
0.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.0085458 0.2730800
|
||||
</scatter>
|
||||
|
||||
<!-- If total is not provided, it will be calculated.
|
||||
However, in the C5G7 problems, we want to use a transport-corrected value
|
||||
so we dont want to have it be calculated -->
|
||||
<total>
|
||||
0.1779492 0.3298048 0.4803882 0.5543674000000001 0.3118013 0.39516779999999996 0.5644058
|
||||
</total>
|
||||
|
||||
</xsdata>
|
||||
|
||||
<xsdata>
|
||||
<!-- Meta data for this data -->
|
||||
<name>MOX1.300K</name>
|
||||
<alias>MOX1.300K</alias>
|
||||
<kT> 2.53E-8 </kT> <!-- in MeV -->
|
||||
<order>0</order>
|
||||
<fissionable>true</fissionable>
|
||||
|
||||
<!-- The data itself, like tallies,
|
||||
goes from low energies (groups) to high energies
|
||||
-->
|
||||
<absorption>
|
||||
8.4339E-03 3.7577E-03 2.7970E-02 1.0421E-01 1.3994E-01 4.0918E-01 4.0935E-01
|
||||
</absorption>
|
||||
<!--
|
||||
Since chi_vector is false, this will be a matrix
|
||||
Matrix is g_in, g_out.
|
||||
This is to show that you can either use a chi vector + nu_fission vector,
|
||||
like in the UO2 data, or a nu_fission matrix like here.
|
||||
-->
|
||||
<nu_fission>
|
||||
1.27888062E-02 8.95701528E-03 7.37557218E-06 2.55837033E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
1.49041240E-03 1.04385401E-03 8.59552023E-07 2.98153464E-10 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
9.56411400E-03 6.69850756E-03 5.51582469E-06 1.91327830E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
3.84928781E-02 2.69596154E-02 2.21996483E-05 7.70040890E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
1.80629998E-02 1.26509513E-02 1.04173100E-05 3.61346022E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
3.91930789E-01 2.74500216E-01 2.26034688E-04 7.84048241E-08 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
4.19762096E-01 2.93992687E-01 2.42085585E-04 8.39724109E-08 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
</nu_fission>
|
||||
|
||||
<fission>
|
||||
7.62704E-03 8.76898E-04 5.69835E-03 2.28872E-02 1.07635E-02 2.32757E-01 2.48968E-01
|
||||
</fission>
|
||||
|
||||
<!-- units of MeV/cm -->
|
||||
<k_fission>
|
||||
1.0 1.0 1.0 1.0 1.0 1.0 1.0
|
||||
</k_fission>
|
||||
|
||||
<!-- for consistency must include nu-scatter -->
|
||||
<!-- will be a matrix of (order+1) x g_in x g_out -->
|
||||
<scatter>
|
||||
1.27537000E-01 4.23780000E-02 9.43740000E-06 5.51630000E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
0.00000000E+00 3.24456000E-01 1.63140000E-03 3.14270000E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
0.00000000E+00 0.00000000E+00 4.50940000E-01 2.67920000E-03 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
0.00000000E+00 0.00000000E+00 0.00000000E+00 4.52565000E-01 5.56640000E-03 0.00000000E+00 0.00000000E+00
|
||||
0.00000000E+00 0.00000000E+00 0.00000000E+00 1.25250000E-04 2.71401000E-01 1.02550000E-02 1.00210000E-08
|
||||
0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 1.29680000E-03 2.65802000E-01 1.68090000E-02
|
||||
0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 8.54580000E-03 2.73080000E-01
|
||||
</scatter>
|
||||
|
||||
<total>
|
||||
0.1783583429163 0.3298451031427 0.4815892 0.5623414 0.421721260021 0.6930878 0.6909757999999999
|
||||
</total>
|
||||
|
||||
</xsdata>
|
||||
|
||||
<xsdata>
|
||||
<!-- Meta data for this data -->
|
||||
<name>MOX2.300K</name>
|
||||
<alias>MOX2.300K</alias>
|
||||
<kT> 2.53E-8 </kT> <!-- in MeV -->
|
||||
<order>0</order>
|
||||
<fissionable>true</fissionable>
|
||||
|
||||
<!-- The data itself, like tallies,
|
||||
goes from low energies (groups) to high energies
|
||||
-->
|
||||
<absorption>
|
||||
0.0090657 0.0042967 0.032881 0.12203 0.18298 0.56846 0.58521
|
||||
</absorption>
|
||||
<!--
|
||||
Since chi_vector is false, this will be a matrix
|
||||
Matrix is g_in, g_out !!! Need to get these values looking right to match
|
||||
output of a nu-fission tally with <energy> filter above <energyout> filter
|
||||
-->
|
||||
<nu_fission>
|
||||
1.40004593E-02 9.80563205E-03 8.07435789E-06 2.80075866E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
2.26856185E-03 1.58885378E-03 1.30832709E-06 4.53820413E-10 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
1.41886199E-02 9.93741584E-03 8.18287404E-06 2.83839974E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
5.54788444E-02 3.88562347E-02 3.19958106E-05 1.10984111E-08 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
2.69085702E-02 1.88462058E-02 1.55187355E-05 5.38299559E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
5.45687127E-01 3.82187973E-01 3.14709185E-04 1.09163414E-07 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
6.13307712E-01 4.29548032E-01 3.53707392E-04 1.22690752E-07 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
</nu_fission>
|
||||
|
||||
<fission>
|
||||
0.00825446 0.00132565 0.00842156 0.032873 0.0159636 0.323794 0.362803
|
||||
</fission>
|
||||
|
||||
<!-- units of MeV/cm -->
|
||||
<k_fission>
|
||||
1.0 1.0 1.0 1.0 1.0 1.0 1.0
|
||||
</k_fission>
|
||||
|
||||
<!-- for consistency must include nu-scatter -->
|
||||
<!-- will be a matrix of (order+1) x g_in x g_out -->
|
||||
<scatter>
|
||||
1.30457000E-01 4.17920000E-02 8.51050000E-06 5.13290000E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
0.00000000E+00 3.28428000E-01 1.64360000E-03 2.20170000E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
0.00000000E+00 0.00000000E+00 4.58371000E-01 2.53310000E-03 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
0.00000000E+00 0.00000000E+00 0.00000000E+00 4.63709000E-01 5.47660000E-03 0.00000000E+00 0.00000000E+00
|
||||
0.00000000E+00 0.00000000E+00 0.00000000E+00 1.76190000E-04 2.82313000E-01 8.72890000E-03 9.00160000E-09
|
||||
0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 2.27600000E-03 2.49751000E-01 1.31140000E-02
|
||||
0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 8.86450000E-03 2.59529000E-01
|
||||
</scatter>
|
||||
|
||||
<total>
|
||||
0.1813232156329 0.3343683022017 0.4937851 0.5912156 0.47419809900160004 0.833601 0.8536035
|
||||
</total>
|
||||
|
||||
</xsdata>
|
||||
|
||||
<xsdata>
|
||||
<!-- Meta data for this data -->
|
||||
<name>MOX3.300K</name>
|
||||
<alias>MOX3.300K</alias>
|
||||
<kT> 2.53E-8 </kT> <!-- in MeV -->
|
||||
<order>0</order>
|
||||
<fissionable>true</fissionable>
|
||||
|
||||
<!-- The data itself, like tallies,
|
||||
goes from low energies (groups) to high energies
|
||||
-->
|
||||
<absorption>
|
||||
9.48620000E-03 4.65560000E-03 3.62400000E-02 1.32720000E-01 2.08400000E-01 6.58700000E-01 6.90170000E-01
|
||||
</absorption>
|
||||
<!--
|
||||
Since chi_vector is false, this will be a matrix
|
||||
Matrix is g_in, g_out !!! Need to get these values looking right to match
|
||||
output of a nu-fission tally with <energy> filter above <energyout> filter
|
||||
-->
|
||||
<nu_fission>
|
||||
1.48071013E-02 1.03705874E-02 8.53956516E-06 2.96212546E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
2.78640474E-03 1.95154023E-03 1.60697792E-06 5.57413653E-10 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
1.73304404E-02 1.21378819E-02 9.99482763E-06 3.46691346E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
6.59928975E-02 4.62200600E-02 3.80594850E-05 1.32017225E-08 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
3.25131926E-02 2.27715674E-02 1.87510386E-05 6.50418701E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
6.32002662E-01 4.42641588E-01 3.64489161E-04 1.26430632E-07 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
7.28595687E-01 5.10293344E-01 4.20196380E-04 1.45753838E-07 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
</nu_fission>
|
||||
|
||||
<fission>
|
||||
8.67209000E-03 1.62426000E-03 1.02716000E-02 3.90447000E-02 1.92576000E-02 3.74888000E-01 4.30599000E-01
|
||||
</fission>
|
||||
|
||||
<!-- units of MeV/cm -->
|
||||
<k_fission>
|
||||
1.0 1.0 1.0 1.0 1.0 1.0 1.0
|
||||
</k_fission>
|
||||
|
||||
<!-- for consistency must include nu-scatter -->
|
||||
<!-- will be a matrix of (order+1) x g_in x g_out -->
|
||||
<scatter>
|
||||
1.31504000E-01 4.20460000E-02 8.69720000E-06 5.19380000E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
0.00000000E+00 3.30403000E-01 1.64630000E-03 2.60060000E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
0.00000000E+00 0.00000000E+00 4.61792000E-01 2.47490000E-03 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
0.00000000E+00 0.00000000E+00 0.00000000E+00 4.68021000E-01 5.43300000E-03 0.00000000E+00 0.00000000E+00
|
||||
0.00000000E+00 0.00000000E+00 0.00000000E+00 1.85970000E-04 2.85771000E-01 8.39730000E-03 8.92800000E-09
|
||||
0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 2.39160000E-03 2.47614000E-01 1.23220000E-02
|
||||
0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 8.96810000E-03 2.56093000E-01
|
||||
</scatter>
|
||||
|
||||
<total>
|
||||
1.83044902E-01 3.36704903E-01 5.00506900E-01 6.06174000E-01 5.02754279E-01 9.21027600E-01 9.55231100E-01
|
||||
</total>
|
||||
|
||||
</xsdata>
|
||||
|
||||
<xsdata>
|
||||
<!-- Meta data for this data -->
|
||||
<name>FC.300K</name>
|
||||
<alias>FC.300K</alias>
|
||||
<kT> 2.53E-8 </kT> <!-- in MeV -->
|
||||
<order>0</order>
|
||||
<fissionable>true</fissionable>
|
||||
|
||||
<!-- The data itself, like tallies,
|
||||
goes from low energies (groups) to high energies
|
||||
-->
|
||||
<absorption>
|
||||
5.1132E-04 7.5813E-05 3.1643E-04 1.1675E-03 3.3977E-03 9.1886E-03 2.3244E-02
|
||||
</absorption>
|
||||
|
||||
<nu_fission>
|
||||
1.323401E-08 1.434500E-08 1.128599E-06 1.276299E-05 3.538502E-07 1.740099E-06 5.063302E-06
|
||||
</nu_fission>
|
||||
|
||||
<chi>
|
||||
5.8791E-01 4.1176E-01 3.3906E-04 1.1761E-07 0.0000E+00 0.0000E+00 0.0000E+00
|
||||
</chi>
|
||||
|
||||
<fission>
|
||||
4.79002E-09 5.82564E-09 4.63719E-07 5.24406E-06 1.45390E-07 7.14972E-07 2.08041E-06
|
||||
</fission>
|
||||
|
||||
<!-- units of MeV/cm -->
|
||||
<k_fission>
|
||||
1.0 1.0 1.0 1.0 1.0 1.0 1.0
|
||||
</k_fission>
|
||||
|
||||
<!-- for consistency must include nu-scatter -->
|
||||
<!-- will be a matrix of (order+1) x g_in x g_out -->
|
||||
<scatter>
|
||||
6.61659000E-02 5.90700000E-02 2.83340000E-04 1.46220000E-06 2.06420000E-08 0.00000000E00 0.00000000E00
|
||||
0.00000000E00 2.40377000E-01 5.24350000E-02 2.49900000E-04 1.92390000E-05 2.98750000E-06 4.21400000E-07
|
||||
0.00000000E00 0.00000000E00 1.83425000E-01 9.22880000E-02 6.93650000E-03 1.07900000E-03 2.05430000E-04
|
||||
0.00000000E00 0.00000000E00 0.00000000E00 7.90769000E-02 1.69990000E-01 2.58600000E-02 4.92560000E-03
|
||||
0.00000000E00 0.00000000E00 0.00000000E00 3.73400000E-05 9.97570000E-02 2.06790000E-01 2.44780000E-02
|
||||
0.00000000E00 0.00000000E00 0.00000000E00 0.00000000E00 9.17420000E-04 3.16774000E-01 2.38760000E-01
|
||||
0.00000000E00 0.00000000E00 0.00000000E00 0.00000000E00 0.00000000E00 4.97930000E-02 1.09910000E00
|
||||
</scatter>
|
||||
|
||||
<total>
|
||||
1.26032048E-01 2.93160367E-01 2.84250824E-01 2.81025244E-01 3.34460185E-01 5.65640735E-01 1.17213908E00
|
||||
</total>
|
||||
|
||||
</xsdata>
|
||||
|
||||
<xsdata>
|
||||
<!-- Meta data for this data -->
|
||||
<name>GT.300K</name>
|
||||
<alias>GT.300K</alias>
|
||||
<kT> 2.53E-8 </kT> <!-- in MeV -->
|
||||
<order>0</order>
|
||||
<fissionable>false</fissionable>
|
||||
|
||||
<!-- The data itself, like tallies,
|
||||
goes from low energies (groups) to high energies
|
||||
-->
|
||||
<absorption>
|
||||
5.11320000E-04 7.58010000E-05 3.15720000E-04 1.15820000E-03 3.39750000E-03 9.18780000E-03 2.32420000E-02
|
||||
</absorption>
|
||||
|
||||
<!-- for consistency must include nu-scatter -->
|
||||
<!-- will be a matrix of (order+1) x g_in x g_out -->
|
||||
<scatter>
|
||||
6.61659000E-02 5.90700000E-02 2.83340000E-04 1.46220000E-06 2.06420000E-08 0.00000000E+00 0.00000000E+00
|
||||
0.00000000E+00 2.40377000E-01 5.24350000E-02 2.49900000E-04 1.92390000E-05 2.98750000E-06 4.21400000E-07
|
||||
0.00000000E+00 0.00000000E+00 1.83297000E-01 9.23970000E-02 6.94460000E-03 1.08030000E-03 2.05670000E-04
|
||||
0.00000000E+00 0.00000000E+00 0.00000000E+00 7.88511000E-02 1.70140000E-01 2.58810000E-02 4.92970000E-03
|
||||
0.00000000E+00 0.00000000E+00 0.00000000E+00 3.73330000E-05 9.97372000E-02 2.06790000E-01 2.44780000E-02
|
||||
0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 9.17260000E-04 3.16765000E-01 2.38770000E-01
|
||||
0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 4.97920000E-02 1.09912000E+00
|
||||
</scatter>
|
||||
|
||||
<total>
|
||||
1.26032043E-01 2.93160349E-01 2.84240290E-01 2.80960000E-01 3.34440033E-01 5.65640060E-01 1.17215400E+00
|
||||
</total>
|
||||
</xsdata>
|
||||
|
||||
<xsdata>
|
||||
<!-- Meta data for this data -->
|
||||
<name>LWTR.300K</name>
|
||||
<alias>LWTR.300K</alias>
|
||||
<kT> 2.53E-8 </kT> <!-- in MeV -->
|
||||
<order>0</order>
|
||||
<fissionable>false</fissionable>
|
||||
|
||||
<!-- The data itself, like tallies,
|
||||
goes from low energies (groups) to high energies
|
||||
-->
|
||||
<absorption>
|
||||
6.0105E-04 1.5793E-05 3.3716E-04 1.9406E-03 5.7416E-03 1.5001E-02 3.7239E-02
|
||||
</absorption>
|
||||
|
||||
<!-- for consistency must include nu-scatter -->
|
||||
<!-- will be a matrix of (order+1) x g_in x g_out -->
|
||||
<scatter>
|
||||
0.0444777 0.1134000 0.0007235 0.0000037 0.0000001 0.0000000 0.0000000
|
||||
0.0000000 0.2823340 0.1299400 0.0006234 0.0000480 0.0000074 0.0000010
|
||||
0.0000000 0.0000000 0.3452560 0.2245700 0.0169990 0.0026443 0.0005034
|
||||
0.0000000 0.0000000 0.0000000 0.0910284 0.4155100 0.0637320 0.0121390
|
||||
0.0000000 0.0000000 0.0000000 0.0000714 0.1391380 0.5118200 0.0612290
|
||||
0.0000000 0.0000000 0.0000000 0.0000000 0.0022157 0.6999130 0.5373200
|
||||
0.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.1324400 2.4807000
|
||||
</scatter>
|
||||
|
||||
<total>
|
||||
0.15920605 0.41296959299999997 0.59030986 0.5843499999999999 0.7180000000000001 1.2544497000000001 2.650379
|
||||
</total>
|
||||
|
||||
</xsdata>
|
||||
|
||||
<xsdata>
|
||||
<!-- Meta data for this data -->
|
||||
<name>CR.300K</name>
|
||||
<alias>CR.300K</alias>
|
||||
<kT> 2.53E-8 </kT> <!-- in MeV -->
|
||||
<order>0</order>
|
||||
<fissionable>false</fissionable>
|
||||
|
||||
<!-- The data itself, like tallies,
|
||||
goes from low energies (groups) to high energies
|
||||
-->
|
||||
<absorption>
|
||||
1.70490000E-03 8.36224000E-03 8.37901000E-02 3.97797000E-01 6.98763000E-01 9.29508000E-01 1.17836000E+00
|
||||
</absorption>
|
||||
|
||||
<!-- for consistency must include nu-scatter -->
|
||||
<!-- will be a matrix of (order+1) x g_in x g_out -->
|
||||
<scatter>
|
||||
1.70563000E-01 4.44012000E-02 9.83670000E-05 1.27786000E-07 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
0.00000000E+00 4.71050000E-01 6.85480000E-04 3.91395000E-10 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
0.00000000E+00 0.00000000E+00 8.01859000E-01 7.20132000E-04 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
0.00000000E+00 0.00000000E+00 0.00000000E+00 5.70752000E-01 1.46015000E-03 0.00000000E+00 0.00000000E+00
|
||||
0.00000000E+00 0.00000000E+00 0.00000000E+00 6.55562000E-05 2.07838000E-01 3.81486000E-03 3.69760000E-09
|
||||
0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 1.02427000E-03 2.02465000E-01 4.75290000E-03
|
||||
0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 3.53043000E-03 6.58597000E-01
|
||||
</scatter>
|
||||
|
||||
<total>
|
||||
2.16767595E-01 4.80097720E-01 8.86369232E-01 9.70009150E-01 9.10481420E-01 1.13775017E+00 1.84048743E+00
|
||||
</total>
|
||||
</xsdata>
|
||||
</library>
|
||||
28
examples/xml/pincell_multigroup/plots.xml
Normal file
28
examples/xml/pincell_multigroup/plots.xml
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0"?>
|
||||
<plots>
|
||||
|
||||
<plot>
|
||||
<id>1</id>
|
||||
<filename>mat</filename>
|
||||
<color>material</color>
|
||||
<origin>0 0 0</origin>
|
||||
<width>1.26 1.26</width>
|
||||
<type>slice</type>
|
||||
<pixels>1000 1000 </pixels>
|
||||
<col_spec id="1" rgb="255 0 0" />
|
||||
<col_spec id="2" rgb="0 0 0" />
|
||||
<col_spec id="3" rgb="0 255 0" />
|
||||
<col_spec id="4" rgb="0 0 255" />
|
||||
</plot>
|
||||
|
||||
<plot>
|
||||
<id>2</id>
|
||||
<filename>cell</filename>
|
||||
<color>cell</color>
|
||||
<origin>0 0 0</origin>
|
||||
<width>1.26 1.26</width>
|
||||
<type>slice</type>
|
||||
<pixels>1000 1000 </pixels>
|
||||
</plot>
|
||||
|
||||
</plots>
|
||||
40
examples/xml/pincell_multigroup/settings.xml
Normal file
40
examples/xml/pincell_multigroup/settings.xml
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
<?xml version="1.0"?>
|
||||
<settings>
|
||||
|
||||
<energy_mode>multi-group</energy_mode>
|
||||
|
||||
<!--
|
||||
Define how many particles to run and for how many batches
|
||||
in an eigenvalue calculation mode
|
||||
-->
|
||||
<eigenvalue>
|
||||
<batches>100</batches>
|
||||
<inactive>10</inactive>
|
||||
<particles>1000</particles>
|
||||
</eigenvalue>
|
||||
|
||||
<!--
|
||||
Start with uniformally distributed neutron source
|
||||
with the default energy spectrum of a Maxwellian
|
||||
and isotropic distribution.
|
||||
-->
|
||||
<source>
|
||||
<space type="box">
|
||||
<parameters>
|
||||
-0.63 -0.63 -1E50
|
||||
0.63 0.63 1E50
|
||||
</parameters>
|
||||
</space>
|
||||
</source>
|
||||
|
||||
<output>
|
||||
<cross_sections>true</cross_sections>
|
||||
<summary>true</summary>
|
||||
<tallies>true</tallies>
|
||||
</output>
|
||||
|
||||
<survival_biasing>false</survival_biasing>
|
||||
|
||||
<cross_sections>./mg_cross_sections.xml</cross_sections>
|
||||
|
||||
</settings>
|
||||
13
examples/xml/pincell_multigroup/tallies.xml
Normal file
13
examples/xml/pincell_multigroup/tallies.xml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0"?>
|
||||
<tallies>
|
||||
<mesh id="1" type="regular">
|
||||
<dimension>100 100 1</dimension>
|
||||
<lower_left>-0.63 -0.63 -1e+50</lower_left>
|
||||
<upper_right>0.63 0.63 1e+50</upper_right>
|
||||
</mesh>
|
||||
<tally id="1" name="tally 1">
|
||||
<filter bins="1e-11 6.35e-08 1e-05 0.0001 0.001 0.5 1.0 20.0" type="energy" />
|
||||
<filter bins="1" type="mesh" />
|
||||
<scores>flux fission nu-fission</scores>
|
||||
</tally>
|
||||
</tallies>
|
||||
|
|
@ -1,11 +1,13 @@
|
|||
from openmc.element import *
|
||||
from openmc.geometry import *
|
||||
from openmc.nuclide import *
|
||||
from openmc.macroscopic import *
|
||||
from openmc.material import *
|
||||
from openmc.plots import *
|
||||
from openmc.settings import *
|
||||
from openmc.surface import *
|
||||
from openmc.universe import *
|
||||
from openmc.mgxs_library import *
|
||||
from openmc.mesh import *
|
||||
from openmc.filter import *
|
||||
from openmc.trigger import *
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import sys
|
||||
import copy
|
||||
from numbers import Integral
|
||||
from collections import Iterable
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
|
@ -14,7 +16,7 @@ if sys.version_info[0] >= 3:
|
|||
_TALLY_ARITHMETIC_OPS = ['+', '-', '*', '/', '^']
|
||||
|
||||
# Acceptable tally aggregation operations
|
||||
_TALLY_AGGREGATE_OPS = ['sum', 'mean']
|
||||
_TALLY_AGGREGATE_OPS = ['sum', 'avg']
|
||||
|
||||
|
||||
class CrossScore(object):
|
||||
|
|
@ -186,6 +188,23 @@ class CrossNuclide(object):
|
|||
return existing
|
||||
|
||||
def __repr__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
@property
|
||||
def left_nuclide(self):
|
||||
return self._left_nuclide
|
||||
|
||||
@property
|
||||
def right_nuclide(self):
|
||||
return self._right_nuclide
|
||||
|
||||
@property
|
||||
def binary_op(self):
|
||||
return self._binary_op
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
|
||||
string = ''
|
||||
|
||||
|
|
@ -207,18 +226,6 @@ class CrossNuclide(object):
|
|||
|
||||
return string
|
||||
|
||||
@property
|
||||
def left_nuclide(self):
|
||||
return self._left_nuclide
|
||||
|
||||
@property
|
||||
def right_nuclide(self):
|
||||
return self._right_nuclide
|
||||
|
||||
@property
|
||||
def binary_op(self):
|
||||
return self._binary_op
|
||||
|
||||
@left_nuclide.setter
|
||||
def left_nuclide(self, left_nuclide):
|
||||
cv.check_type('left_nuclide', left_nuclide,
|
||||
|
|
@ -430,7 +437,7 @@ class CrossFilter(object):
|
|||
filter_index = left_index * self.right_filter.num_bins + right_index
|
||||
return filter_index
|
||||
|
||||
def get_pandas_dataframe(self, datasize, summary=None):
|
||||
def get_pandas_dataframe(self, data_size, summary=None):
|
||||
"""Builds a Pandas DataFrame for the CrossFilter's bins.
|
||||
|
||||
This method constructs a Pandas DataFrame object for the CrossFilter
|
||||
|
|
@ -445,7 +452,7 @@ class CrossFilter(object):
|
|||
|
||||
Parameters
|
||||
----------
|
||||
datasize : Integral
|
||||
data_size : Integral
|
||||
The total number of bins in the tally corresponding to this filter
|
||||
summary : None or Summary
|
||||
An optional Summary object to be used to construct columns for
|
||||
|
|
@ -472,19 +479,18 @@ class CrossFilter(object):
|
|||
|
||||
# If left and right filters are identical, do not combine bins
|
||||
if self.left_filter == self.right_filter:
|
||||
df = self.left_filter.get_pandas_dataframe(datasize, summary)
|
||||
df = self.left_filter.get_pandas_dataframe(data_size, summary)
|
||||
|
||||
# If left and right filters are different, combine their bins
|
||||
else:
|
||||
left_df = self.left_filter.get_pandas_dataframe(datasize, summary)
|
||||
right_df = self.right_filter.get_pandas_dataframe(datasize, summary)
|
||||
left_df = self.left_filter.get_pandas_dataframe(data_size, summary)
|
||||
right_df = self.right_filter.get_pandas_dataframe(data_size, summary)
|
||||
left_df = left_df.astype(str)
|
||||
right_df = right_df.astype(str)
|
||||
df = '(' + left_df + ' ' + self.binary_op + ' ' + right_df + ')'
|
||||
|
||||
return df
|
||||
|
||||
|
||||
class AggregateScore(object):
|
||||
"""A special-purpose tally score used to encapsulate an aggregate of a
|
||||
subset or all of tally's scores for tally aggregation.
|
||||
|
|
@ -494,7 +500,7 @@ class AggregateScore(object):
|
|||
scores : Iterable of str or CrossScore
|
||||
The scores included in the aggregation
|
||||
aggregate_op : str
|
||||
The tally aggregation operator (e.g., 'sum', 'mean', etc.) used
|
||||
The tally aggregation operator (e.g., 'sum', 'avg', etc.) used
|
||||
to aggregate across a tally's scores with this AggregateScore
|
||||
|
||||
Attributes
|
||||
|
|
@ -502,7 +508,7 @@ class AggregateScore(object):
|
|||
scores : Iterable of str or CrossScore
|
||||
The scores included in the aggregation
|
||||
aggregate_op : str
|
||||
The tally aggregation operator (e.g., 'sum', 'mean', etc.) used
|
||||
The tally aggregation operator (e.g., 'sum', 'avg', etc.) used
|
||||
to aggregate across a tally's scores with this AggregateScore
|
||||
|
||||
"""
|
||||
|
|
@ -556,10 +562,16 @@ class AggregateScore(object):
|
|||
def aggregate_op(self):
|
||||
return self._aggregate_op
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
|
||||
# Append each score in the aggregate to the string
|
||||
string = '(' + ', '.join(self.scores) + ')'
|
||||
return string
|
||||
|
||||
@scores.setter
|
||||
def scores(self, scores):
|
||||
cv.check_iterable_type('scores', scores,
|
||||
(basestring, CrossScore, AggregateScore))
|
||||
cv.check_iterable_type('scores', scores, basestring)
|
||||
self._scores = scores
|
||||
|
||||
@aggregate_op.setter
|
||||
|
|
@ -578,7 +590,7 @@ class AggregateNuclide(object):
|
|||
nuclides : Iterable of str or Nuclide or CrossNuclide
|
||||
The nuclides included in the aggregation
|
||||
aggregate_op : str
|
||||
The tally aggregation operator (e.g., 'sum', 'mean', etc.) used
|
||||
The tally aggregation operator (e.g., 'sum', 'avg', etc.) used
|
||||
to aggregate across a tally's nuclides with this AggregateNuclide
|
||||
|
||||
Attributes
|
||||
|
|
@ -586,7 +598,7 @@ class AggregateNuclide(object):
|
|||
nuclides : Iterable of str or Nuclide or CrossNuclide
|
||||
The nuclides included in the aggregation
|
||||
aggregate_op : str
|
||||
The tally aggregation operator (e.g., 'sum', 'mean', etc.) used
|
||||
The tally aggregation operator (e.g., 'sum', 'avg', etc.) used
|
||||
to aggregate across a tally's nuclides with this AggregateNuclide
|
||||
|
||||
"""
|
||||
|
|
@ -644,10 +656,19 @@ class AggregateNuclide(object):
|
|||
def aggregate_op(self):
|
||||
return self._aggregate_op
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
|
||||
# Append each nuclide in the aggregate to the string
|
||||
names = [nuclide.name if isinstance(nuclide, Nuclide) else str(nuclide)
|
||||
for nuclide in self.nuclides]
|
||||
string = '(' + ', '.join(map(str, names)) + ')'
|
||||
return string
|
||||
|
||||
@nuclides.setter
|
||||
def nuclides(self, nuclides):
|
||||
cv.check_iterable_type('nuclides', nuclides,
|
||||
(basestring, Nuclide, CrossNuclide, AggregateNuclide))
|
||||
(basestring, Nuclide, CrossNuclide))
|
||||
self._nuclides = nuclides
|
||||
|
||||
@aggregate_op.setter
|
||||
|
|
@ -668,7 +689,7 @@ class AggregateFilter(object):
|
|||
bins : Iterable of tuple
|
||||
The filter bins included in the aggregation
|
||||
aggregate_op : str
|
||||
The tally aggregation operator (e.g., 'sum', 'mean', etc.) used
|
||||
The tally aggregation operator (e.g., 'sum', 'avg', etc.) used
|
||||
to aggregate across a tally filter's bins with this AggregateFilter
|
||||
|
||||
Attributes
|
||||
|
|
@ -678,7 +699,7 @@ class AggregateFilter(object):
|
|||
aggregate_filter : filter
|
||||
The filter included in the aggregation
|
||||
aggregate_op : str
|
||||
The tally aggregation operator (e.g., 'sum', 'mean', etc.) used
|
||||
The tally aggregation operator (e.g., 'sum', 'avg', etc.) used
|
||||
to aggregate across a tally filter's bins with this AggregateFilter
|
||||
bins : Iterable of tuple
|
||||
The filter bins included in the aggregation
|
||||
|
|
@ -715,6 +736,21 @@ class AggregateFilter(object):
|
|||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
||||
def __gt__(self, other):
|
||||
if self.type != other.type:
|
||||
if self.aggregate_filter.type in _FILTER_TYPES and \
|
||||
other.aggregate_filter.type in _FILTER_TYPES:
|
||||
delta = _FILTER_TYPES.index(self.aggregate_filter.type) - \
|
||||
_FILTER_TYPES.index(other.aggregate_filter.type)
|
||||
return delta > 0
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
def __lt__(self, other):
|
||||
return not self > other
|
||||
|
||||
def __repr__(self):
|
||||
string = 'AggregateFilter\n'
|
||||
string += '{0: <16}{1}{2}\n'.format('\tType', '=\t', self.type)
|
||||
|
|
@ -759,7 +795,7 @@ class AggregateFilter(object):
|
|||
|
||||
@property
|
||||
def num_bins(self):
|
||||
return 1 if self.aggregate_filter else 0
|
||||
return len(self.bins) if self.aggregate_filter else 0
|
||||
|
||||
@property
|
||||
def stride(self):
|
||||
|
|
@ -776,14 +812,13 @@ class AggregateFilter(object):
|
|||
|
||||
@aggregate_filter.setter
|
||||
def aggregate_filter(self, aggregate_filter):
|
||||
cv.check_type('aggregate_filter', aggregate_filter,
|
||||
(Filter, CrossFilter, AggregateFilter))
|
||||
cv.check_type('aggregate_filter', aggregate_filter, (Filter, CrossFilter))
|
||||
self._aggregate_filter = aggregate_filter
|
||||
|
||||
@bins.setter
|
||||
def bins(self, bins):
|
||||
cv.check_iterable_type('bins', bins, (Integral, tuple))
|
||||
self._bins = bins
|
||||
cv.check_iterable_type('bins', bins, Iterable)
|
||||
self._bins = list(map(tuple, bins))
|
||||
|
||||
@aggregate_op.setter
|
||||
def aggregate_op(self, aggregate_op):
|
||||
|
|
@ -823,15 +858,14 @@ class AggregateFilter(object):
|
|||
|
||||
"""
|
||||
|
||||
if filter_bin not in self.bins and \
|
||||
filter_bin != self._aggregate_filter.bins:
|
||||
if filter_bin not in self.bins:
|
||||
msg = 'Unable to get the bin index for AggregateFilter since ' \
|
||||
'"{0}" is not one of the bins'.format(filter_bin)
|
||||
raise ValueError(msg)
|
||||
else:
|
||||
return 0
|
||||
return self.bins.index(filter_bin)
|
||||
|
||||
def get_pandas_dataframe(self, datasize, summary=None):
|
||||
def get_pandas_dataframe(self, data_size, summary=None):
|
||||
"""Builds a Pandas DataFrame for the AggregateFilter's bins.
|
||||
|
||||
This method constructs a Pandas DataFrame object for the AggregateFilter
|
||||
|
|
@ -840,7 +874,7 @@ class AggregateFilter(object):
|
|||
|
||||
Parameters
|
||||
----------
|
||||
datasize : Integral
|
||||
data_size : Integral
|
||||
The total number of bins in the tally corresponding to this filter
|
||||
summary : None or Summary
|
||||
An optional Summary object to be used to construct columns for
|
||||
|
|
@ -868,14 +902,80 @@ class AggregateFilter(object):
|
|||
|
||||
import pandas as pd
|
||||
|
||||
# Construct a sring representing the filter aggregation
|
||||
aggregate_bin = '{0}('.format(self.aggregate_op)
|
||||
aggregate_bin += ', '.join(map(str, self.bins)) + ')'
|
||||
# Create NumPy array of the bin tuples for repeating / tiling
|
||||
filter_bins = np.empty(self.num_bins, dtype=tuple)
|
||||
for i, bin in enumerate(self.bins):
|
||||
filter_bins[i] = bin
|
||||
|
||||
# Construct NumPy array of bin repeated for each element in dataframe
|
||||
aggregate_bin_array = np.array([aggregate_bin])
|
||||
aggregate_bin_array = np.repeat(aggregate_bin_array, datasize)
|
||||
# Repeat and tile bins as needed for DataFrame
|
||||
filter_bins = np.repeat(filter_bins, self.stride)
|
||||
tile_factor = data_size / len(filter_bins)
|
||||
filter_bins = np.tile(filter_bins, tile_factor)
|
||||
|
||||
# Construct Pandas DataFrame for the AggregateFilter
|
||||
df = pd.DataFrame({self.type: aggregate_bin_array})
|
||||
# Create DataFrame with aggregated bins
|
||||
df = pd.DataFrame({self.type: filter_bins})
|
||||
return df
|
||||
|
||||
def can_merge(self, other):
|
||||
"""Determine if AggregateFilter can be merged with another.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
other : AggregateFilter
|
||||
Filter to compare with
|
||||
|
||||
Returns
|
||||
-------
|
||||
bool
|
||||
Whether the filter can be merged
|
||||
|
||||
"""
|
||||
|
||||
if not isinstance(other, AggregateFilter):
|
||||
return False
|
||||
|
||||
# Filters must be of the same type
|
||||
elif self.type != other.type:
|
||||
return False
|
||||
|
||||
# None of the bins in this filter should match in the other filter
|
||||
for bin in self.bins:
|
||||
if bin in other.bins:
|
||||
return False
|
||||
|
||||
# If all conditional checks passed then filters are mergeable
|
||||
return True
|
||||
|
||||
def merge(self, other):
|
||||
"""Merge this aggregatefilter with another.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
other : AggregateFilter
|
||||
Filter to merge with
|
||||
|
||||
Returns
|
||||
-------
|
||||
merged_filter : AggregateFilter
|
||||
Filter resulting from the merge
|
||||
|
||||
"""
|
||||
|
||||
if not self.can_merge(other):
|
||||
msg = 'Unable to merge "{0}" with "{1}" ' \
|
||||
'filters'.format(self.type, other.type)
|
||||
raise ValueError(msg)
|
||||
|
||||
# Create deep copy of filter to return as merged filter
|
||||
merged_filter = copy.deepcopy(self)
|
||||
|
||||
# Merge unique filter bins
|
||||
merged_bins = self.bins + other.bins
|
||||
|
||||
# Sort energy bin edges
|
||||
if 'energy' in self.type:
|
||||
merged_bins = sorted(merged_bins)
|
||||
|
||||
# Assign merged bins to merged filter
|
||||
merged_filter.bins = list(merged_bins)
|
||||
return merged_filter
|
||||
|
|
|
|||
|
|
@ -41,25 +41,36 @@ def check_type(name, value, expected_type, expected_iter_type=None):
|
|||
Description of value being checked
|
||||
value : object
|
||||
Object to check type of
|
||||
expected_type : type
|
||||
expected_type : type or Iterable of type
|
||||
type to check object against
|
||||
expected_iter_type : type or None, optional
|
||||
expected_iter_type : type or Iterable of type or None, optional
|
||||
Expected type of each element in value, assuming it is iterable. If
|
||||
None, no check will be performed.
|
||||
|
||||
"""
|
||||
|
||||
if not _isinstance(value, expected_type):
|
||||
msg = 'Unable to set "{0}" to "{1}" which is not of type "{2}"'.format(
|
||||
name, value, expected_type.__name__)
|
||||
if isinstance(expected_type, Iterable):
|
||||
msg = 'Unable to set "{0}" to "{1}" which is not one of the ' \
|
||||
'following types: "{2}"'.format(name, value, ', '.join(
|
||||
[t.__name__ for t in expected_type]))
|
||||
else:
|
||||
msg = 'Unable to set "{0}" to "{1}" which is not of type "{2}"'.format(
|
||||
name, value, expected_type.__name__)
|
||||
raise ValueError(msg)
|
||||
|
||||
if expected_iter_type:
|
||||
for item in value:
|
||||
if not _isinstance(item, expected_iter_type):
|
||||
msg = 'Unable to set "{0}" to "{1}" since each item must be ' \
|
||||
'of type "{2}"'.format(name, value,
|
||||
expected_iter_type.__name__)
|
||||
if isinstance(expected_iter_type, Iterable):
|
||||
msg = 'Unable to set "{0}" to "{1}" since each item must be ' \
|
||||
'one of the following types: "{2}"'.format(
|
||||
name, value, ', '.join([t.__name__ for t in
|
||||
expected_iter_type]))
|
||||
else:
|
||||
msg = 'Unable to set "{0}" to "{1}" since each item must be ' \
|
||||
'of type "{2}"'.format(name, value,
|
||||
expected_iter_type.__name__)
|
||||
raise ValueError(msg)
|
||||
|
||||
|
||||
|
|
@ -127,7 +138,7 @@ def check_iterable_type(name, value, expected_type, min_depth=1, max_depth=1):
|
|||
# But first, have we exceeded the max depth?
|
||||
if len(tree) > max_depth:
|
||||
msg = 'Error setting {0}: Found an iterable at {1}, items '\
|
||||
'in that iterable excceed the maximum depth of {2}' \
|
||||
'in that iterable exceed the maximum depth of {2}' \
|
||||
.format(name, ind_str, max_depth)
|
||||
raise ValueError(msg)
|
||||
|
||||
|
|
@ -245,3 +256,50 @@ def check_greater_than(name, value, minimum, equality=False):
|
|||
msg = 'Unable to set "{0}" to "{1}" since it is less than ' \
|
||||
'or equal to "{2}"'.format(name, value, minimum)
|
||||
raise ValueError(msg)
|
||||
|
||||
|
||||
class CheckedList(list):
|
||||
"""A list for which each element is type-checked as it's added
|
||||
|
||||
Parameters
|
||||
----------
|
||||
expected_type : type or Iterable of type
|
||||
Type(s) which each element should be
|
||||
name : str
|
||||
Name of data being checked
|
||||
items : Iterable, optional
|
||||
Items to initialize the list with
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, expected_type, name, items=[]):
|
||||
self.expected_type = expected_type
|
||||
self.name = name
|
||||
for item in items:
|
||||
self.append(item)
|
||||
|
||||
def append(self, item):
|
||||
"""Append item to list
|
||||
|
||||
Parameters
|
||||
----------
|
||||
item : object
|
||||
Item to append
|
||||
|
||||
"""
|
||||
check_type(self.name, item, self.expected_type)
|
||||
super(CheckedList, self).append(item)
|
||||
|
||||
def insert(self, index, item):
|
||||
"""Insert item before index
|
||||
|
||||
Parameters
|
||||
----------
|
||||
index : int
|
||||
Index in list
|
||||
item : object
|
||||
Item to insert
|
||||
|
||||
"""
|
||||
check_type(self.name, item, self.expected_type)
|
||||
super(CheckedList, self).insert(index, item)
|
||||
|
|
|
|||
|
|
@ -57,6 +57,15 @@ class Element(object):
|
|||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
||||
def __gt__(self, other):
|
||||
return repr(self) > repr(other)
|
||||
|
||||
def __lt__(self, other):
|
||||
return not self > other
|
||||
|
||||
def __hash__(self):
|
||||
return hash(repr(self))
|
||||
|
||||
def __hash__(self):
|
||||
return hash(repr(self))
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,8 @@ class Executor(object):
|
|||
# Launch a subprocess to run OpenMC
|
||||
p = subprocess.Popen(command, shell=True,
|
||||
cwd=self._working_directory,
|
||||
stdout=subprocess.PIPE)
|
||||
stdout=subprocess.PIPE,
|
||||
universal_newlines=True)
|
||||
|
||||
# Capture and re-print OpenMC output in real-time
|
||||
while True:
|
||||
|
|
|
|||
103
openmc/filter.py
103
openmc/filter.py
|
|
@ -77,6 +77,25 @@ class Filter(object):
|
|||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
||||
def __gt__(self, other):
|
||||
if self.type != other.type:
|
||||
if self.type in _FILTER_TYPES and other.type in _FILTER_TYPES:
|
||||
delta = _FILTER_TYPES.index(self.type) - \
|
||||
_FILTER_TYPES.index(other.type)
|
||||
return delta > 0
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
# Compare largest/smallest energy bin edges in energy filters
|
||||
# This logic is used when merging tallies with energy filters
|
||||
if 'energy' in self.type and 'energy' in other.type:
|
||||
return self.bins[0] >= other.bins[-1]
|
||||
else:
|
||||
return max(self.bins) > max(other.bins)
|
||||
|
||||
def __lt__(self, other):
|
||||
return not self > other
|
||||
|
||||
def __hash__(self):
|
||||
return hash(repr(self))
|
||||
|
||||
|
|
@ -246,20 +265,28 @@ class Filter(object):
|
|||
return False
|
||||
|
||||
# Filters must be of the same type
|
||||
elif self.type != other.type:
|
||||
if self.type != other.type:
|
||||
return False
|
||||
|
||||
# Distribcell filters cannot have more than one bin
|
||||
elif self.type == 'distribcell':
|
||||
if self.type == 'distribcell':
|
||||
return False
|
||||
|
||||
# Mesh filters cannot have more than one bin
|
||||
elif self.type == 'mesh':
|
||||
return False
|
||||
|
||||
# Different energy bins are not mergeable
|
||||
# Different energy bins structures must be mutually exclusive and
|
||||
# share only one shared bin edge at the minimum or maximum energy
|
||||
elif 'energy' in self.type:
|
||||
return False
|
||||
# This low energy edge coincides with other's high energy edge
|
||||
if self.bins[0] == other.bins[-1]:
|
||||
return True
|
||||
# This high energy edge coincides with other's low energy edge
|
||||
elif self.bins[-1] == other.bins[0]:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
else:
|
||||
return True
|
||||
|
|
@ -288,9 +315,21 @@ class Filter(object):
|
|||
merged_filter = copy.deepcopy(self)
|
||||
|
||||
# Merge unique filter bins
|
||||
merged_bins = list(set(np.concatenate((self.bins, other.bins))))
|
||||
merged_filter.bins = merged_bins
|
||||
merged_filter.num_bins = len(merged_bins)
|
||||
merged_bins = np.concatenate((self.bins, other.bins))
|
||||
merged_bins = np.unique(merged_bins)
|
||||
|
||||
# Sort energy bin edges
|
||||
if 'energy' in self.type:
|
||||
merged_bins = sorted(merged_bins)
|
||||
|
||||
# Assign merged bins to merged filter
|
||||
merged_filter.bins = list(merged_bins)
|
||||
|
||||
# Count bins in the merged filter
|
||||
if 'energy' in merged_filter.type:
|
||||
merged_filter.num_bins = len(merged_bins) - 1
|
||||
else:
|
||||
merged_filter.num_bins = len(merged_bins)
|
||||
|
||||
return merged_filter
|
||||
|
||||
|
|
@ -502,9 +541,9 @@ class Filter(object):
|
|||
2. separate columns for the cell IDs, universe IDs, and lattice IDs
|
||||
and x,y,z cell indices corresponding to each (with summary info).
|
||||
|
||||
For 'energy' and 'energyout' filters, the DataFrame include a single
|
||||
column with each element comprising a string with the lower, upper
|
||||
energy bounds for each filter bin.
|
||||
For 'energy' and 'energyout' filters, the DataFrame includes one
|
||||
column for the lower energy bound and one column for the upper
|
||||
energy bound for each filter bin.
|
||||
|
||||
For 'mesh' filters, the DataFrame includes three columns for the
|
||||
x,y,z mesh cell indices corresponding to each filter bin.
|
||||
|
|
@ -521,14 +560,8 @@ class Filter(object):
|
|||
|
||||
"""
|
||||
|
||||
# Attempt to import Pandas
|
||||
try:
|
||||
import pandas as pd
|
||||
except ImportError:
|
||||
msg = 'The Pandas Python package must be installed on your system'
|
||||
raise ImportError(msg)
|
||||
|
||||
# Initialize Pandas DataFrame
|
||||
import pandas as pd
|
||||
df = pd.DataFrame()
|
||||
|
||||
# mesh filters
|
||||
|
|
@ -707,7 +740,6 @@ class Filter(object):
|
|||
filter_bins = np.repeat(filter_bins, self.stride)
|
||||
tile_factor = data_size / len(filter_bins)
|
||||
filter_bins = np.tile(filter_bins, tile_factor)
|
||||
filter_bins = filter_bins
|
||||
df = pd.DataFrame({self.type : filter_bins})
|
||||
|
||||
# If OpenCG level info DataFrame was created, concatenate
|
||||
|
|
@ -719,21 +751,30 @@ class Filter(object):
|
|||
|
||||
# energy, energyout filters
|
||||
elif 'energy' in self.type:
|
||||
bins = self.bins
|
||||
num_bins = self.num_bins
|
||||
# Extract the lower and upper energy bounds, then repeat and tile
|
||||
# them as necessary to account for other filters.
|
||||
lo_bins = np.repeat(self.bins[:-1], self.stride)
|
||||
hi_bins = np.repeat(self.bins[1:], self.stride)
|
||||
tile_factor = data_size / len(lo_bins)
|
||||
lo_bins = np.tile(lo_bins, tile_factor)
|
||||
hi_bins = np.tile(hi_bins, tile_factor)
|
||||
|
||||
# Create strings for
|
||||
template = '({0:.1e} - {1:.1e})'
|
||||
filter_bins = []
|
||||
for i in range(num_bins):
|
||||
filter_bins.append(template.format(bins[i], bins[i+1]))
|
||||
# Add the new energy columns to the DataFrame.
|
||||
df.loc[:, self.type + ' low [MeV]'] = lo_bins
|
||||
df.loc[:, self.type + ' high [MeV]'] = hi_bins
|
||||
|
||||
# Tile the energy bins into a DataFrame column
|
||||
filter_bins = np.repeat(filter_bins, self.stride)
|
||||
tile_factor = data_size / len(filter_bins)
|
||||
filter_bins = np.tile(filter_bins, tile_factor)
|
||||
filter_bins = filter_bins
|
||||
df = pd.concat([df, pd.DataFrame({self.type + ' [MeV]' : filter_bins})])
|
||||
elif self.type in ('azimuthal', 'polar'):
|
||||
# Extract the lower and upper angle bounds, then repeat and tile
|
||||
# them as necessary to account for other filters.
|
||||
lo_bins = np.repeat(self.bins[:-1], self.stride)
|
||||
hi_bins = np.repeat(self.bins[1:], self.stride)
|
||||
tile_factor = data_size / len(lo_bins)
|
||||
lo_bins = np.tile(lo_bins, tile_factor)
|
||||
hi_bins = np.tile(hi_bins, tile_factor)
|
||||
|
||||
# Add the new angle columns to the DataFrame.
|
||||
df.loc[:, self.type + ' low'] = lo_bins
|
||||
df.loc[:, self.type + ' high'] = hi_bins
|
||||
|
||||
# universe, material, surface, cell, and cellborn filters
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -65,8 +65,10 @@ class Geometry(object):
|
|||
|
||||
# Find the distribcell index of the cell.
|
||||
cells = self.get_all_cells()
|
||||
if path[-1] in cells:
|
||||
distribcell_index = cells[path[-1]].distribcell_index
|
||||
for cell in cells:
|
||||
if cell.id == path[-1]:
|
||||
distribcell_index = cell.distribcell_index
|
||||
break
|
||||
else:
|
||||
raise RuntimeError('Could not find cell {} specified in a \
|
||||
distribcell filter'.format(path[-1]))
|
||||
|
|
@ -94,7 +96,16 @@ class Geometry(object):
|
|||
|
||||
"""
|
||||
|
||||
return self._root_universe.get_all_cells()
|
||||
all_cells = self._root_universe.get_all_cells()
|
||||
cells = set()
|
||||
|
||||
for cell in all_cells.values():
|
||||
if cell._type == 'normal':
|
||||
cells.add(cell)
|
||||
|
||||
cells = list(cells)
|
||||
cells.sort(key=lambda x: x.id)
|
||||
return cells
|
||||
|
||||
def get_all_universes(self):
|
||||
"""Return all universes defined
|
||||
|
|
@ -106,7 +117,15 @@ class Geometry(object):
|
|||
|
||||
"""
|
||||
|
||||
return self._root_universe.get_all_universes()
|
||||
all_universes = self._root_universe.get_all_universes()
|
||||
universes = set()
|
||||
|
||||
for universe in all_universes.values():
|
||||
universes.add(universe)
|
||||
|
||||
universes = list(universes)
|
||||
universes.sort(key=lambda x: x.id)
|
||||
return universes
|
||||
|
||||
def get_all_nuclides(self):
|
||||
"""Return all nuclides assigned to a material in the geometry
|
||||
|
|
@ -150,10 +169,19 @@ class Geometry(object):
|
|||
return materials
|
||||
|
||||
def get_all_material_cells(self):
|
||||
"""Return all cells filled by a material
|
||||
|
||||
Returns
|
||||
-------
|
||||
list of openmc.universe.Cell
|
||||
Cells filled by Materials in the geometry
|
||||
|
||||
"""
|
||||
|
||||
all_cells = self.get_all_cells()
|
||||
material_cells = set()
|
||||
|
||||
for cell_id, cell in all_cells.items():
|
||||
for cell in all_cells:
|
||||
if cell._type == 'normal':
|
||||
material_cells.add(cell)
|
||||
|
||||
|
|
@ -174,9 +202,9 @@ class Geometry(object):
|
|||
all_universes = self.get_all_universes()
|
||||
material_universes = set()
|
||||
|
||||
for universe_id, universe in all_universes.items():
|
||||
cells = universe._cells
|
||||
for cell_id, cell in cells.items():
|
||||
for universe in all_universes:
|
||||
cells = universe.cells
|
||||
for cell in cells:
|
||||
if cell._type == 'normal':
|
||||
material_universes.add(universe)
|
||||
|
||||
|
|
@ -184,6 +212,227 @@ class Geometry(object):
|
|||
material_universes.sort(key=lambda x: x.id)
|
||||
return material_universes
|
||||
|
||||
def get_all_lattices(self):
|
||||
"""Return all lattices defined
|
||||
|
||||
Returns
|
||||
-------
|
||||
list of openmc.universe.Lattice
|
||||
Lattices in the geometry
|
||||
|
||||
"""
|
||||
|
||||
cells = self.get_all_cells()
|
||||
lattices = set()
|
||||
|
||||
for cell in cells:
|
||||
if isinstance(cell.fill, openmc.Lattice):
|
||||
lattices.add(cell.fill)
|
||||
|
||||
lattices = list(lattices)
|
||||
lattices.sort(key=lambda x: x.id)
|
||||
return lattices
|
||||
|
||||
def get_materials_by_name(self, name, case_sensitive=False, matching=False):
|
||||
"""Return a list of materials with matching names.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
name : str
|
||||
The name to match
|
||||
case_sensitive : bool
|
||||
Whether to distinguish upper and lower case letters in each
|
||||
material's name (default is True)
|
||||
matching : bool
|
||||
Whether the names must match completely (default is True)
|
||||
|
||||
Returns
|
||||
-------
|
||||
list of openmc.material.Material
|
||||
Materials matching the queried name
|
||||
|
||||
"""
|
||||
|
||||
if not case_sensitive:
|
||||
name = name.lower()
|
||||
|
||||
all_materials = self.get_all_materials()
|
||||
materials = set()
|
||||
|
||||
for material in all_materials:
|
||||
material_name = material.name
|
||||
if not case_sensitive:
|
||||
material_name = material_name.lower()
|
||||
|
||||
if material_name == name:
|
||||
materials.add(material)
|
||||
elif not matching and name in material_name:
|
||||
materials.add(material)
|
||||
|
||||
materials = list(materials)
|
||||
materials.sort(key=lambda x: x.id)
|
||||
return materials
|
||||
|
||||
def get_cells_by_name(self, name, case_sensitive=False, matching=False):
|
||||
"""Return a list of cells with matching names.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
name : str
|
||||
The name to search match
|
||||
case_sensitive : bool
|
||||
Whether to distinguish upper and lower case letters in each
|
||||
cell's name (default is True)
|
||||
matching : bool
|
||||
Whether the names must match completely (default is True)
|
||||
|
||||
Returns
|
||||
-------
|
||||
list of openmc.universe.Cell
|
||||
Cells matching the queried name
|
||||
|
||||
"""
|
||||
|
||||
if not case_sensitive:
|
||||
name = name.lower()
|
||||
|
||||
all_cells = self.get_all_cells()
|
||||
cells = set()
|
||||
|
||||
for cell in all_cells:
|
||||
cell_name = cell.name
|
||||
if not case_sensitive:
|
||||
cell_name = cell_name.lower()
|
||||
|
||||
if cell_name == name:
|
||||
cells.add(cell)
|
||||
elif not matching and name in cell_name:
|
||||
cells.add(cell)
|
||||
|
||||
cells = list(cells)
|
||||
cells.sort(key=lambda x: x.id)
|
||||
return cells
|
||||
|
||||
def get_cells_by_fill_name(self, name, case_sensitive=False, matching=False):
|
||||
"""Return a list of cells with fills with matching names.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
name : str
|
||||
The name to match
|
||||
case_sensitive : bool
|
||||
Whether to distinguish upper and lower case letters in each
|
||||
cell's name (default is True)
|
||||
matching : bool
|
||||
Whether the names must match completely (default is True)
|
||||
|
||||
Returns
|
||||
-------
|
||||
list of openmc.universe.Cell
|
||||
Cells with fills matching the queried name
|
||||
|
||||
"""
|
||||
|
||||
if not case_sensitive:
|
||||
name = name.lower()
|
||||
|
||||
all_cells = self.get_all_cells()
|
||||
cells = set()
|
||||
|
||||
for cell in all_cells:
|
||||
cell_fill_name = cell.fill.name
|
||||
if not case_sensitive:
|
||||
cell_fill_name = cell_fill_name.lower()
|
||||
|
||||
if cell_fill_name == name:
|
||||
cells.add(cell)
|
||||
elif not matching and name in cell_fill_name:
|
||||
cells.add(cell)
|
||||
|
||||
cells = list(cells)
|
||||
cells.sort(key=lambda x: x.id)
|
||||
return cells
|
||||
|
||||
def get_universes_by_name(self, name, case_sensitive=False, matching=False):
|
||||
"""Return a list of universes with matching names.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
name : str
|
||||
The name to match
|
||||
case_sensitive : bool
|
||||
Whether to distinguish upper and lower case letters in each
|
||||
universe's name (default is True)
|
||||
matching : bool
|
||||
Whether the names must match completely (default is True)
|
||||
|
||||
Returns
|
||||
-------
|
||||
list of openmc.universe.Universe
|
||||
Universes matching the queried name
|
||||
|
||||
"""
|
||||
|
||||
if not case_sensitive:
|
||||
name = name.lower()
|
||||
|
||||
all_universes = self.get_all_universes()
|
||||
universes = set()
|
||||
|
||||
for universe in all_universes:
|
||||
universe_name = universe.name
|
||||
if not case_sensitive:
|
||||
universe_name = universe_name.lower()
|
||||
|
||||
if universe_name == name:
|
||||
universes.add(universe)
|
||||
elif not matching and name in universe_name:
|
||||
universes.add(universe)
|
||||
|
||||
universes = list(universes)
|
||||
universes.sort(key=lambda x: x.id)
|
||||
return universes
|
||||
|
||||
def get_lattices_by_name(self, name, case_sensitive=False, matching=False):
|
||||
"""Return a list of lattices with matching names.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
name : str
|
||||
The name to match
|
||||
case_sensitive : bool
|
||||
Whether to distinguish upper and lower case letters in each
|
||||
lattice's name (default is True)
|
||||
matching : bool
|
||||
Whether the names must match completely (default is True)
|
||||
|
||||
Returns
|
||||
-------
|
||||
list of openmc.universe.Lattice
|
||||
Lattices matching the queried name
|
||||
|
||||
"""
|
||||
|
||||
if not case_sensitive:
|
||||
name = name.lower()
|
||||
|
||||
all_lattices = self.get_all_lattices()
|
||||
lattices = set()
|
||||
|
||||
for lattice in all_lattices:
|
||||
lattice_name = lattice.name
|
||||
if not case_sensitive:
|
||||
lattice_name = lattice_name.lower()
|
||||
|
||||
if lattice_name == name:
|
||||
lattices.add(lattice)
|
||||
elif not matching and name in lattice_name:
|
||||
lattices.add(lattice)
|
||||
|
||||
lattices = list(lattices)
|
||||
lattices.sort(key=lambda x: x.id)
|
||||
return lattices
|
||||
|
||||
|
||||
class GeometryFile(object):
|
||||
"""Geometry file used for an OpenMC simulation. Corresponds directly to the
|
||||
|
|
|
|||
85
openmc/macroscopic.py
Normal file
85
openmc/macroscopic.py
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
from numbers import Integral
|
||||
import sys
|
||||
|
||||
from openmc.checkvalue import check_type
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
basestring = str
|
||||
|
||||
|
||||
class Macroscopic(object):
|
||||
"""A Macroscopic object that can be used in a material.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
name : str
|
||||
Name of the macroscopic data, e.g. UO2
|
||||
xs : str
|
||||
Cross section identifier, e.g. 71c
|
||||
|
||||
Attributes
|
||||
----------
|
||||
name : str
|
||||
Name of the nuclide, e.g. UO2
|
||||
xs : str
|
||||
Cross section identifier, e.g. 71c
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, name='', xs=None):
|
||||
# Initialize class attributes
|
||||
self._name = ''
|
||||
self._xs = None
|
||||
|
||||
# Set the Material class attributes
|
||||
self.name = name
|
||||
|
||||
if xs is not None:
|
||||
self.xs = xs
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, Macroscopic):
|
||||
if self._name != other._name:
|
||||
return False
|
||||
elif self._xs != other._xs:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
elif isinstance(other, basestring) and other == self.name:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
||||
def __hash__(self):
|
||||
return hash((self._name, self._xs))
|
||||
|
||||
def __repr__(self):
|
||||
string = 'Nuclide - {0}\n'.format(self._name)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tXS', '=\t', self._xs)
|
||||
return string
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def xs(self):
|
||||
return self._xs
|
||||
|
||||
@name.setter
|
||||
def name(self, name):
|
||||
check_type('name', name, basestring)
|
||||
self._name = name
|
||||
|
||||
@xs.setter
|
||||
def xs(self, xs):
|
||||
check_type('cross-section identifier', xs, basestring)
|
||||
self._xs = xs
|
||||
|
||||
def __repr__(self):
|
||||
string = 'Macroscopic - {0}\n'.format(self._name)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tXS', '=\t', self.xs)
|
||||
return string
|
||||
|
|
@ -22,14 +22,15 @@ def reset_auto_material_id():
|
|||
|
||||
|
||||
# Units for density supported by OpenMC
|
||||
DENSITY_UNITS = ['g/cm3', 'g/cc', 'kg/cm3', 'atom/b-cm', 'atom/cm3', 'sum']
|
||||
DENSITY_UNITS = ['g/cm3', 'g/cc', 'kg/cm3', 'atom/b-cm', 'atom/cm3', 'sum',
|
||||
'macro']
|
||||
|
||||
# Constant for density when not needed
|
||||
NO_DENSITY = 99999.
|
||||
|
||||
|
||||
class Material(object):
|
||||
"""A material composed of a collection of nuclides/elements that can be
|
||||
"""A material composed of a collection of nuclides/elements that can be
|
||||
assigned to a region of space.
|
||||
|
||||
Parameters
|
||||
|
|
@ -49,7 +50,8 @@ class Material(object):
|
|||
Density of the material (units defined separately)
|
||||
density_units : str
|
||||
Units used for `density`. Can be one of 'g/cm3', 'g/cc', 'kg/cm3',
|
||||
'atom/b-cm', 'atom/cm3', or 'sum'.
|
||||
'atom/b-cm', 'atom/cm3', 'sum', or 'macro'. The 'macro' unit only
|
||||
applies in the case of a multi-group calculation.
|
||||
|
||||
"""
|
||||
|
||||
|
|
@ -65,6 +67,10 @@ class Material(object):
|
|||
# Values - tuple (nuclide, percent, percent type)
|
||||
self._nuclides = OrderedDict()
|
||||
|
||||
# The single instance of Macroscopic data present in this material
|
||||
# (only one is allowed, hence this is different than _nuclides, etc)
|
||||
self._macroscopic = None
|
||||
|
||||
# An ordered dictionary of Elements (order affects OpenMC results)
|
||||
# Keys - Element names
|
||||
# Values - tuple (element, percent, percent type)
|
||||
|
|
@ -128,6 +134,10 @@ class Material(object):
|
|||
string += '{0: <16}'.format('\t{0}'.format(nuclide))
|
||||
string += '=\t{0: <12} [{1}]\n'.format(percent, percent_type)
|
||||
|
||||
if self._macroscopic is not None:
|
||||
string += '{0: <16}\n'.format('\tMacroscopic Data')
|
||||
string += '{0: <16}'.format('\t{0}'.format(self._macroscopic))
|
||||
|
||||
string += '{0: <16}\n'.format('\tElements')
|
||||
|
||||
for element in self._elements:
|
||||
|
|
@ -149,6 +159,7 @@ class Material(object):
|
|||
clone._density = self._density
|
||||
clone._density_units = self._density_units
|
||||
clone._nuclides = deepcopy(self._nuclides, memo)
|
||||
clone._macroscopic = self._macroscopic
|
||||
clone._elements = deepcopy(self._elements, memo)
|
||||
clone._sab = deepcopy(self._sab, memo)
|
||||
clone._convert_to_distrib_comps = self._convert_to_distrib_comps
|
||||
|
|
@ -268,6 +279,11 @@ class Material(object):
|
|||
|
||||
"""
|
||||
|
||||
if self._macroscopic is not None:
|
||||
msg = 'Unable to add a Nuclide to Material ID="{0}" as a ' \
|
||||
'macroscopic data-set has already been added'.format(self._id)
|
||||
raise ValueError(msg)
|
||||
|
||||
if not isinstance(nuclide, (openmc.Nuclide, str)):
|
||||
msg = 'Unable to add a Nuclide to Material ID="{0}" with a ' \
|
||||
'non-Nuclide value "{1}"'.format(self._id, nuclide)
|
||||
|
|
@ -311,6 +327,64 @@ class Material(object):
|
|||
if nuclide._name in self._nuclides:
|
||||
del self._nuclides[nuclide._name]
|
||||
|
||||
def add_macroscopic(self, macroscopic):
|
||||
"""Add a macroscopic to the material
|
||||
|
||||
Parameters
|
||||
----------
|
||||
macroscopic : str or Macroscopic
|
||||
Macroscopic to add
|
||||
|
||||
"""
|
||||
|
||||
# Ensure no nuclides, elements, or sab are added since these would be
|
||||
# incompatible with macroscopics
|
||||
if self._nuclides or self._elements or self._sab:
|
||||
msg = 'Unable to add a Macroscopic data set to Material ID="{0}" ' \
|
||||
'with a macroscopic value "{1}" as an incompatible data ' \
|
||||
'member (i.e., nuclide, element, or S(a,b) table) ' \
|
||||
'has already been added'.format(self._id, macroscopic)
|
||||
raise ValueError(msg)
|
||||
|
||||
if not isinstance(macroscopic, (openmc.Macroscopic, basestring)):
|
||||
msg = 'Unable to add a Macroscopic to Material ID="{0}" with a ' \
|
||||
'non-Macroscopic value "{1}"'.format(self._id, macroscopic)
|
||||
raise ValueError(msg)
|
||||
|
||||
if isinstance(macroscopic, openmc.Macroscopic):
|
||||
# Copy this Macroscopic to separate it from the Macroscopic in
|
||||
# other Materials
|
||||
macroscopic = deepcopy(macroscopic)
|
||||
else:
|
||||
macroscopic = openmc.Macroscopic(macroscopic)
|
||||
|
||||
if self._macroscopic is None:
|
||||
self._macroscopic = macroscopic
|
||||
else:
|
||||
msg = 'Unable to add a Macroscopic to Material ID="{0}", ' \
|
||||
'Only One Macroscopic allowed per ' \
|
||||
'Material!'.format(self._id, macroscopic)
|
||||
raise ValueError(msg)
|
||||
|
||||
def remove_macroscopic(self, macroscopic):
|
||||
"""Remove a macroscopic from the material
|
||||
|
||||
Parameters
|
||||
----------
|
||||
macroscopic : Macroscopic
|
||||
Macroscopic to remove
|
||||
|
||||
"""
|
||||
|
||||
if not isinstance(macroscopic, openmc.Macroscopic):
|
||||
msg = 'Unable to remove a Macroscopic "{0}" in Material ID="{1}" ' \
|
||||
'since it is not a Macroscopic'.format(self._id, macroscopic)
|
||||
raise ValueError(msg)
|
||||
|
||||
# If the Material contains the Macroscopic, delete it
|
||||
if macroscopic._name == self._macroscopic.name:
|
||||
self._macroscopic = None
|
||||
|
||||
def add_element(self, element, percent, percent_type='ao'):
|
||||
"""Add a natural element to the material
|
||||
|
||||
|
|
@ -325,6 +399,11 @@ class Material(object):
|
|||
|
||||
"""
|
||||
|
||||
if self._macroscopic is not None:
|
||||
msg = 'Unable to add an Element to Material ID="{0}" as a ' \
|
||||
'macroscopic data-set has already been added'.format(self._id)
|
||||
raise ValueError(msg)
|
||||
|
||||
if not isinstance(element, openmc.Element):
|
||||
msg = 'Unable to add an Element to Material ID="{0}" with a ' \
|
||||
'non-Element value "{1}"'.format(self._id, element)
|
||||
|
|
@ -371,6 +450,11 @@ class Material(object):
|
|||
|
||||
"""
|
||||
|
||||
if self._macroscopic is not None:
|
||||
msg = 'Unable to add an S(a,b) table to Material ID="{0}" as a ' \
|
||||
'macroscopic data-set has already been added'.format(self._id)
|
||||
raise ValueError(msg)
|
||||
|
||||
if not isinstance(name, basestring):
|
||||
msg = 'Unable to add an S(a,b) table to Material ID="{0}" with a ' \
|
||||
'non-string table name "{1}"'.format(self._id, name)
|
||||
|
|
@ -427,6 +511,15 @@ class Material(object):
|
|||
|
||||
return xml_element
|
||||
|
||||
def _get_macroscopic_xml(self, macroscopic):
|
||||
xml_element = ET.Element("macroscopic")
|
||||
xml_element.set("name", macroscopic._name)
|
||||
|
||||
if macroscopic.xs is not None:
|
||||
xml_element.set("xs", macroscopic.xs)
|
||||
|
||||
return xml_element
|
||||
|
||||
def _get_element_xml(self, element, distrib=False):
|
||||
xml_element = ET.Element("element")
|
||||
xml_element.set("name", str(element[0]._name))
|
||||
|
|
@ -482,14 +575,19 @@ class Material(object):
|
|||
subelement.set("units", self._density_units)
|
||||
|
||||
if not self._convert_to_distrib_comps:
|
||||
# Create nuclide XML subelements
|
||||
subelements = self._get_nuclides_xml(self._nuclides)
|
||||
for subelement in subelements:
|
||||
element.append(subelement)
|
||||
if self._macroscopic is None:
|
||||
# Create nuclide XML subelements
|
||||
subelements = self._get_nuclides_xml(self._nuclides)
|
||||
for subelement in subelements:
|
||||
element.append(subelement)
|
||||
|
||||
# Create element XML subelements
|
||||
subelements = self._get_elements_xml(self._elements)
|
||||
for subelement in subelements:
|
||||
# Create element XML subelements
|
||||
subelements = self._get_elements_xml(self._elements)
|
||||
for subelement in subelements:
|
||||
element.append(subelement)
|
||||
else:
|
||||
# Create macroscopic XML subelements
|
||||
subelement = self._get_macroscopic_xml(self._macroscopic)
|
||||
element.append(subelement)
|
||||
|
||||
else:
|
||||
|
|
@ -516,15 +614,21 @@ class Material(object):
|
|||
subsubelement = ET.SubElement(subelement, "otf_file_path")
|
||||
subsubelement.text = self._distrib_otf_file
|
||||
|
||||
# Create nuclide XML subelements
|
||||
subelements = self.get_nuclides_xml(self._nuclides, distrib=True)
|
||||
for subelement_nuc in subelements:
|
||||
subelement.append(subelement_nuc)
|
||||
if self._macroscopic is None:
|
||||
# Create nuclide XML subelements
|
||||
subelements = self.get_nuclides_xml(self._nuclides, distrib=True)
|
||||
for subelement_nuc in subelements:
|
||||
subelement.append(subelement_nuc)
|
||||
|
||||
# Create element XML subelements
|
||||
subelements = self._get_elements_xml(self._elements, distrib=True)
|
||||
for subelement_ele in subelements:
|
||||
subelement.append(subelement_ele)
|
||||
# Create element XML subelements
|
||||
subelements = self._get_elements_xml(self._elements, distrib=True)
|
||||
for subsubelement in subelements:
|
||||
subelement.append(subsubelement)
|
||||
else:
|
||||
# Create macroscopic XML subelements
|
||||
subsubelement = self._get_macroscopic_xml(self._macroscopic,
|
||||
distrib=True)
|
||||
subelement.append(subsubelement)
|
||||
|
||||
if len(self._sab) > 0:
|
||||
for sab in self._sab:
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ class EnergyGroups(object):
|
|||
----------
|
||||
group_edges : Iterable of Real
|
||||
The energy group boundaries [MeV]
|
||||
num_group : Integral
|
||||
num_groups : Integral
|
||||
The number of energy groups
|
||||
|
||||
"""
|
||||
|
|
@ -54,10 +54,12 @@ class EnergyGroups(object):
|
|||
def __eq__(self, other):
|
||||
if not isinstance(other, EnergyGroups):
|
||||
return False
|
||||
elif self.group_edges != other.group_edges:
|
||||
elif self.num_groups != other.num_groups:
|
||||
return False
|
||||
else:
|
||||
elif np.allclose(self.group_edges, other.group_edges):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
|
@ -236,3 +238,64 @@ class EnergyGroups(object):
|
|||
condensed_groups.group_edges = group_edges
|
||||
|
||||
return condensed_groups
|
||||
|
||||
def can_merge(self, other):
|
||||
"""Determine if energy groups can be merged with another.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
other : EnergyGroups
|
||||
EnergyGroups to compare with
|
||||
|
||||
Returns
|
||||
-------
|
||||
bool
|
||||
Whether the energy groups can be merged
|
||||
|
||||
"""
|
||||
|
||||
if not isinstance(other, EnergyGroups):
|
||||
return False
|
||||
|
||||
# If the energy group structures match then groups are mergeable
|
||||
if self == other:
|
||||
return True
|
||||
|
||||
# This low energy edge coincides with other's high energy edge
|
||||
if self.group_edges[0] == other.group_edges[-1]:
|
||||
return True
|
||||
# This high energy edge coincides with other's low energy edge
|
||||
elif self.group_edges[-1] == other.group_edges[0]:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def merge(self, other):
|
||||
"""Merge this energy groups with another.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
other : EnergyGroups
|
||||
EnergyGroups to merge with
|
||||
|
||||
Returns
|
||||
-------
|
||||
merged_groups : EnergyGroups
|
||||
EnergyGroups resulting from the merge
|
||||
|
||||
"""
|
||||
|
||||
if not self.can_merge(other):
|
||||
raise ValueError('Unable to merge energy groups')
|
||||
|
||||
# Create deep copy to return as merged energy groups
|
||||
merged_groups = copy.deepcopy(self)
|
||||
|
||||
# Merge unique filter bins
|
||||
merged_edges = np.concatenate((self.group_edges, other.group_edges))
|
||||
merged_edges = np.unique(merged_edges)
|
||||
merged_edges = sorted(merged_edges)
|
||||
|
||||
# Assign merged edges to merged groups
|
||||
merged_groups.group_edges = list(merged_edges)
|
||||
return merged_groups
|
||||
|
|
|
|||
|
|
@ -116,11 +116,11 @@ class Library(object):
|
|||
clone._by_nuclide = self.by_nuclide
|
||||
clone._mgxs_types = self.mgxs_types
|
||||
clone._domain_type = self.domain_type
|
||||
clone._domains = self.domains
|
||||
clone._domains = copy.deepcopy(self.domains)
|
||||
clone._correction = self.correction
|
||||
clone._energy_groups = copy.deepcopy(self.energy_groups, memo)
|
||||
clone._tally_trigger = copy.deepcopy(self.tally_trigger, memo)
|
||||
clone._all_mgxs = self.all_mgxs
|
||||
clone._all_mgxs = copy.deepcopy(self.all_mgxs)
|
||||
clone._sp_filename = self._sp_filename
|
||||
clone._keff = self._keff
|
||||
clone._sparse = self.sparse
|
||||
|
|
@ -426,7 +426,7 @@ class Library(object):
|
|||
----------
|
||||
domain : Material or Cell or Universe or Integral
|
||||
The material, cell, or universe object of interest (or its ID)
|
||||
mgxs_type : {'total', 'transport', 'absorption', 'capture', 'fission', 'nu-fission', 'scatter', 'nu-scatter', 'scatter matrix', 'nu-scatter matrix', 'chi'}
|
||||
mgxs_type : {'total', 'transport', 'absorption', 'capture', 'fission', 'nu-fission', 'kappa-fission', 'scatter', 'nu-scatter', 'scatter matrix', 'nu-scatter matrix', 'chi'}
|
||||
The type of multi-group cross section object to return
|
||||
|
||||
Returns
|
||||
|
|
@ -457,7 +457,7 @@ class Library(object):
|
|||
break
|
||||
else:
|
||||
msg = 'Unable to find MGXS for {0} "{1}" in ' \
|
||||
'library'.format(self.domain_type, domain)
|
||||
'library'.format(self.domain_type, domain_id)
|
||||
raise ValueError(msg)
|
||||
else:
|
||||
domain_id = domain.id
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ MGXS_TYPES = ['total',
|
|||
'capture',
|
||||
'fission',
|
||||
'nu-fission',
|
||||
'kappa-fission',
|
||||
'scatter',
|
||||
'nu-scatter',
|
||||
'scatter matrix',
|
||||
|
|
@ -66,6 +67,10 @@ class MGXS(object):
|
|||
The energy group structure for energy condensation
|
||||
by_nuclide : bool
|
||||
If true, computes cross sections for each nuclide in domain
|
||||
nuclides : Iterable of basestring
|
||||
The user-specified nuclides to compute cross sections. If by_nuclide
|
||||
is True but nuclides are not specified by the user, all nuclides in the
|
||||
spatial domain will be used.
|
||||
name : str, optional
|
||||
Name of the multi-group cross section. Used as a label to identify
|
||||
tallies in OpenMC 'tallies.xml' file.
|
||||
|
|
@ -110,6 +115,8 @@ class MGXS(object):
|
|||
sparse : bool
|
||||
Whether or not the MGXS' tallies use SciPy's LIL sparse matrix format
|
||||
for compressed data storage
|
||||
derived : bool
|
||||
Whether or not the MGXS is merged from one or more other MGXS
|
||||
|
||||
"""
|
||||
|
||||
|
|
@ -122,6 +129,7 @@ class MGXS(object):
|
|||
self._name = ''
|
||||
self._rxn_type = None
|
||||
self._by_nuclide = None
|
||||
self._nuclides = None
|
||||
self._domain = None
|
||||
self._domain_type = None
|
||||
self._energy_groups = None
|
||||
|
|
@ -130,6 +138,7 @@ class MGXS(object):
|
|||
self._rxn_rate_tally = None
|
||||
self._xs_tally = None
|
||||
self._sparse = False
|
||||
self._derived = False
|
||||
|
||||
self.name = name
|
||||
self.by_nuclide = by_nuclide
|
||||
|
|
@ -150,6 +159,7 @@ class MGXS(object):
|
|||
clone._name = self.name
|
||||
clone._rxn_type = self.rxn_type
|
||||
clone._by_nuclide = self.by_nuclide
|
||||
clone._nuclides = copy.deepcopy(self._nuclides)
|
||||
clone._domain = self.domain
|
||||
clone._domain_type = self.domain_type
|
||||
clone._energy_groups = copy.deepcopy(self.energy_groups, memo)
|
||||
|
|
@ -157,6 +167,7 @@ class MGXS(object):
|
|||
clone._rxn_rate_tally = copy.deepcopy(self._rxn_rate_tally, memo)
|
||||
clone._xs_tally = copy.deepcopy(self._xs_tally, memo)
|
||||
clone._sparse = self.sparse
|
||||
clone._derived = self.derived
|
||||
|
||||
clone._tallies = OrderedDict()
|
||||
for tally_type, tally in self.tallies.items():
|
||||
|
|
@ -231,8 +242,7 @@ class MGXS(object):
|
|||
|
||||
@property
|
||||
def num_subdomains(self):
|
||||
tally = list(self.tallies.values())[0]
|
||||
domain_filter = tally.find_filter(self.domain_type)
|
||||
domain_filter = self.xs_tally.find_filter(self.domain_type)
|
||||
return domain_filter.num_bins
|
||||
|
||||
@property
|
||||
|
|
@ -249,6 +259,10 @@ class MGXS(object):
|
|||
else:
|
||||
return 'sum'
|
||||
|
||||
@property
|
||||
def derived(self):
|
||||
return self._derived
|
||||
|
||||
@name.setter
|
||||
def name(self, name):
|
||||
cv.check_type('name', name, basestring)
|
||||
|
|
@ -259,6 +273,11 @@ class MGXS(object):
|
|||
cv.check_type('by_nuclide', by_nuclide, bool)
|
||||
self._by_nuclide = by_nuclide
|
||||
|
||||
@nuclides.setter
|
||||
def nuclides(self, nuclides):
|
||||
cv.check_iterable_type('nuclides', nuclides, basestring)
|
||||
self._nuclides = nuclides
|
||||
|
||||
@domain.setter
|
||||
def domain(self, domain):
|
||||
cv.check_type('domain', domain, tuple(_DOMAINS))
|
||||
|
|
@ -315,7 +334,7 @@ class MGXS(object):
|
|||
|
||||
Parameters
|
||||
----------
|
||||
mgxs_type : {'total', 'transport', 'absorption', 'capture', 'fission', 'nu-fission', 'scatter', 'nu-scatter', 'scatter matrix', 'nu-scatter matrix', 'chi'}
|
||||
mgxs_type : {'total', 'transport', 'absorption', 'capture', 'fission', 'nu-fission', 'kappa-fission', 'scatter', 'nu-scatter', 'scatter matrix', 'nu-scatter matrix', 'chi'}
|
||||
The type of multi-group cross section object to return
|
||||
domain : Material or Cell or Universe
|
||||
The domain for spatial homogenization
|
||||
|
|
@ -352,6 +371,8 @@ class MGXS(object):
|
|||
mgxs = FissionXS(domain, domain_type, energy_groups)
|
||||
elif mgxs_type == 'nu-fission':
|
||||
mgxs = NuFissionXS(domain, domain_type, energy_groups)
|
||||
elif mgxs_type == 'kappa-fission':
|
||||
mgxs = KappaFissionXS(domain, domain_type, energy_groups)
|
||||
elif mgxs_type == 'scatter':
|
||||
mgxs = ScatterXS(domain, domain_type, energy_groups)
|
||||
elif mgxs_type == 'nu-scatter':
|
||||
|
|
@ -386,8 +407,14 @@ class MGXS(object):
|
|||
if self.domain is None:
|
||||
raise ValueError('Unable to get all nuclides without a domain')
|
||||
|
||||
nuclides = self.domain.get_all_nuclides()
|
||||
return nuclides.keys()
|
||||
# If the user defined nuclides, return them
|
||||
if self._nuclides:
|
||||
return self._nuclides
|
||||
|
||||
# Otherwise, return all nuclides in the spatial domain
|
||||
else:
|
||||
nuclides = self.domain.get_all_nuclides()
|
||||
return nuclides.keys()
|
||||
|
||||
def get_nuclide_density(self, nuclide):
|
||||
"""Get the atomic number density in units of atoms/b-cm for a nuclide
|
||||
|
|
@ -510,27 +537,27 @@ class MGXS(object):
|
|||
# Create each Tally needed to compute the multi group cross section
|
||||
for score, key, filters in zip(scores, keys, all_filters):
|
||||
self.tallies[key] = openmc.Tally(name=self.name)
|
||||
self.tallies[key].add_score(score)
|
||||
self.tallies[key].scores = [score]
|
||||
self.tallies[key].estimator = estimator
|
||||
self.tallies[key].add_filter(domain_filter)
|
||||
self.tallies[key].filters = [domain_filter]
|
||||
|
||||
# If a tally trigger was specified, add it to each tally
|
||||
if self.tally_trigger:
|
||||
trigger_clone = copy.deepcopy(self.tally_trigger)
|
||||
trigger_clone.add_score(score)
|
||||
self.tallies[key].add_trigger(trigger_clone)
|
||||
trigger_clone.scores = [score]
|
||||
self.tallies[key].triggers.append(trigger_clone)
|
||||
|
||||
# Add all non-domain specific Filters (e.g., 'energy') to the Tally
|
||||
for add_filter in filters:
|
||||
self.tallies[key].add_filter(add_filter)
|
||||
self.tallies[key].filters.append(add_filter)
|
||||
|
||||
# If this is a by-nuclide cross-section, add all nuclides to Tally
|
||||
if self.by_nuclide and score != 'flux':
|
||||
all_nuclides = self.domain.get_all_nuclides()
|
||||
for nuclide in all_nuclides:
|
||||
self.tallies[key].add_nuclide(nuclide)
|
||||
self.tallies[key].nuclides.append(nuclide)
|
||||
else:
|
||||
self.tallies[key].add_nuclide('total')
|
||||
self.tallies[key].nuclides.append('total')
|
||||
|
||||
def _compute_xs(self):
|
||||
"""Performs generic cleanup after a subclass' uses tally arithmetic to
|
||||
|
|
@ -550,9 +577,9 @@ class MGXS(object):
|
|||
# If computing xs for each nuclide, replace CrossNuclides with originals
|
||||
if self.by_nuclide:
|
||||
self.xs_tally._nuclides = []
|
||||
nuclides = self.domain.get_all_nuclides()
|
||||
nuclides = self.get_all_nuclides()
|
||||
for nuclide in nuclides:
|
||||
self.xs_tally.add_nuclide(openmc.Nuclide(nuclide))
|
||||
self.xs_tally.nuclides.append(openmc.Nuclide(nuclide))
|
||||
|
||||
# Remove NaNs which may have resulted from divide-by-zero operations
|
||||
self.xs_tally._mean = np.nan_to_num(self.xs_tally.mean)
|
||||
|
|
@ -679,7 +706,7 @@ class MGXS(object):
|
|||
|
||||
# Construct a collection of the domain filter bins
|
||||
if not isinstance(subdomains, basestring):
|
||||
cv.check_iterable_type('subdomains', subdomains, Integral)
|
||||
cv.check_iterable_type('subdomains', subdomains, Integral, max_depth=2)
|
||||
for subdomain in subdomains:
|
||||
filters.append(self.domain_type)
|
||||
filter_bins.append((subdomain,))
|
||||
|
|
@ -852,19 +879,181 @@ class MGXS(object):
|
|||
|
||||
# Clone this MGXS to initialize the subdomain-averaged version
|
||||
avg_xs = copy.deepcopy(self)
|
||||
avg_xs._rxn_rate_tally = None
|
||||
avg_xs._xs_tally = None
|
||||
|
||||
# Average each of the tallies across subdomains
|
||||
for tally_type, tally in avg_xs.tallies.items():
|
||||
tally_avg = tally.summation(filter_type=self.domain_type,
|
||||
filter_bins=subdomains)
|
||||
avg_xs.tallies[tally_type] = tally_avg
|
||||
if self.derived:
|
||||
avg_xs._rxn_rate_tally = avg_xs.rxn_rate_tally.average(
|
||||
filter_type=self.domain_type, filter_bins=subdomains)
|
||||
else:
|
||||
avg_xs._rxn_rate_tally = None
|
||||
avg_xs._xs_tally = None
|
||||
|
||||
avg_xs._domain_type = 'sum({0})'.format(self.domain_type)
|
||||
# Average each of the tallies across subdomains
|
||||
for tally_type, tally in avg_xs.tallies.items():
|
||||
tally_avg = tally.average(filter_type=self.domain_type,
|
||||
filter_bins=subdomains)
|
||||
avg_xs.tallies[tally_type] = tally_avg
|
||||
|
||||
avg_xs._domain_type = 'avg({0})'.format(self.domain_type)
|
||||
avg_xs.sparse = self.sparse
|
||||
return avg_xs
|
||||
|
||||
def get_slice(self, nuclides=[], groups=[]):
|
||||
"""Build a sliced MGXS for the specified nuclides and energy groups.
|
||||
|
||||
This method constructs a new MGXS to encapsulate a subset of the data
|
||||
represented by this MGXS. The subset of data to include in the tally
|
||||
slice is determined by the nuclides and energy groups specified in
|
||||
the input parameters.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
nuclides : list of str
|
||||
A list of nuclide name strings
|
||||
(e.g., ['U-235', 'U-238']; default is [])
|
||||
groups : list of Integral
|
||||
A list of energy group indices starting at 1 for the high energies
|
||||
(e.g., [1, 2, 3]; default is [])
|
||||
|
||||
Returns
|
||||
-------
|
||||
MGXS
|
||||
A new tally which encapsulates the subset of data requested for the
|
||||
nuclide(s) and/or energy group(s) requested in the parameters.
|
||||
|
||||
"""
|
||||
|
||||
cv.check_iterable_type('nuclides', nuclides, basestring)
|
||||
cv.check_iterable_type('energy_groups', groups, Integral)
|
||||
|
||||
# Build lists of filters and filter bins to slice
|
||||
if len(groups) == 0:
|
||||
filters = []
|
||||
filter_bins = []
|
||||
else:
|
||||
filter_bins = []
|
||||
for group in groups:
|
||||
group_bounds = self.energy_groups.get_group_bounds(group)
|
||||
filter_bins.append(group_bounds)
|
||||
filter_bins = [tuple(filter_bins)]
|
||||
filters = ['energy']
|
||||
|
||||
# Clone this MGXS to initialize the sliced version
|
||||
slice_xs = copy.deepcopy(self)
|
||||
slice_xs._rxn_rate_tally = None
|
||||
slice_xs._xs_tally = None
|
||||
|
||||
# Slice each of the tallies across nuclides and energy groups
|
||||
for tally_type, tally in slice_xs.tallies.items():
|
||||
slice_nuclides = [nuc for nuc in nuclides if nuc in tally.nuclides]
|
||||
if len(groups) != 0 and tally.contains_filter('energy'):
|
||||
tally_slice = tally.get_slice(filters=filters,
|
||||
filter_bins=filter_bins, nuclides=slice_nuclides)
|
||||
else:
|
||||
tally_slice = tally.get_slice(nuclides=slice_nuclides)
|
||||
slice_xs.tallies[tally_type] = tally_slice
|
||||
|
||||
# Assign sliced energy group structure to sliced MGXS
|
||||
if groups:
|
||||
new_group_edges = []
|
||||
for group in groups:
|
||||
group_edges = self.energy_groups.get_group_bounds(group)
|
||||
new_group_edges.extend(group_edges)
|
||||
new_group_edges = np.unique(new_group_edges)
|
||||
slice_xs.energy_groups.group_edges = sorted(new_group_edges)
|
||||
|
||||
# Assign sliced nuclides to sliced MGXS
|
||||
if nuclides:
|
||||
slice_xs.nuclides = nuclides
|
||||
|
||||
slice_xs.sparse = self.sparse
|
||||
return slice_xs
|
||||
|
||||
def can_merge(self, other):
|
||||
"""Determine if another MGXS can be merged with this one
|
||||
|
||||
If results have been loaded from a statepoint, then MGXS are only
|
||||
mergeable along one and only one of enegy groups or nuclides.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
other : MGXS
|
||||
MGXS to check for merging
|
||||
|
||||
"""
|
||||
|
||||
if not isinstance(other, type(self)):
|
||||
return False
|
||||
|
||||
# Compare reaction type, energy groups, nuclides, domain type
|
||||
if self.rxn_type != other.rxn_type:
|
||||
return False
|
||||
elif not self.energy_groups.can_merge(other.energy_groups):
|
||||
return False
|
||||
elif self.by_nuclide != other.by_nuclide:
|
||||
return False
|
||||
elif self.domain_type != other.domain_type:
|
||||
return False
|
||||
elif 'distribcell' not in self.domain_type and self.domain != other.domain:
|
||||
return False
|
||||
elif not self.xs_tally.can_merge(other.xs_tally):
|
||||
return False
|
||||
elif not self.rxn_rate_tally.can_merge(other.rxn_rate_tally):
|
||||
return False
|
||||
|
||||
# If all conditionals pass then MGXS are mergeable
|
||||
return True
|
||||
|
||||
def merge(self, other):
|
||||
"""Merge another MGXS with this one
|
||||
|
||||
MGXS are only mergeable if their energy groups and nuclides are either
|
||||
identical or mutually exclusive. If results have been loaded from a
|
||||
statepoint, then MGXS are only mergeable along one and only one of
|
||||
energy groups or nuclides.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
other : MGXS
|
||||
MGXS to merge with this one
|
||||
|
||||
Returns
|
||||
-------
|
||||
merged_mgxs : MGXS
|
||||
Merged MGXS
|
||||
|
||||
"""
|
||||
|
||||
if not self.can_merge(other):
|
||||
raise ValueError('Unable to merge MGXS')
|
||||
|
||||
# Create deep copy of tally to return as merged tally
|
||||
merged_mgxs = copy.deepcopy(self)
|
||||
merged_mgxs._derived = True
|
||||
|
||||
# Merge energy groups
|
||||
if self.energy_groups != other.energy_groups:
|
||||
merged_groups = self.energy_groups.merge(other.energy_groups)
|
||||
merged_mgxs.energy_groups = merged_groups
|
||||
|
||||
# Merge nuclides
|
||||
if self.nuclides != other.nuclides:
|
||||
|
||||
# The nuclides must be mutually exclusive
|
||||
for nuclide in self.nuclides:
|
||||
if nuclide in other.nuclides:
|
||||
msg = 'Unable to merge MGXS with shared nuclides'
|
||||
raise ValueError(msg)
|
||||
|
||||
# Concatenate lists of nuclides for the merged MGXS
|
||||
merged_mgxs.nuclides = self.nuclides + other.nuclides
|
||||
|
||||
# Null base tallies but merge reaction rate and cross section tallies
|
||||
merged_mgxs._tallies = OrderedDict()
|
||||
merged_mgxs._rxn_rate_tally = self.rxn_rate_tally.merge(other.rxn_rate_tally)
|
||||
merged_mgxs._xs_tally = self.xs_tally.merge(other.xs_tally)
|
||||
|
||||
return merged_mgxs
|
||||
|
||||
def print_xs(self, subdomains='all', nuclides='all', xs_type='macro'):
|
||||
"""Print a string representation for the multi-group cross section.
|
||||
|
||||
|
|
@ -1019,6 +1208,9 @@ class MGXS(object):
|
|||
cv.check_iterable_type('subdomains', subdomains, Integral)
|
||||
elif self.domain_type == 'distribcell':
|
||||
subdomains = np.arange(self.num_subdomains, dtype=np.int)
|
||||
elif self.domain_type == 'avg(distribcell)':
|
||||
domain_filter = self.xs_tally.find_filter('avg(distribcell)')
|
||||
subdomains = domain_filter.bins
|
||||
else:
|
||||
subdomains = [self.domain.id]
|
||||
|
||||
|
|
@ -1232,28 +1424,34 @@ class MGXS(object):
|
|||
# Override energy groups bounds with indices
|
||||
all_groups = np.arange(self.num_groups, 0, -1, dtype=np.int)
|
||||
all_groups = np.repeat(all_groups, self.num_nuclides)
|
||||
if 'energy [MeV]' in df and 'energyout [MeV]' in df:
|
||||
df.rename(columns={'energy [MeV]': 'group in'}, inplace=True)
|
||||
if 'energy low [MeV]' in df and 'energyout low [MeV]' in df:
|
||||
df.rename(columns={'energy low [MeV]': 'group in'},
|
||||
inplace=True)
|
||||
in_groups = np.tile(all_groups, self.num_subdomains)
|
||||
in_groups = np.repeat(in_groups, self.num_groups)
|
||||
in_groups = np.repeat(in_groups, df.shape[0] / in_groups.size)
|
||||
df['group in'] = in_groups
|
||||
del df['energy high [MeV]']
|
||||
|
||||
df.rename(columns={'energyout [MeV]': 'group out'}, inplace=True)
|
||||
out_groups = \
|
||||
np.tile(all_groups, self.num_subdomains * self.num_groups)
|
||||
df.rename(columns={'energyout low [MeV]': 'group out'},
|
||||
inplace=True)
|
||||
out_groups = np.tile(all_groups, df.shape[0] / all_groups.size)
|
||||
df['group out'] = out_groups
|
||||
del df['energyout high [MeV]']
|
||||
columns = ['group in', 'group out']
|
||||
|
||||
elif 'energyout [MeV]' in df:
|
||||
df.rename(columns={'energyout [MeV]': 'group out'}, inplace=True)
|
||||
elif 'energyout low [MeV]' in df:
|
||||
df.rename(columns={'energyout low [MeV]': 'group out'},
|
||||
inplace=True)
|
||||
in_groups = np.tile(all_groups, self.num_subdomains)
|
||||
df['group out'] = in_groups
|
||||
del df['energyout high [MeV]']
|
||||
columns = ['group out']
|
||||
|
||||
elif 'energy [MeV]' in df:
|
||||
df.rename(columns={'energy [MeV]': 'group in'}, inplace=True)
|
||||
elif 'energy low [MeV]' in df:
|
||||
df.rename(columns={'energy low [MeV]': 'group in'}, inplace=True)
|
||||
in_groups = np.tile(all_groups, self.num_subdomains)
|
||||
df['group in'] = in_groups
|
||||
del df['energy high [MeV]']
|
||||
columns = ['group in']
|
||||
|
||||
# Select out those groups the user requested
|
||||
|
|
@ -1275,8 +1473,7 @@ class MGXS(object):
|
|||
|
||||
# Sort the dataframe by domain type id (e.g., distribcell id) and
|
||||
# energy groups such that data is from fast to thermal
|
||||
df.sort([self.domain_type] + columns, inplace=True)
|
||||
|
||||
df.sort_values(by=[self.domain_type] + columns, inplace=True)
|
||||
return df
|
||||
|
||||
|
||||
|
|
@ -1319,7 +1516,7 @@ class TotalXS(MGXS):
|
|||
|
||||
@property
|
||||
def rxn_rate_tally(self):
|
||||
if self._rxn_rate_tally is None:
|
||||
if self._rxn_rate_tally is None :
|
||||
self._rxn_rate_tally = self.tallies['total']
|
||||
self._rxn_rate_tally.sparse = self.sparse
|
||||
return self._rxn_rate_tally
|
||||
|
|
@ -1477,96 +1674,80 @@ class CaptureXS(MGXS):
|
|||
self._rxn_rate_tally.sparse = self.sparse
|
||||
return self._rxn_rate_tally
|
||||
|
||||
class FissionXSBase(MGXS):
|
||||
"""A fission production multi-group cross section base class
|
||||
for NuFission and KappaFission
|
||||
"""
|
||||
|
||||
class FissionXS(MGXS):
|
||||
# This is an abstract class which cannot be instantiated
|
||||
__metaclass__ = abc.ABCMeta
|
||||
|
||||
def __init__(self, rxn_type, domain=None, domain_type=None,
|
||||
groups=None, by_nuclide=False, name=''):
|
||||
super(FissionXSBase, self).__init__(domain, domain_type,
|
||||
groups, by_nuclide, name)
|
||||
self._rxn_type = rxn_type
|
||||
|
||||
@property
|
||||
def tallies(self):
|
||||
"""Construct the OpenMC tallies needed to compute this cross section.
|
||||
|
||||
This method constructs two tracklength tallies to compute the 'flux'
|
||||
and 'rxn_type' reaction rates in the spatial domain and energy
|
||||
groups of interest.
|
||||
|
||||
"""
|
||||
|
||||
# Instantiate tallies if they do not exist
|
||||
if self._tallies is None:
|
||||
|
||||
# Create a list of scores for each Tally to be created
|
||||
scores = ['flux', self._rxn_type]
|
||||
estimator = 'tracklength'
|
||||
keys = scores
|
||||
|
||||
# Create the non-domain specific Filters for the Tallies
|
||||
group_edges = self.energy_groups.group_edges
|
||||
energy_filter = openmc.Filter('energy', group_edges)
|
||||
filters = [[energy_filter], [energy_filter]]
|
||||
|
||||
# Initialize the Tallies
|
||||
self._create_tallies(scores, filters, keys, estimator)
|
||||
|
||||
return self._tallies
|
||||
|
||||
@property
|
||||
def rxn_rate_tally(self):
|
||||
if self._rxn_rate_tally is None:
|
||||
self._rxn_rate_tally = self.tallies[self._rxn_type]
|
||||
self._rxn_rate_tally.sparse = self.sparse
|
||||
return self._rxn_rate_tally
|
||||
|
||||
|
||||
class FissionXS(FissionXSBase):
|
||||
"""A fission multi-group cross section."""
|
||||
|
||||
def __init__(self, domain=None, domain_type=None,
|
||||
groups=None, by_nuclide=False, name=''):
|
||||
super(FissionXS, self).__init__(domain, domain_type,
|
||||
super(FissionXS, self).__init__('fission', domain, domain_type,
|
||||
groups, by_nuclide, name)
|
||||
self._rxn_type = 'fission'
|
||||
|
||||
@property
|
||||
def tallies(self):
|
||||
"""Construct the OpenMC tallies needed to compute this cross section.
|
||||
|
||||
This method constructs two tracklength tallies to compute the 'flux'
|
||||
and 'fission' reaction rates in the spatial domain and energy
|
||||
groups of interest.
|
||||
|
||||
"""
|
||||
|
||||
# Instantiate tallies if they do not exist
|
||||
if self._tallies is None:
|
||||
|
||||
# Create a list of scores for each Tally to be created
|
||||
scores = ['flux', 'fission']
|
||||
estimator = 'tracklength'
|
||||
keys = scores
|
||||
|
||||
# Create the non-domain specific Filters for the Tallies
|
||||
group_edges = self.energy_groups.group_edges
|
||||
energy_filter = openmc.Filter('energy', group_edges)
|
||||
filters = [[energy_filter], [energy_filter]]
|
||||
|
||||
# Initialize the Tallies
|
||||
self._create_tallies(scores, filters, keys, estimator)
|
||||
|
||||
return self._tallies
|
||||
|
||||
@property
|
||||
def rxn_rate_tally(self):
|
||||
if self._rxn_rate_tally is None:
|
||||
self._rxn_rate_tally = self.tallies['fission']
|
||||
self._rxn_rate_tally.sparse = self.sparse
|
||||
return self._rxn_rate_tally
|
||||
|
||||
|
||||
class NuFissionXS(MGXS):
|
||||
class NuFissionXS(FissionXSBase):
|
||||
"""A fission production multi-group cross section."""
|
||||
|
||||
def __init__(self, domain=None, domain_type=None,
|
||||
groups=None, by_nuclide=False, name=''):
|
||||
super(NuFissionXS, self).__init__(domain, domain_type,
|
||||
super(NuFissionXS, self).__init__('nu-fission', domain, domain_type,
|
||||
groups, by_nuclide, name)
|
||||
self._rxn_type = 'nu-fission'
|
||||
|
||||
@property
|
||||
def tallies(self):
|
||||
"""Construct the OpenMC tallies needed to compute this cross section.
|
||||
|
||||
This method constructs two tracklength tallies to compute the 'flux'
|
||||
and 'nu-fission' reaction rates in the spatial domain and energy
|
||||
groups of interest.
|
||||
|
||||
"""
|
||||
|
||||
# Instantiate tallies if they do not exist
|
||||
if self._tallies is None:
|
||||
|
||||
# Create a list of scores for each Tally to be created
|
||||
scores = ['flux', 'nu-fission']
|
||||
estimator = 'tracklength'
|
||||
keys = scores
|
||||
|
||||
# Create the non-domain specific Filters for the Tallies
|
||||
group_edges = self.energy_groups.group_edges
|
||||
energy_filter = openmc.Filter('energy', group_edges)
|
||||
filters = [[energy_filter], [energy_filter]]
|
||||
|
||||
# Initialize the Tallies
|
||||
self._create_tallies(scores, filters, keys, estimator)
|
||||
|
||||
return self._tallies
|
||||
|
||||
@property
|
||||
def rxn_rate_tally(self):
|
||||
if self._rxn_rate_tally is None:
|
||||
self._rxn_rate_tally = self.tallies['nu-fission']
|
||||
self._rxn_rate_tally.sparse = self.sparse
|
||||
return self._rxn_rate_tally
|
||||
class KappaFissionXS(FissionXSBase):
|
||||
"""A recoverable fission energy production rate multi-group cross section."""
|
||||
|
||||
def __init__(self, domain=None, domain_type=None,
|
||||
groups=None, by_nuclide=False, name=''):
|
||||
super(KappaFissionXS, self).__init__('kappa-fission', domain, domain_type,
|
||||
groups, by_nuclide, name)
|
||||
|
||||
class ScatterXS(MGXS):
|
||||
"""A scatter multi-group cross section."""
|
||||
|
|
@ -1741,6 +1922,58 @@ class ScatterMatrixXS(MGXS):
|
|||
cv.check_value('correction', correction, ('P0', None))
|
||||
self._correction = correction
|
||||
|
||||
def get_slice(self, nuclides=[], in_groups=[], out_groups=[]):
|
||||
"""Build a sliced ScatterMatrix for the specified nuclides and
|
||||
energy groups.
|
||||
|
||||
This method constructs a new MGXS to encapsulate a subset of the data
|
||||
represented by this MGXS. The subset of data to include in the tally
|
||||
slice is determined by the nuclides and energy groups specified in
|
||||
the input parameters.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
nuclides : list of str
|
||||
A list of nuclide name strings
|
||||
(e.g., ['U-235', 'U-238']; default is [])
|
||||
in_groups : list of Integral
|
||||
A list of incoming energy group indices starting at 1 for the high
|
||||
energies (e.g., [1, 2, 3]; default is [])
|
||||
out_groups : list of Integral
|
||||
A list of outgoing energy group indices starting at 1 for the high
|
||||
energies (e.g., [1, 2, 3]; default is [])
|
||||
|
||||
Returns
|
||||
-------
|
||||
MGXS
|
||||
A new tally which encapsulates the subset of data requested for the
|
||||
nuclide(s) and/or energy group(s) requested in the parameters.
|
||||
|
||||
"""
|
||||
|
||||
# Call super class method and null out derived tallies
|
||||
slice_xs = super(ScatterMatrixXS, self).get_slice(nuclides, in_groups)
|
||||
slice_xs._rxn_rate_tally = None
|
||||
slice_xs._xs_tally = None
|
||||
|
||||
# Slice outgoing energy groups if needed
|
||||
if len(out_groups) != 0:
|
||||
filter_bins = []
|
||||
for group in out_groups:
|
||||
group_bounds = self.energy_groups.get_group_bounds(group)
|
||||
filter_bins.append(group_bounds)
|
||||
filter_bins = [tuple(filter_bins)]
|
||||
|
||||
# Slice each of the tallies across energyout groups
|
||||
for tally_type, tally in slice_xs.tallies.items():
|
||||
if tally.contains_filter('energyout'):
|
||||
tally_slice = tally.get_slice(filters=['energyout'],
|
||||
filter_bins=filter_bins)
|
||||
slice_xs.tallies[tally_type] = tally_slice
|
||||
|
||||
slice_xs.sparse = self.sparse
|
||||
return slice_xs
|
||||
|
||||
def get_xs(self, in_groups='all', out_groups='all',
|
||||
subdomains='all', nuclides='all', xs_type='macro',
|
||||
order_groups='increasing', value='mean'):
|
||||
|
|
@ -1795,7 +2028,7 @@ class ScatterMatrixXS(MGXS):
|
|||
|
||||
# Construct a collection of the domain filter bins
|
||||
if not isinstance(subdomains, basestring):
|
||||
cv.check_iterable_type('subdomains', subdomains, Integral)
|
||||
cv.check_iterable_type('subdomains', subdomains, Integral, max_depth=2)
|
||||
for subdomain in subdomains:
|
||||
filters.append(self.domain_type)
|
||||
filter_bins.append((subdomain,))
|
||||
|
|
@ -2080,10 +2313,117 @@ class Chi(MGXS):
|
|||
super(Chi, self)._compute_xs()
|
||||
|
||||
# Add the coarse energy filter back to the nu-fission tally
|
||||
nu_fission_in.add_filter(energy_filter)
|
||||
nu_fission_in.filters.append(energy_filter)
|
||||
|
||||
return self._xs_tally
|
||||
|
||||
def get_slice(self, nuclides=[], groups=[]):
|
||||
"""Build a sliced Chi for the specified nuclides and energy groups.
|
||||
|
||||
This method constructs a new MGXS to encapsulate a subset of the data
|
||||
represented by this MGXS. The subset of data to include in the tally
|
||||
slice is determined by the nuclides and energy groups specified in
|
||||
the input parameters.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
nuclides : list of str
|
||||
A list of nuclide name strings
|
||||
(e.g., ['U-235', 'U-238']; default is [])
|
||||
groups : list of Integral
|
||||
A list of energy group indices starting at 1 for the high energies
|
||||
(e.g., [1, 2, 3]; default is [])
|
||||
|
||||
Returns
|
||||
-------
|
||||
MGXS
|
||||
A new tally which encapsulates the subset of data requested for the
|
||||
nuclide(s) and/or energy group(s) requested in the parameters.
|
||||
|
||||
"""
|
||||
|
||||
# Temporarily remove energy filter from nu-fission-in since its
|
||||
# group structure will work in super MGXS.get_slice(...) method
|
||||
nu_fission_in = self.tallies['nu-fission-in']
|
||||
energy_filter = nu_fission_in.find_filter('energy')
|
||||
nu_fission_in.remove_filter(energy_filter)
|
||||
|
||||
# Call super class method and null out derived tallies
|
||||
slice_xs = super(Chi, self).get_slice(nuclides, groups)
|
||||
slice_xs._rxn_rate_tally = None
|
||||
slice_xs._xs_tally = None
|
||||
|
||||
# Slice energy groups if needed
|
||||
if len(groups) != 0:
|
||||
filter_bins = []
|
||||
for group in groups:
|
||||
group_bounds = self.energy_groups.get_group_bounds(group)
|
||||
filter_bins.append(group_bounds)
|
||||
filter_bins = [tuple(filter_bins)]
|
||||
|
||||
# Slice nu-fission-out tally along energyout filter
|
||||
nu_fission_out = slice_xs.tallies['nu-fission-out']
|
||||
tally_slice = nu_fission_out.get_slice(filters=['energyout'],
|
||||
filter_bins=filter_bins)
|
||||
slice_xs._tallies['nu-fission-out'] = tally_slice
|
||||
|
||||
# Add energy filter back to nu-fission-in tallies
|
||||
self.tallies['nu-fission-in'].add_filter(energy_filter)
|
||||
slice_xs._tallies['nu-fission-in'].add_filter(energy_filter)
|
||||
|
||||
slice_xs.sparse = self.sparse
|
||||
return slice_xs
|
||||
|
||||
def merge(self, other):
|
||||
"""Merge another Chi with this one
|
||||
|
||||
If results have been loaded from a statepoint, then Chi are only
|
||||
mergeable along one and only one of energy groups or nuclides.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
other : MGXS
|
||||
MGXS to merge with this one
|
||||
|
||||
Returns
|
||||
-------
|
||||
merged_mgxs : MGXS
|
||||
Merged MGXS
|
||||
"""
|
||||
|
||||
if not self.can_merge(other):
|
||||
raise ValueError('Unable to merge Chi')
|
||||
|
||||
# Create deep copy of tally to return as merged tally
|
||||
merged_mgxs = copy.deepcopy(self)
|
||||
merged_mgxs._derived = True
|
||||
merged_mgxs._rxn_rate_tally = None
|
||||
merged_mgxs._xs_tally = None
|
||||
|
||||
# Merge energy groups
|
||||
if self.energy_groups != other.energy_groups:
|
||||
merged_groups = self.energy_groups.merge(other.energy_groups)
|
||||
merged_mgxs.energy_groups = merged_groups
|
||||
|
||||
# Merge nuclides
|
||||
if self.nuclides != other.nuclides:
|
||||
|
||||
# The nuclides must be mutually exclusive
|
||||
for nuclide in self.nuclides:
|
||||
if nuclide in other.nuclides:
|
||||
msg = 'Unable to merge Chi with shared nuclides'
|
||||
raise ValueError(msg)
|
||||
|
||||
# Concatenate lists of nuclides for the merged MGXS
|
||||
merged_mgxs.nuclides = self.nuclides + other.nuclides
|
||||
|
||||
# Merge tallies
|
||||
for tally_key in self.tallies:
|
||||
merged_tally = self.tallies[tally_key].merge(other.tallies[tally_key])
|
||||
merged_mgxs.tallies[tally_key] = merged_tally
|
||||
|
||||
return merged_mgxs
|
||||
|
||||
def get_xs(self, groups='all', subdomains='all', nuclides='all',
|
||||
xs_type='macro', order_groups='increasing', value='mean'):
|
||||
"""Returns an array of the fission spectrum.
|
||||
|
|
@ -2135,7 +2475,7 @@ class Chi(MGXS):
|
|||
|
||||
# Construct a collection of the domain filter bins
|
||||
if not isinstance(subdomains, basestring):
|
||||
cv.check_iterable_type('subdomains', subdomains, Integral)
|
||||
cv.check_iterable_type('subdomains', subdomains, Integral, max_depth=2)
|
||||
for subdomain in subdomains:
|
||||
filters.append(self.domain_type)
|
||||
filter_bins.append((subdomain,))
|
||||
|
|
@ -2172,7 +2512,7 @@ class Chi(MGXS):
|
|||
xs_tally = nu_fission_out / nu_fission_in
|
||||
|
||||
# Add the coarse energy filter back to the nu-fission tally
|
||||
nu_fission_in.add_filter(energy_filter)
|
||||
nu_fission_in.filters.append(energy_filter)
|
||||
|
||||
xs = xs_tally.get_values(filters=filters,
|
||||
filter_bins=filter_bins, value=value)
|
||||
|
|
|
|||
722
openmc/mgxs_library.py
Normal file
722
openmc/mgxs_library.py
Normal file
|
|
@ -0,0 +1,722 @@
|
|||
from collections import Iterable
|
||||
from numbers import Real, Integral
|
||||
from xml.etree import ElementTree as ET
|
||||
import warnings
|
||||
import sys
|
||||
if sys.version_info[0] >= 3:
|
||||
basestring = str
|
||||
|
||||
import numpy as np
|
||||
|
||||
import openmc
|
||||
from openmc.mgxs import EnergyGroups
|
||||
from openmc.checkvalue import check_type, check_value, check_greater_than, \
|
||||
check_iterable_type
|
||||
from openmc.clean_xml import *
|
||||
|
||||
# Supported incoming particle MGXS angular treatment representations
|
||||
_REPRESENTATIONS = ['isotropic', 'angle']
|
||||
|
||||
|
||||
def ndarray_to_string(arr):
|
||||
"""Converts a numpy ndarray in to a join with spaces between entries
|
||||
similar to ' '.join(map(str,arr)) but applied to all sub-dimensions.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
arr : ndarray
|
||||
Array to combine in to a string
|
||||
|
||||
Returns
|
||||
-------
|
||||
text : str
|
||||
String representation of array in arr
|
||||
|
||||
"""
|
||||
|
||||
shape = arr.shape
|
||||
ndim = arr.ndim
|
||||
tab = ' '
|
||||
indent = '\n' + tab + tab
|
||||
text = indent
|
||||
|
||||
if ndim == 1:
|
||||
text += tab
|
||||
for i in range(shape[0]):
|
||||
text += '{:.7E} '.format(arr[i])
|
||||
text += indent
|
||||
elif ndim == 2:
|
||||
for i in range(shape[0]):
|
||||
text += tab
|
||||
for j in range(shape[1]):
|
||||
text += '{:.7E} '.format(arr[i, j])
|
||||
text += indent
|
||||
elif ndim == 3:
|
||||
for i in range(shape[0]):
|
||||
for j in range(shape[1]):
|
||||
text += tab
|
||||
for k in range(shape[2]):
|
||||
text += '{:.7E} '.format(arr[i, j, k])
|
||||
text += indent
|
||||
elif ndim == 4:
|
||||
for i in range(shape[0]):
|
||||
for j in range(shape[1]):
|
||||
for k in range(shape[2]):
|
||||
text += tab
|
||||
for l in range(shape[3]):
|
||||
text += '{:.7E} '.format(arr[i, j, k, l])
|
||||
text += indent
|
||||
elif ndim == 5:
|
||||
for i in range(shape[0]):
|
||||
for j in range(shape[1]):
|
||||
for k in range(shape[2]):
|
||||
for l in range(shape[3]):
|
||||
text += tab
|
||||
for m in range(shape[4]):
|
||||
text += '{:.7E} '.format(arr[i, j, k, l, m])
|
||||
text += indent
|
||||
|
||||
return text
|
||||
|
||||
|
||||
class XSdata(object):
|
||||
"""A multi-group cross section data set providing all the
|
||||
multi-group data necessary for a multi-group OpenMC calculation.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
name : str, optional
|
||||
Name of the mgxs data set.
|
||||
|
||||
representation : {'isotropic', 'angle'}
|
||||
Method used in generating the MGXS (isotropic or angle-dependent flux
|
||||
weighting). Defaults to 'isotropic'
|
||||
|
||||
Attributes
|
||||
----------
|
||||
name : str
|
||||
Unique identifier for the xsdata object
|
||||
alias : str
|
||||
Separate unique identifier for the xsdata object
|
||||
kT : float
|
||||
Temperature (in units of MeV) of this data set.
|
||||
energy_groups : EnergyGroups
|
||||
Energy group structure
|
||||
fissionable : boolean
|
||||
Whether or not this is a fissionable data set.
|
||||
scatt_type : {'legendre', 'histogram', or 'tabular'}
|
||||
Angular distribution representation (legendre, histogram, or tabular)
|
||||
order : int
|
||||
Either the Legendre order, number of bins, or number of points used to
|
||||
describe the angular distribution associated with each group-to-group
|
||||
transfer probability.
|
||||
tabular_legendre : dict
|
||||
Set how to treat the Legendre scattering kernel (tabular or leave in
|
||||
Legendre polynomial form). Dict contains two keys: 'enable' and
|
||||
'num_points'. 'enable' is a boolean and 'num_points' is the
|
||||
number of points to use, if 'enable' is True.
|
||||
|
||||
"""
|
||||
def __init__(self, name, energy_groups, representation="isotropic"):
|
||||
# Initialize class attributes
|
||||
self._name = name
|
||||
self._energy_groups = energy_groups
|
||||
self._representation = representation
|
||||
self._alias = None
|
||||
self._kT = None
|
||||
self._fissionable = False
|
||||
self._scatt_type = 'legendre'
|
||||
self._order = None
|
||||
self._tabular_legendre = None
|
||||
self._num_polar = None
|
||||
self._num_azimuthal = None
|
||||
self._total = None
|
||||
self._absorption = None
|
||||
self._scatter = None
|
||||
self._multiplicity = None
|
||||
self._fission = None
|
||||
self._nu_fission = None
|
||||
self._k_fission = None
|
||||
self._chi = None
|
||||
self._use_chi = None
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def energy_groups(self):
|
||||
return self._energy_groups
|
||||
|
||||
@property
|
||||
def representation(self):
|
||||
return self._representation
|
||||
|
||||
@property
|
||||
def alias(self):
|
||||
return self._alias
|
||||
|
||||
@property
|
||||
def kT(self):
|
||||
return self._kT
|
||||
|
||||
@property
|
||||
def scatt_type(self):
|
||||
return self._scatt_type
|
||||
|
||||
@property
|
||||
def order(self):
|
||||
return self._order
|
||||
|
||||
@property
|
||||
def tabular_legendre(self):
|
||||
return self._tabular_legendre
|
||||
|
||||
@property
|
||||
def num_polar(self):
|
||||
return self._num_polar
|
||||
|
||||
@property
|
||||
def num_azimuthal(self):
|
||||
return self._num_azimuthal
|
||||
|
||||
@property
|
||||
def total(self):
|
||||
return self._total
|
||||
|
||||
@property
|
||||
def absorption(self):
|
||||
return self._absorption
|
||||
|
||||
@property
|
||||
def scatter(self):
|
||||
return self._scatter
|
||||
|
||||
@property
|
||||
def multiplicity(self):
|
||||
return self._multiplicity
|
||||
|
||||
@property
|
||||
def fission(self):
|
||||
return self._fission
|
||||
|
||||
@property
|
||||
def nu_fission(self):
|
||||
return self._nu_fission
|
||||
|
||||
@property
|
||||
def k_fission(self):
|
||||
return self._k_fission
|
||||
|
||||
@property
|
||||
def chi(self):
|
||||
return self._chi
|
||||
|
||||
@property
|
||||
def num_orders(self):
|
||||
if (self._order is not None) and (self._scatt_type is not None):
|
||||
if self._scatt_type is 'legendre':
|
||||
return self._order + 1
|
||||
else:
|
||||
return self._order
|
||||
|
||||
@name.setter
|
||||
def name(self, name):
|
||||
check_type('name for XSdata', name, basestring)
|
||||
self._name = name
|
||||
|
||||
@energy_groups.setter
|
||||
def energy_groups(self, energy_groups):
|
||||
# Check validity of energy_groups
|
||||
check_type("energy_groups", energy_groups, EnergyGroups)
|
||||
|
||||
# Check that there is one or more groups
|
||||
if ((energy_groups.num_groups is None) or
|
||||
(energy_groups.num_groups < 1)):
|
||||
|
||||
msg = 'energy_groups object incorrectly initialized.'
|
||||
raise ValueError(msg)
|
||||
|
||||
self._energy_groups = energy_groups
|
||||
|
||||
@representation.setter
|
||||
def representation(self, representation):
|
||||
# Check it is of valid type.
|
||||
check_value('representation', representation, _REPRESENTATIONS)
|
||||
self._representation = representation
|
||||
|
||||
@alias.setter
|
||||
def alias(self, alias):
|
||||
if alias is not None:
|
||||
check_type('alias', alias, basestring)
|
||||
self._alias = alias
|
||||
else:
|
||||
self._alias = self._name
|
||||
|
||||
@kT.setter
|
||||
def kT(self, kT):
|
||||
# Check validity of type and that the kT value is >= 0
|
||||
check_type("kT", kT, Real)
|
||||
check_greater_than("kT", kT, 0.0, equality=True)
|
||||
self._kT = kT
|
||||
|
||||
@scatt_type.setter
|
||||
def scatt_type(self, scatt_type):
|
||||
# check to see it is of a valid type and value
|
||||
check_value("scatt_type", scatt_type, ['legendre', 'histogram',
|
||||
'tabular'])
|
||||
self._scatt_type = scatt_type
|
||||
|
||||
@order.setter
|
||||
def order(self, order):
|
||||
# Check type and value
|
||||
check_type("order", order, Integral)
|
||||
check_greater_than("order", order, 0, equality=True)
|
||||
self._order = order
|
||||
|
||||
@tabular_legendre.setter
|
||||
def tabular_legendre(self, tabular_legendre):
|
||||
# Check to make sure this is a dict and it has our keys with the
|
||||
# right values.
|
||||
check_type("tabular_legendre", tabular_legendre, dict)
|
||||
if 'enable' in tabular_legendre:
|
||||
enable = tabular_legendre['enable']
|
||||
check_type('enable', enable, bool)
|
||||
else:
|
||||
msg = "enable must be provided in tabular_legendre"
|
||||
raise ValueError(msg)
|
||||
if 'num_points' in tabular_legendre:
|
||||
num_points = tabular_legendre['num_points']
|
||||
check_value('num_points', num_points, Integral)
|
||||
check_greater_than('num_points', num_points, 0)
|
||||
else:
|
||||
if not enable:
|
||||
num_points = 1
|
||||
else:
|
||||
num_points = 33
|
||||
self._tabular_legendre = {'enable': enable, 'num_points': num_points}
|
||||
|
||||
@num_polar.setter
|
||||
def num_polar(self, num_polar):
|
||||
# Make sure we have positive ints
|
||||
check_value("num_polar", num_polar, Integral)
|
||||
check_greater_than("num_polar", num_polar, 0)
|
||||
self._num_polar = num_polar
|
||||
|
||||
@num_azimuthal.setter
|
||||
def num_azimuthal(self, num_azimuthal):
|
||||
check_value("num_azimuthal", num_azimuthal, Integral)
|
||||
check_greater_than("num_azimuthal", num_azimuthal, 0)
|
||||
self._num_azimuthal = num_azimuthal
|
||||
|
||||
@total.setter
|
||||
def total(self, total):
|
||||
if self._representation is 'isotropic':
|
||||
shape = (self._energy_groups.num_groups,)
|
||||
elif self._representation is 'angle':
|
||||
shape = (self._num_polar, self._num_azimuthal,
|
||||
self._energy_groups.num_groups)
|
||||
# check we have a numpy list
|
||||
check_type("total", total, np.ndarray, expected_iter_type=Real)
|
||||
if total.shape == shape:
|
||||
self._total = np.copy(total)
|
||||
else:
|
||||
msg = 'Shape of provided total "{0}" does not match shape ' \
|
||||
'required, "{1}"'.format(total.shape, shape)
|
||||
raise ValueError(msg)
|
||||
|
||||
@absorption.setter
|
||||
def absorption(self, absorption):
|
||||
if self._representation is 'isotropic':
|
||||
shape = (self._energy_groups.num_groups,)
|
||||
elif self._representation is 'angle':
|
||||
shape = (self._num_polar, self._num_azimuthal,
|
||||
self._energy_groups.num_groups)
|
||||
# check we have a numpy list
|
||||
check_type("absorption", absorption, np.ndarray, expected_iter_type=Real)
|
||||
if absorption.shape == shape:
|
||||
self._absorption = np.copy(absorption)
|
||||
else:
|
||||
msg = 'Shape of provided absorption "{0}" does not match shape ' \
|
||||
'required, "{1}"'.format(absorption.shape, shape)
|
||||
raise ValueError(msg)
|
||||
|
||||
@fission.setter
|
||||
def fission(self, fission):
|
||||
if self._representation is 'isotropic':
|
||||
shape = (self._energy_groups.num_groups,)
|
||||
elif self._representation is 'angle':
|
||||
shape = (self._num_polar, self._num_azimuthal,
|
||||
self._energy_groups.num_groups)
|
||||
# check we have a numpy list
|
||||
check_type("fission", fission, np.ndarray, expected_iter_type=Real)
|
||||
if fission.shape == shape:
|
||||
self._fission = np.copy(fission)
|
||||
if np.sum(self._fission) > 0.0:
|
||||
self._fissionable = True
|
||||
else:
|
||||
msg = 'Shape of provided fission "{0}" does not match shape ' \
|
||||
'required, "{1}"'.format(fission.shape, shape)
|
||||
raise ValueError(msg)
|
||||
|
||||
@k_fission.setter
|
||||
def k_fission(self, k_fission):
|
||||
if self._representation is 'isotropic':
|
||||
shape = (self._energy_groups.num_groups,)
|
||||
elif self._representation is 'angle':
|
||||
shape = (self._num_polar, self._num_azimuthal,
|
||||
self._energy_groups.num_groups)
|
||||
# check we have a numpy list
|
||||
check_type("k_fission", k_fission, np.ndarray, expected_iter_type=Real)
|
||||
if k_fission.shape == shape:
|
||||
self._k_fission = np.copy(k_fission)
|
||||
if np.sum(self._k_fission) > 0.0:
|
||||
self._fissionable = True
|
||||
else:
|
||||
msg = 'Shape of provided k_fission "{0}" does not match shape ' \
|
||||
'required, "{1}"'.format(k_fission.shape, shape)
|
||||
raise ValueError(msg)
|
||||
|
||||
@chi.setter
|
||||
def chi(self, chi):
|
||||
if not self._use_chi:
|
||||
msg = 'Providing chi when nu_fission already provided as matrix!'
|
||||
raise ValueError(msg)
|
||||
if self._representation is 'isotropic':
|
||||
shape = (self._energy_groups.num_groups,)
|
||||
elif self._representation is 'angle':
|
||||
shape = (self._num_polar, self._num_azimuthal,
|
||||
self._energy_groups.num_groups)
|
||||
# check we have a numpy list
|
||||
check_type("chi", chi, np.ndarray, expected_iter_type=Real)
|
||||
if chi.shape == shape:
|
||||
self._chi = np.copy(chi)
|
||||
else:
|
||||
msg = 'Shape of provided chi "{0}" does not match shape ' \
|
||||
'required, "{1}"'.format(chi.shape, shape)
|
||||
raise ValueError(msg)
|
||||
if self._use_chi is not None:
|
||||
self._use_chi = True
|
||||
|
||||
@scatter.setter
|
||||
def scatter(self, scatter):
|
||||
if self._representation is 'isotropic':
|
||||
shape = (self.num_orders, self._energy_groups.num_groups,
|
||||
self._energy_groups.num_groups)
|
||||
max_depth = 3
|
||||
elif self._representation is 'angle':
|
||||
shape = (self._num_polar, self._num_azimuthal, self.num_orders,
|
||||
self._energy_groups.num_groups,
|
||||
self._energy_groups.num_groups)
|
||||
max_depth = 5
|
||||
# check we have a numpy list
|
||||
check_iterable_type("scatter", scatter, expected_type=Real,
|
||||
max_depth=max_depth)
|
||||
if scatter.shape == shape:
|
||||
self._scatter = np.copy(scatter)
|
||||
else:
|
||||
msg = 'Shape of provided scatter "{0}" does not match shape ' \
|
||||
'required, "{1}"'.format(scatter.shape, shape)
|
||||
raise ValueError(msg)
|
||||
|
||||
@multiplicity.setter
|
||||
def multiplicity(self, multiplicity):
|
||||
if self._representation is 'isotropic':
|
||||
shape = (self._energy_groups.num_groups,
|
||||
self._energy_groups.num_groups)
|
||||
max_depth = 2
|
||||
elif self._representation is 'angle':
|
||||
shape = (self._num_polar, self._num_azimuthal,
|
||||
self._energy_groups.num_groups,
|
||||
self._energy_groups.num_groups)
|
||||
max_depth = 4
|
||||
# check we have a numpy list
|
||||
check_iterable_type("multiplicity", multiplicity, expected_type=Real,
|
||||
max_depth=max_depth)
|
||||
if multiplicity.shape == shape:
|
||||
self._multiplicity = np.copy(multiplicity)
|
||||
else:
|
||||
msg = 'Shape of provided multiplicity "{0}" does not match shape ' \
|
||||
'required, "{1}"'.format(multiplicity.shape, shape)
|
||||
raise ValueError(msg)
|
||||
|
||||
@nu_fission.setter
|
||||
def nu_fission(self, nu_fission):
|
||||
# nu_fission can be given as a vector or a matrix
|
||||
# Vector is used when chi also exists.
|
||||
# Matrix is used when chi does not exist.
|
||||
# We have to check that the correct form is given, but only if
|
||||
# chi already has been set. If not, we just check that this is OK
|
||||
# and set the use_chi flag.
|
||||
|
||||
# First lets set our dimensions here since they get used repeatedly
|
||||
# throughout this code.
|
||||
if self._representation is 'isotropic':
|
||||
shape_vec = (self._energy_groups.num_groups,)
|
||||
shape_mat = (self._energy_groups.num_groups,
|
||||
self._energy_groups.num_groups)
|
||||
elif self._representation is 'angle':
|
||||
shape_vec = (self._num_polar, self._num_azimuthal,
|
||||
self._energy_groups.num_groups)
|
||||
shape_mat = (self._num_polar, self._num_azimuthal,
|
||||
self._energy_groups.num_groups,
|
||||
self._energy_groups.num_groups)
|
||||
|
||||
# Begin by checking the case when chi has already been given and thus
|
||||
# the rules for filling in nu_fission are set.
|
||||
if self._use_chi is not None:
|
||||
if self._use_chi:
|
||||
shape = shape_vec
|
||||
else:
|
||||
shape = shape_mat
|
||||
if nu_fission.shape != shape:
|
||||
msg = "Invalid Shape of Nu_fission!"
|
||||
raise ValueError(msg)
|
||||
else:
|
||||
# Get shape of nu_fission so we can figure if we need chi or not
|
||||
if nu_fission.shape == shape_vec:
|
||||
self._use_chi = True
|
||||
shape = shape_vec
|
||||
elif nu_fission.shape == shape_mat:
|
||||
self._use_chi = False
|
||||
shape = shape_mat
|
||||
else:
|
||||
msg = "Invalid Shape of Nu_fission!"
|
||||
raise ValueError(msg)
|
||||
|
||||
# check we have a numpy list
|
||||
check_type("nu_fission", nu_fission, np.ndarray, expected_iter_type=Real)
|
||||
self._nu_fission = np.copy(nu_fission)
|
||||
if np.sum(self._nu_fission) > 0.0:
|
||||
self._fissionable = True
|
||||
|
||||
def _get_xsdata_xml(self):
|
||||
element = ET.Element("xsdata")
|
||||
element.set("name", self._name)
|
||||
|
||||
if self._alias is not None:
|
||||
subelement = ET.SubElement(element, 'alias')
|
||||
subelement.text = self.alias
|
||||
|
||||
if self._kT is not None:
|
||||
subelement = ET.SubElement(element, 'kT')
|
||||
subelement.text = str(self._kT)
|
||||
|
||||
if self._fissionable is not None:
|
||||
subelement = ET.SubElement(element, 'fissionable')
|
||||
subelement.text = str(self._fissionable)
|
||||
|
||||
if self._representation is not None:
|
||||
subelement = ET.SubElement(element, 'representation')
|
||||
subelement.text = self._representation
|
||||
|
||||
if self._representation == 'angle':
|
||||
if self._num_azimuthal is not None:
|
||||
subelement = ET.SubElement(element, 'num_azimuthal')
|
||||
subelement.text = str(self._num_azimuthal)
|
||||
if self._num_polar is not None:
|
||||
subelement = ET.SubElement(element, 'num_polar')
|
||||
subelement.text = str(self._num_polar)
|
||||
|
||||
if self._scatt_type is not None:
|
||||
subelement = ET.SubElement(element, 'scatt_type')
|
||||
subelement.text = self._scatt_type
|
||||
|
||||
if self._order is not None:
|
||||
subelement = ET.SubElement(element, 'order')
|
||||
subelement.text = str(self._order)
|
||||
|
||||
if self._tabular_legendre is not None:
|
||||
subelement = ET.SubElement(element, 'tabular_legendre')
|
||||
subelement.set('enable', str(self._tabular_legendre['enable']))
|
||||
subelement.set('num_points', str(self._tabular_legendre['num_points']))
|
||||
|
||||
if self._total is not None:
|
||||
subelement = ET.SubElement(element, 'total')
|
||||
subelement.text = ndarray_to_string(self._total)
|
||||
|
||||
if self._absorption is not None:
|
||||
subelement = ET.SubElement(element, 'absorption')
|
||||
subelement.text = ndarray_to_string(self._absorption)
|
||||
|
||||
if self._scatter is not None:
|
||||
subelement = ET.SubElement(element, 'scatter')
|
||||
subelement.text = ndarray_to_string(self._scatter)
|
||||
|
||||
if self._multiplicity is not None:
|
||||
subelement = ET.SubElement(element, 'multiplicity')
|
||||
subelement.text = ndarray_to_string(self._multiplicity)
|
||||
|
||||
if self._fissionable:
|
||||
if self._fission is not None:
|
||||
subelement = ET.SubElement(element, 'fission')
|
||||
subelement.text = ndarray_to_string(self._fission)
|
||||
|
||||
if self._k_fission is not None:
|
||||
subelement = ET.SubElement(element, 'k_fission')
|
||||
subelement.text = ndarray_to_string(self._k_fission)
|
||||
|
||||
if self._nu_fission is not None:
|
||||
subelement = ET.SubElement(element, 'nu_fission')
|
||||
subelement.text = ndarray_to_string(self._nu_fission)
|
||||
|
||||
if self._chi is not None:
|
||||
subelement = ET.SubElement(element, 'chi')
|
||||
subelement.text = ndarray_to_string(self._chi)
|
||||
|
||||
return element
|
||||
|
||||
|
||||
class MGXSLibraryFile(object):
|
||||
"""Multi-Group Cross Sections file used for an OpenMC simulation.
|
||||
Corresponds directly to the MG version of the cross_sections.xml input file.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
energy_groups : openmc.mgxs.EnergyGroups
|
||||
Energy group structure.
|
||||
inverse_velocities : Iterable of Real
|
||||
Inverse of velocities, units of sec/cm
|
||||
filename : str
|
||||
XML file to write to.
|
||||
xsdatas : Iterable of XSdata
|
||||
Iterable of multi-Group cross section data objects
|
||||
"""
|
||||
|
||||
def __init__(self, energy_groups):
|
||||
# Initialize MGXSLibraryFile class attributes
|
||||
self._xsdatas = []
|
||||
self._energy_groups = energy_groups
|
||||
self._inverse_velocities = None
|
||||
self._cross_sections_file = ET.Element("cross_sections")
|
||||
|
||||
@property
|
||||
def inverse_velocities(self):
|
||||
return self._inverse_velocities
|
||||
|
||||
@property
|
||||
def energy_groups(self):
|
||||
return self._energy_groups
|
||||
|
||||
@inverse_velocities.setter
|
||||
def inverse_velocities(self, inverse_velocities):
|
||||
cv.check_type('inverse_velocities', inverse_velocities, Iterable, Real)
|
||||
cv.check_greater_than('number of inverse_velocities',
|
||||
len(inverse_velocities), 0.0)
|
||||
self._inverse_velocities = np.array(inverse_velocities)
|
||||
|
||||
@energy_groups.setter
|
||||
def energy_groups(self, energy_groups):
|
||||
check_type("energy groups", energy_groups, EnergyGroups)
|
||||
self._energy_groups = energy_groups
|
||||
|
||||
def add_xsdata(self, xsdata):
|
||||
"""Add an XSdata entry to the file.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
xsdata : XSdata
|
||||
MGXS information to add
|
||||
|
||||
"""
|
||||
|
||||
# Check the type
|
||||
if not isinstance(xsdata, XSdata):
|
||||
msg = 'Unable to add a non-XSdata "{0}" to the ' \
|
||||
'MGXSLibraryFile'.format(xsdata)
|
||||
raise ValueError(msg)
|
||||
|
||||
# Make sure energy groups match.
|
||||
if xsdata.energy_groups != self._energy_groups:
|
||||
msg = 'Energy groups of XSdata do not match that of MGXSLibraryFile!'
|
||||
raise ValueError(msg)
|
||||
|
||||
self._xsdatas.append(xsdata)
|
||||
|
||||
def add_xsdatas(self, xsdatas):
|
||||
"""Add multiple xsdatas to the file.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
xsdatas : tuple or list of XSdata
|
||||
XSdatas to add
|
||||
|
||||
"""
|
||||
|
||||
if not isinstance(xsdatas, Iterable):
|
||||
msg = 'Unable to create OpenMC xsdatas.xml file from "{0}" which ' \
|
||||
'is not iterable'.format(xsdatas)
|
||||
raise ValueError(msg)
|
||||
|
||||
for xsdata in xsdatas:
|
||||
self.add_xsdata(xsdata)
|
||||
|
||||
def remove_xsdata(self, xsdata):
|
||||
"""Remove a xsdata from the file
|
||||
|
||||
Parameters
|
||||
----------
|
||||
xsdata : XSdata
|
||||
XSdata to remove
|
||||
|
||||
"""
|
||||
|
||||
if not isinstance(xsdata, XSdata):
|
||||
msg = 'Unable to remove a non-XSdata "{0}" from the ' \
|
||||
'XSdatasFile'.format(xsdata)
|
||||
raise ValueError(msg)
|
||||
|
||||
self._xsdatas.remove(xsdata)
|
||||
|
||||
def _create_groups_subelement(self):
|
||||
if self._energy_groups is not None:
|
||||
element = ET.SubElement(self._cross_sections_file, "groups")
|
||||
element.text = str(self._energy_groups.num_groups)
|
||||
|
||||
def _create_group_structure_subelement(self):
|
||||
if self._energy_groups is not None:
|
||||
element = ET.SubElement(self._cross_sections_file,
|
||||
"group_structure")
|
||||
element.text = ' '.join(map(str, self._energy_groups.group_edges))
|
||||
|
||||
def _create_inverse_velocities_subelement(self):
|
||||
if self._inverse_velocities is not None:
|
||||
element = ET.SubElement(self._cross_sections_file,
|
||||
"inverse_velocities")
|
||||
element.text = ' '.join(map(str, self._inverse_velocities))
|
||||
|
||||
def _create_xsdata_subelements(self):
|
||||
for xsdata in self._xsdatas:
|
||||
xml_element = xsdata._get_xsdata_xml()
|
||||
self._cross_sections_file.append(xml_element)
|
||||
|
||||
def export_to_xml(self, filename='mg_cross_sections.xml'):
|
||||
"""Create an mg_cross_sections.xml file that can be used for a
|
||||
simulation.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
filename : str, optional
|
||||
filename of file, default is mg_cross_sections.xml
|
||||
|
||||
"""
|
||||
|
||||
# Reset xml element tree
|
||||
self._cross_sections_file.clear()
|
||||
|
||||
self._create_groups_subelement()
|
||||
self._create_group_structure_subelement()
|
||||
self._create_inverse_velocities_subelement()
|
||||
self._create_xsdata_subelements()
|
||||
|
||||
# Clean the indentation in the file to be user-readable
|
||||
sort_xml_elements(self._cross_sections_file)
|
||||
clean_xml_indentation(self._cross_sections_file)
|
||||
|
||||
# Write the XML Tree to the xsdatas.xml file
|
||||
tree = ET.ElementTree(self._cross_sections_file)
|
||||
tree.write(filename, xml_declaration=True,
|
||||
encoding='utf-8', method="xml")
|
||||
|
||||
|
||||
|
||||
|
|
@ -60,6 +60,12 @@ class Nuclide(object):
|
|||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
||||
def __gt__(self, other):
|
||||
return repr(self) > repr(other)
|
||||
|
||||
def __lt__(self, other):
|
||||
return not self > other
|
||||
|
||||
def __hash__(self):
|
||||
return hash(repr(self))
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ except ImportError:
|
|||
import openmc
|
||||
from openmc.region import Intersection
|
||||
from openmc.surface import Halfspace
|
||||
import openmc.checkvalue as cv
|
||||
|
||||
|
||||
# A dictionary of all OpenMC Materials created
|
||||
|
|
@ -79,10 +80,7 @@ def get_opencg_material(openmc_material):
|
|||
|
||||
"""
|
||||
|
||||
if not isinstance(openmc_material, openmc.Material):
|
||||
msg = 'Unable to create an OpenCG Material from "{0}" ' \
|
||||
'which is not an OpenMC Material'.format(openmc_material)
|
||||
raise ValueError(msg)
|
||||
cv.check_type('openmc_material', openmc_material, openmc.Material)
|
||||
|
||||
global OPENCG_MATERIALS
|
||||
material_id = openmc_material.id
|
||||
|
|
@ -119,10 +117,7 @@ def get_openmc_material(opencg_material):
|
|||
|
||||
"""
|
||||
|
||||
if not isinstance(opencg_material, opencg.Material):
|
||||
msg = 'Unable to create an OpenMC Material from "{0}" ' \
|
||||
'which is not an OpenCG Material'.format(opencg_material)
|
||||
raise ValueError(msg)
|
||||
cv.check_type('opencg_material', opencg_material, opencg.Material)
|
||||
|
||||
global OPENMC_MATERIALS
|
||||
material_id = opencg_material.id
|
||||
|
|
@ -165,10 +160,7 @@ def is_opencg_surface_compatible(opencg_surface):
|
|||
|
||||
"""
|
||||
|
||||
if not isinstance(opencg_surface, opencg.Surface):
|
||||
msg = 'Unable to check if OpenCG Surface is compatible' \
|
||||
'since "{0}" is not a Surface'.format(opencg_surface)
|
||||
raise ValueError(msg)
|
||||
cv.check_type('opencg_surface', opencg_surface, opencg.Surface)
|
||||
|
||||
if opencg_surface.type in ['x-squareprism',
|
||||
'y-squareprism', 'z-squareprism']:
|
||||
|
|
@ -192,10 +184,7 @@ def get_opencg_surface(openmc_surface):
|
|||
|
||||
"""
|
||||
|
||||
if not isinstance(openmc_surface, openmc.Surface):
|
||||
msg = 'Unable to create an OpenCG Surface from "{0}" ' \
|
||||
'which is not an OpenMC Surface'.format(openmc_surface)
|
||||
raise ValueError(msg)
|
||||
cv.check_type('openmc_surface', openmc_surface, openmc.Surface)
|
||||
|
||||
global OPENCG_SURFACES
|
||||
surface_id = openmc_surface.id
|
||||
|
|
@ -278,10 +267,7 @@ def get_openmc_surface(opencg_surface):
|
|||
|
||||
"""
|
||||
|
||||
if not isinstance(opencg_surface, opencg.Surface):
|
||||
msg = 'Unable to create an OpenMC Surface from "{0}" which ' \
|
||||
'is not an OpenCG Surface'.format(opencg_surface)
|
||||
raise ValueError(msg)
|
||||
cv.check_type('opencg_surface', opencg_surface, opencg.Surface)
|
||||
|
||||
global openmc_surface
|
||||
surface_id = opencg_surface.id
|
||||
|
|
@ -369,10 +355,7 @@ def get_compatible_opencg_surfaces(opencg_surface):
|
|||
|
||||
"""
|
||||
|
||||
if not isinstance(opencg_surface, opencg.Surface):
|
||||
msg = 'Unable to create an OpenMC Surface from "{0}" which ' \
|
||||
'is not an OpenCG Surface'.format(opencg_surface)
|
||||
raise ValueError(msg)
|
||||
cv.check_type('opencg_surface', opencg_surface, opencg.Surface)
|
||||
|
||||
global OPENMC_SURFACES
|
||||
surface_id = opencg_surface.id
|
||||
|
|
@ -451,10 +434,7 @@ def get_opencg_cell(openmc_cell):
|
|||
|
||||
"""
|
||||
|
||||
if not isinstance(openmc_cell, openmc.Cell):
|
||||
msg = 'Unable to create an OpenCG Cell from "{0}" which ' \
|
||||
'is not an OpenMC Cell'.format(openmc_cell)
|
||||
raise ValueError(msg)
|
||||
cv.check_type('openmc_cell', openmc_cell, openmc.Cell)
|
||||
|
||||
global OPENCG_CELLS
|
||||
cell_id = openmc_cell.id
|
||||
|
|
@ -469,9 +449,9 @@ def get_opencg_cell(openmc_cell):
|
|||
|
||||
fill = openmc_cell.fill
|
||||
|
||||
if (openmc_cell.fill_type == 'material'):
|
||||
if openmc_cell.fill_type == 'material':
|
||||
opencg_cell.fill = get_opencg_material(fill)
|
||||
elif (openmc_cell.fill_type == 'universe'):
|
||||
elif openmc_cell.fill_type == 'universe':
|
||||
opencg_cell.fill = get_opencg_universe(fill)
|
||||
else:
|
||||
opencg_cell.fill = get_opencg_lattice(fill)
|
||||
|
|
@ -533,20 +513,10 @@ def get_compatible_opencg_cells(opencg_cell, opencg_surface, halfspace):
|
|||
OpenMC
|
||||
|
||||
"""
|
||||
if not isinstance(opencg_cell, opencg.Cell):
|
||||
msg = 'Unable to create compatible OpenMC Cell from "{0}" which ' \
|
||||
'is not an OpenCG Cell'.format(opencg_cell)
|
||||
raise ValueError(msg)
|
||||
|
||||
elif not isinstance(opencg_surface, opencg.Surface):
|
||||
msg = 'Unable to create compatible OpenMC Cell since "{0}" is ' \
|
||||
'not an OpenCG Surface'.format(opencg_surface)
|
||||
raise ValueError(msg)
|
||||
|
||||
elif halfspace not in [-1, +1]:
|
||||
msg = 'Unable to create compatible Cell since "{0}"' \
|
||||
'is not a +/-1 halfspace'.format(halfspace)
|
||||
raise ValueError(msg)
|
||||
cv.check_type('opencg_cell', opencg_cell, opencg.Cell)
|
||||
cv.check_type('opencg_surface', opencg_surface, opencg.Surface)
|
||||
cv.check_value('halfspace', halfspace, (-1, +1))
|
||||
|
||||
# Initialize an empty list for the new compatible cells
|
||||
compatible_cells = []
|
||||
|
|
@ -575,7 +545,7 @@ def get_compatible_opencg_cells(opencg_cell, opencg_surface, halfspace):
|
|||
num_clones = 8
|
||||
|
||||
for clone_id in range(num_clones):
|
||||
# Create a cloned OpenCG Cell with Surfaces compatible with OpenMC
|
||||
# Create cloned OpenCG Cell with Surfaces compatible with OpenMC
|
||||
clone = opencg_cell.clone()
|
||||
compatible_cells.append(clone)
|
||||
|
||||
|
|
@ -641,10 +611,7 @@ def make_opencg_cells_compatible(opencg_universe):
|
|||
|
||||
"""
|
||||
|
||||
if not isinstance(opencg_universe, opencg.Universe):
|
||||
msg = 'Unable to make compatible OpenCG Cells for "{0}" which ' \
|
||||
'is not an OpenCG Universe'.format(opencg_universe)
|
||||
raise ValueError(msg)
|
||||
cv.check_type('opencg_universe', opencg_universe, opencg.Universe)
|
||||
|
||||
# Check all OpenCG Cells in this Universe for compatibility with OpenMC
|
||||
opencg_cells = opencg_universe.cells
|
||||
|
|
@ -700,10 +667,7 @@ def get_openmc_cell(opencg_cell):
|
|||
|
||||
"""
|
||||
|
||||
if not isinstance(opencg_cell, opencg.Cell):
|
||||
msg = 'Unable to create an OpenMC Cell from "{0}" which ' \
|
||||
'is not an OpenCG Cell'.format(opencg_cell)
|
||||
raise ValueError(msg)
|
||||
cv.check_type('opencg_cell', opencg_cell, opencg.Cell)
|
||||
|
||||
global OPENMC_CELLS
|
||||
cell_id = opencg_cell.id
|
||||
|
|
@ -718,9 +682,9 @@ def get_openmc_cell(opencg_cell):
|
|||
|
||||
fill = opencg_cell.fill
|
||||
|
||||
if (opencg_cell.type == 'universe'):
|
||||
if opencg_cell.type == 'universe':
|
||||
openmc_cell.fill = get_openmc_universe(fill)
|
||||
elif (opencg_cell.type == 'lattice'):
|
||||
elif opencg_cell.type == 'lattice':
|
||||
openmc_cell.fill = get_openmc_lattice(fill)
|
||||
else:
|
||||
openmc_cell.fill = get_openmc_material(fill)
|
||||
|
|
@ -764,10 +728,7 @@ def get_opencg_universe(openmc_universe):
|
|||
|
||||
"""
|
||||
|
||||
if not isinstance(openmc_universe, openmc.Universe):
|
||||
msg = 'Unable to create an OpenCG Universe from "{0}" which ' \
|
||||
'is not an OpenMC Universe'.format(openmc_universe)
|
||||
raise ValueError(msg)
|
||||
cv.check_type('openmc_universe', openmc_universe, openmc.Universe)
|
||||
|
||||
global OPENCG_UNIVERSES
|
||||
universe_id = openmc_universe.id
|
||||
|
|
@ -811,10 +772,7 @@ def get_openmc_universe(opencg_universe):
|
|||
|
||||
"""
|
||||
|
||||
if not isinstance(opencg_universe, opencg.Universe):
|
||||
msg = 'Unable to create an OpenMC Universe from "{0}" which ' \
|
||||
'is not an OpenCG Universe'.format(opencg_universe)
|
||||
raise ValueError(msg)
|
||||
cv.check_type('opencg_universe', opencg_universe, opencg.Universe)
|
||||
|
||||
global OPENMC_UNIVERSES
|
||||
universe_id = opencg_universe.id
|
||||
|
|
@ -861,10 +819,7 @@ def get_opencg_lattice(openmc_lattice):
|
|||
|
||||
"""
|
||||
|
||||
if not isinstance(openmc_lattice, openmc.Lattice):
|
||||
msg = 'Unable to create an OpenCG Lattice from "{0}" which ' \
|
||||
'is not an OpenMC Lattice'.format(openmc_lattice)
|
||||
raise ValueError(msg)
|
||||
cv.check_type('openmc_lattice', openmc_lattice, openmc.Lattice)
|
||||
|
||||
global OPENCG_LATTICES
|
||||
lattice_id = openmc_lattice.id
|
||||
|
|
@ -958,10 +913,7 @@ def get_openmc_lattice(opencg_lattice):
|
|||
|
||||
"""
|
||||
|
||||
if not isinstance(opencg_lattice, opencg.Lattice):
|
||||
msg = 'Unable to create an OpenMC Lattice from "{0}" which ' \
|
||||
'is not an OpenCG Lattice'.format(opencg_lattice)
|
||||
raise ValueError(msg)
|
||||
cv.check_type('opencg_lattice', opencg_lattice, opencg.Lattice)
|
||||
|
||||
global OPENMC_LATTICES
|
||||
lattice_id = opencg_lattice.id
|
||||
|
|
@ -1032,10 +984,7 @@ def get_opencg_geometry(openmc_geometry):
|
|||
|
||||
"""
|
||||
|
||||
if not isinstance(openmc_geometry, openmc.Geometry):
|
||||
msg = 'Unable to get OpenCG geometry from "{0}" which is ' \
|
||||
'not an OpenMC Geometry object'.format(openmc_geometry)
|
||||
raise ValueError(msg)
|
||||
cv.check_type('openmc_geometry', openmc_geometry, openmc.Geometry)
|
||||
|
||||
# Clear dictionaries and auto-generated IDs
|
||||
OPENMC_SURFACES.clear()
|
||||
|
|
@ -1072,10 +1021,7 @@ def get_openmc_geometry(opencg_geometry):
|
|||
|
||||
"""
|
||||
|
||||
if not isinstance(opencg_geometry, opencg.Geometry):
|
||||
msg = 'Unable to get OpenMC geometry from "{0}" which is ' \
|
||||
'not an OpenCG Geometry object'.format(opencg_geometry)
|
||||
raise ValueError(msg)
|
||||
cv.check_type('opencg_geometry', opencg_geometry, opencg.Geometry)
|
||||
|
||||
# Deep copy the goemetry since it may be modified to make all Surfaces
|
||||
# compatible with OpenMC's specifications
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import numpy as np
|
|||
from openmc.clean_xml import *
|
||||
from openmc.checkvalue import (check_type, check_length, check_value,
|
||||
check_greater_than, check_less_than)
|
||||
from openmc import Nuclide
|
||||
from openmc.source import Source
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
|
|
@ -70,15 +71,19 @@ class SettingsFile(object):
|
|||
cross_sections : str
|
||||
Indicates the path to an XML cross section listing file (usually named
|
||||
cross_sections.xml). If it is not set, the :envvar:`CROSS_SECTIONS`
|
||||
environment variable will be used to find the path to the XML cross
|
||||
section listing.
|
||||
environment variable will be used for continuous-energy calculations
|
||||
and :envvar:`MG_CROSS_SECTIONS` will be used for multi-group
|
||||
calculations to find the path to the XML cross section file.
|
||||
multipole_library : str
|
||||
Indicates the path to a directory containing a windowed multipole
|
||||
cross section library. If it is not set, the :envvar:`MULTIPOLE_LIBRARY'
|
||||
environment variable will be used. A multipole library is optional.
|
||||
energy_grid : str
|
||||
Set the method used to search energy grids. Acceptable values are
|
||||
'nuclide', 'logarithm', and 'material-union'.
|
||||
energy_grid : {'nuclide', 'logarithm', 'material-union'}
|
||||
Set the method used to search energy grids.
|
||||
energy_mode : {'continuous-energy', 'multi-group'}
|
||||
Set whether the calculation should be continuous-energy or multi-group.
|
||||
max_order : int
|
||||
Maximum scattering order to apply globally when in multi-group mode.
|
||||
ptables : bool
|
||||
Determine whether probability tables are used.
|
||||
run_cmfd : bool
|
||||
|
|
@ -128,6 +133,8 @@ class SettingsFile(object):
|
|||
use_windowed_multipole : bool
|
||||
Whether or not windowed multipole can be used to evaluate resolved
|
||||
resonance cross sections.
|
||||
resonance_scattering : ResonanceScattering or iterable of ResonanceScattering
|
||||
The elastic scattering model to use for resonant isotopes
|
||||
|
||||
"""
|
||||
|
||||
|
|
@ -141,6 +148,10 @@ class SettingsFile(object):
|
|||
self._particles = None
|
||||
self._keff_trigger = None
|
||||
|
||||
# Energy mode subelement
|
||||
self._energy_mode = None
|
||||
self._max_order = None
|
||||
|
||||
# Source subelement
|
||||
self._source = None
|
||||
|
||||
|
|
@ -206,6 +217,8 @@ class SettingsFile(object):
|
|||
self._source_element = None
|
||||
self._multipole_active = None
|
||||
|
||||
self._resonance_scattering = None
|
||||
|
||||
@property
|
||||
def run_mode(self):
|
||||
return self._run_mode
|
||||
|
|
@ -230,6 +243,14 @@ class SettingsFile(object):
|
|||
def keff_trigger(self):
|
||||
return self._keff_trigger
|
||||
|
||||
@property
|
||||
def energy_mode(self):
|
||||
return self._energy_mode
|
||||
|
||||
@property
|
||||
def max_order(self):
|
||||
return self._max_order
|
||||
|
||||
@property
|
||||
def source(self):
|
||||
return self._source
|
||||
|
|
@ -394,9 +415,13 @@ class SettingsFile(object):
|
|||
def use_windowed_multipole(self):
|
||||
return self._multipole_active
|
||||
|
||||
@property
|
||||
def resonance_scattering(self):
|
||||
return self._resonance_scattering
|
||||
|
||||
@run_mode.setter
|
||||
def run_mode(self, run_mode):
|
||||
if 'run_mode' not in ['eigenvalue', 'fixed source']:
|
||||
if run_mode not in ['eigenvalue', 'fixed source']:
|
||||
msg = 'Unable to set run mode to "{0}". Only "eigenvalue" ' \
|
||||
'and "fixed source" are supported."'.format(run_mode)
|
||||
raise ValueError(msg)
|
||||
|
|
@ -455,6 +480,18 @@ class SettingsFile(object):
|
|||
|
||||
self._keff_trigger = keff_trigger
|
||||
|
||||
@energy_mode.setter
|
||||
def energy_mode(self, energy_mode):
|
||||
check_value('energy mode', energy_mode,
|
||||
['continuous-energy', 'multi-group'])
|
||||
self._energy_mode = energy_mode
|
||||
|
||||
@max_order.setter
|
||||
def max_order(self, max_order):
|
||||
check_type('maximum scattering order', max_order, Integral)
|
||||
check_greater_than('maximum scattering order', max_order, 0, True)
|
||||
self._max_order = max_order
|
||||
|
||||
@source.setter
|
||||
def source(self, source):
|
||||
if isinstance(source, Source):
|
||||
|
|
@ -763,6 +800,16 @@ class SettingsFile(object):
|
|||
check_type('use_windowed_multipole', active, bool)
|
||||
self._multipole_active = active
|
||||
|
||||
@resonance_scattering.setter
|
||||
def resonance_scattering(self, res):
|
||||
if isinstance(res, Iterable):
|
||||
check_type('resonance_scattering', res, Iterable,
|
||||
ResonanceScattering)
|
||||
self._resonance_scattering = res
|
||||
else:
|
||||
check_type('resonance_scattering', res, ResonanceScattering)
|
||||
self._resonance_scattering = [res]
|
||||
|
||||
def _create_run_mode_subelement(self):
|
||||
|
||||
if self.run_mode == 'eigenvalue':
|
||||
|
|
@ -809,6 +856,16 @@ class SettingsFile(object):
|
|||
subelement = ET.SubElement(element, key)
|
||||
subelement.text = str(self._keff_trigger[key]).lower()
|
||||
|
||||
def _create_energy_mode_subelement(self):
|
||||
if self._energy_mode is not None:
|
||||
element = ET.SubElement(self._settings_file, "energy_mode")
|
||||
element.text = str(self._energy_mode)
|
||||
|
||||
def _create_max_order_subelement(self):
|
||||
if self._max_order is not None:
|
||||
element = ET.SubElement(self._settings_file, "max_order")
|
||||
element.text = str(self._max_order)
|
||||
|
||||
def _create_source_subelement(self):
|
||||
if self.source is not None:
|
||||
for source in self.source:
|
||||
|
|
@ -1002,7 +1059,7 @@ class SettingsFile(object):
|
|||
element = ET.SubElement(self._settings_file, "uniform_fs")
|
||||
|
||||
subelement = ET.SubElement(element, "dimension")
|
||||
subelement.text = str(self._ufs_dimension)
|
||||
subelement.text = ' '.join(map(str, self._ufs_dimension))
|
||||
|
||||
subelement = ET.SubElement(element, "lower_left")
|
||||
subelement.text = ' '.join(map(str, self._ufs_lower_left))
|
||||
|
|
@ -1043,6 +1100,17 @@ class SettingsFile(object):
|
|||
"use_windowed_multipole")
|
||||
element.text = str(self._multipole_active)
|
||||
|
||||
def _create_resonance_scattering_element(self):
|
||||
if self.resonance_scattering is None: return
|
||||
|
||||
element = ET.SubElement(self._settings_file, "resonance_scattering")
|
||||
|
||||
for r in self.resonance_scattering:
|
||||
if r.nuclide.name != r.nuclide_0K.name:
|
||||
raise ValueError("The nuclide and nuclide_0K attributes of "
|
||||
"a ResonantScattering object must have identical names.")
|
||||
r.create_xml_subelement(element)
|
||||
|
||||
def export_to_xml(self):
|
||||
"""Create a settings.xml file that can be used for a simulation.
|
||||
|
||||
|
|
@ -1064,6 +1132,8 @@ class SettingsFile(object):
|
|||
self._create_cross_sections_subelement()
|
||||
self._create_multipole_library_subelement()
|
||||
self._create_energy_grid_subelement()
|
||||
self._create_energy_mode_subelement()
|
||||
self._create_max_order_subelement()
|
||||
self._create_ptables_subelement()
|
||||
self._create_run_cmfd_subelement()
|
||||
self._create_seed_subelement()
|
||||
|
|
@ -1079,6 +1149,7 @@ class SettingsFile(object):
|
|||
self._create_ufs_subelement()
|
||||
self._create_dd_subelement()
|
||||
self._create_use_multipole_subelement()
|
||||
self._create_resonance_scattering_element()
|
||||
|
||||
# Clean the indentation in the file to be user-readable
|
||||
clean_xml_indentation(self._settings_file)
|
||||
|
|
@ -1087,3 +1158,104 @@ class SettingsFile(object):
|
|||
tree = ET.ElementTree(self._settings_file)
|
||||
tree.write("settings.xml", xml_declaration=True,
|
||||
encoding='utf-8', method="xml")
|
||||
|
||||
|
||||
class ResonanceScattering(object):
|
||||
"""Specification of the elastic scattering model for resonant isotopes
|
||||
|
||||
Attributes
|
||||
----------
|
||||
nuclide : openmc.nuclide.Nuclide
|
||||
The nuclide affected by this resonance scattering treatment.
|
||||
nuclide_0K : openmc.nuclide.Nuclide
|
||||
This should be the same isotope as the nuclide attribute above, but it
|
||||
should have an xs attribute that identifies 0 Kelvin data.
|
||||
method : str
|
||||
The method used to sample outgoing scattering energies. Valid options
|
||||
are 'ARES', 'CXS' (constant cross section), 'DBRC' (Doppler broadening
|
||||
rejection correction), and 'WCM' (weight correction method).
|
||||
E_min : Real
|
||||
The minimum energy above which the specified method is applied. By
|
||||
default, CXS will be used below E_min.
|
||||
E_max : Real
|
||||
The maximum energy below which the specified method is applied. By
|
||||
default, the asymptotic target-at-rest model is applied above E_max.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self._nuclide = None
|
||||
self._nuclide_0K = None
|
||||
self._method = None
|
||||
self._E_min = None
|
||||
self._E_max = None
|
||||
|
||||
@property
|
||||
def nuclide(self):
|
||||
return self._nuclide
|
||||
|
||||
@property
|
||||
def nuclide_0K(self):
|
||||
return self._nuclide_0K
|
||||
|
||||
@property
|
||||
def method(self):
|
||||
return self._method
|
||||
|
||||
@property
|
||||
def E_min(self):
|
||||
return self._E_min
|
||||
|
||||
@property
|
||||
def E_max(self):
|
||||
return self._E_max
|
||||
|
||||
@nuclide.setter
|
||||
def nuclide(self, nuc):
|
||||
check_type('nuclide', nuc, Nuclide)
|
||||
if nuc.zaid == None: raise ValueError("The nuclide must have an "
|
||||
"explicitly defined zaid attribute.")
|
||||
self._nuclide = nuc
|
||||
|
||||
@nuclide_0K.setter
|
||||
def nuclide_0K(self, nuc):
|
||||
check_type('nuclide_0K', nuc, Nuclide)
|
||||
if nuc.zaid == None: raise ValueError("The nuclide_0K must have an "
|
||||
"explicitly defined zaid attribute.")
|
||||
self._nuclide_0K = nuc
|
||||
|
||||
@method.setter
|
||||
def method(self, m):
|
||||
check_value('method', m, ('ARES', 'CXS', 'DBRC', 'WCM'))
|
||||
self._method = m
|
||||
|
||||
@E_min.setter
|
||||
def E_min(self, E):
|
||||
check_type('E_min', E, Real)
|
||||
check_greater_than('E_min', E, 0, True)
|
||||
self._E_min = E
|
||||
|
||||
@E_max.setter
|
||||
def E_max(self, E):
|
||||
check_type('E_max', E, Real)
|
||||
check_greater_than('E_max', E, 0, True)
|
||||
self._E_max = E
|
||||
|
||||
def create_xml_subelement(self, xml_element):
|
||||
scatterer = ET.SubElement(xml_element, "scatterer")
|
||||
subelement = ET.SubElement(scatterer, 'nuclide')
|
||||
subelement.text = self.nuclide.name
|
||||
if self.method is not None:
|
||||
subelement = ET.SubElement(scatterer, 'method')
|
||||
subelement.text = self.method
|
||||
subelement = ET.SubElement(scatterer, 'xs_label')
|
||||
subelement.text = str(self.nuclide.zaid) + '.' + str(self.nuclide.xs)
|
||||
subelement = ET.SubElement(scatterer, 'xs_label_0K')
|
||||
subelement.text = str(self.nuclide_0K.zaid) + '.' \
|
||||
+ str(self.nuclide_0K.xs)
|
||||
if self.E_min is not None:
|
||||
subelement = ET.SubElement(scatterer, 'E_min')
|
||||
subelement.text = str(self.E_min)
|
||||
if self.E_max is not None:
|
||||
subelement = ET.SubElement(scatterer, 'E_max')
|
||||
subelement.text = str(self.E_max)
|
||||
|
|
|
|||
|
|
@ -103,11 +103,11 @@ class StatePoint(object):
|
|||
raise IOError('Could not read statepoint file. This most likely '
|
||||
'means the statepoint file was produced by a different '
|
||||
'version of OpenMC than the one you are using.')
|
||||
if self._f['revision'].value != 14:
|
||||
if self._f['revision'].value != 15:
|
||||
raise IOError('Statepoint file has a file revision of {} '
|
||||
'which is not consistent with the revision this '
|
||||
'version of OpenMC expects ({}).'.format(
|
||||
self._f['revision'].value, 14))
|
||||
self._f['revision'].value, 15))
|
||||
|
||||
# Set flags for what data has been read
|
||||
self._meshes_read = False
|
||||
|
|
@ -389,7 +389,7 @@ class StatePoint(object):
|
|||
new_filter.mesh = self.meshes[key]
|
||||
|
||||
# Add Filter to the Tally
|
||||
tally.add_filter(new_filter)
|
||||
tally.filters.append(new_filter)
|
||||
|
||||
# Read Nuclide bins
|
||||
nuclide_names = \
|
||||
|
|
@ -398,7 +398,7 @@ class StatePoint(object):
|
|||
# Add all Nuclides to the Tally
|
||||
for name in nuclide_names:
|
||||
nuclide = openmc.Nuclide(name.decode().strip())
|
||||
tally.add_nuclide(nuclide)
|
||||
tally.nuclides.append(nuclide)
|
||||
|
||||
scores = self._f['{0}{1}/score_bins'.format(
|
||||
base, tally_key)].value
|
||||
|
|
@ -425,7 +425,7 @@ class StatePoint(object):
|
|||
pattern = r'-n$|-pn$|-yn$'
|
||||
score = re.sub(pattern, '-' + moments[j].decode(), score)
|
||||
|
||||
tally.add_score(score)
|
||||
tally.scores.append(score)
|
||||
|
||||
# Add Tally to the global dictionary of all Tallies
|
||||
tally.sparse = self.sparse
|
||||
|
|
|
|||
|
|
@ -60,6 +60,9 @@ class Summary(object):
|
|||
# Read date and time
|
||||
self.date_and_time = self._f['date_and_time'][...]
|
||||
|
||||
# Read if continuous-energy or multi-group
|
||||
self.run_CE = (self._f['run_CE'].value == 1)
|
||||
|
||||
self.n_batches = self._f['n_batches'].value
|
||||
self.n_particles = self._f['n_particles'].value
|
||||
self.n_active = self._f['n_active'].value
|
||||
|
|
@ -279,7 +282,7 @@ class Summary(object):
|
|||
# Get the distribcell index
|
||||
ind = self._f['geometry/cells'][key]['distribcell_index'].value
|
||||
if ind != 0:
|
||||
cell.distribcell_index = ind
|
||||
cell.distribcell_index = ind
|
||||
|
||||
# Add the Cell to the global dictionary of all Cells
|
||||
self.cells[index] = cell
|
||||
|
|
@ -542,7 +545,7 @@ class Summary(object):
|
|||
# If this is a moment, use generic moment order
|
||||
pattern = r'-n$|-pn$|-yn$'
|
||||
score = re.sub(pattern, '-' + moments[j].decode(), score)
|
||||
tally.add_score(score)
|
||||
tally.scores.append(score)
|
||||
|
||||
# Read filter metadata
|
||||
num_filters = self._f['{0}/n_filters'.format(subbase)].value
|
||||
|
|
@ -563,7 +566,7 @@ class Summary(object):
|
|||
new_filter.num_bins = num_bins
|
||||
|
||||
# Add Filter to the Tally
|
||||
tally.add_filter(new_filter)
|
||||
tally.filters.append(new_filter)
|
||||
|
||||
# Add Tally to the global dictionary of all Tallies
|
||||
self.tallies[tally_id] = tally
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,6 +1,7 @@
|
|||
from numbers import Real
|
||||
from xml.etree import ElementTree as ET
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
from openmc.checkvalue import check_type, check_value
|
||||
|
||||
|
|
@ -46,9 +47,7 @@ class Trigger(object):
|
|||
clone._trigger_type = self._trigger_type
|
||||
clone._threshold = self._threshold
|
||||
|
||||
clone._scores = []
|
||||
for score in self._scores:
|
||||
clone.add_score(score)
|
||||
clone.scores = self.scores
|
||||
|
||||
memo[id(self)] = clone
|
||||
|
||||
|
|
@ -97,6 +96,17 @@ class Trigger(object):
|
|||
check_type('tally trigger threshold', threshold, Real)
|
||||
self._threshold = threshold
|
||||
|
||||
@scores.setter
|
||||
def scores(self, scores):
|
||||
cv.check_type('trigger scores', scores, Iterable, basestring)
|
||||
|
||||
# Set scores making sure not to have duplicates
|
||||
self._scores = []
|
||||
for score in scores:
|
||||
if score not in self._scores:
|
||||
self._scores.append(score)
|
||||
|
||||
|
||||
def add_score(self, score):
|
||||
"""Add a score to the list of scores to be checked against the trigger.
|
||||
|
||||
|
|
@ -107,16 +117,11 @@ class Trigger(object):
|
|||
|
||||
"""
|
||||
|
||||
if not isinstance(score, basestring):
|
||||
msg = 'Unable to add score "{0}" to tally trigger since ' \
|
||||
'it is not a string'.format(score)
|
||||
raise ValueError(msg)
|
||||
|
||||
# If the score is already in the Tally, don't add it again
|
||||
if score in self._scores:
|
||||
return
|
||||
else:
|
||||
self._scores.append(score)
|
||||
warnings.warn('Trigger.add_score(...) has been deprecated and may be '
|
||||
'removed in a future version. Tally trigger scores should '
|
||||
'be defined using the scores property directly.',
|
||||
DeprecationWarning)
|
||||
self.scores.append(score)
|
||||
|
||||
def get_trigger_xml(self, element):
|
||||
"""Return XML representation of the trigger
|
||||
|
|
|
|||
|
|
@ -16,9 +16,6 @@ if sys.version_info[0] >= 3:
|
|||
basestring = str
|
||||
|
||||
|
||||
# DeprecationWarning filter for the Cell.add_surface(...) method
|
||||
warnings.simplefilter('always', DeprecationWarning)
|
||||
|
||||
# A static variable for auto-generated Cell IDs
|
||||
AUTO_CELL_ID = 10000
|
||||
|
||||
|
|
@ -286,6 +283,10 @@ class Cell(object):
|
|||
"""Add a half-space to the list of half-spaces whose intersection defines the
|
||||
cell.
|
||||
|
||||
.. deprecated:: 0.7.1
|
||||
Use the Cell.region property to directly specify a Region
|
||||
expression.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
surface : openmc.surface.Surface
|
||||
|
|
|
|||
2
setup.py
2
setup.py
|
|
@ -36,7 +36,7 @@ if have_setuptools:
|
|||
|
||||
# Optional dependencies
|
||||
'extras_require': {
|
||||
'pandas': ['pandas'],
|
||||
'pandas': ['pandas>=0.17.0'],
|
||||
'sparse' : ['scipy'],
|
||||
'vtk': ['vtk', 'silomesh'],
|
||||
'validate': ['lxml']
|
||||
|
|
|
|||
773
src/ace.F90
773
src/ace.F90
File diff suppressed because it is too large
Load diff
|
|
@ -1,298 +0,0 @@
|
|||
module ace_header
|
||||
|
||||
use constants, only: MAX_FILE_LEN, ZERO
|
||||
use dict_header, only: DictIntInt
|
||||
use endf_header, only: Tab1
|
||||
use multipole_header, only: MultipoleArray
|
||||
use secondary_header, only: SecondaryDistribution, AngleEnergyContainer
|
||||
use stl_vector, only: VectorInt
|
||||
|
||||
implicit none
|
||||
|
||||
!===============================================================================
|
||||
! REACTION contains the cross-section and secondary energy and angle
|
||||
! distributions for a single reaction in a continuous-energy ACE-format table
|
||||
!===============================================================================
|
||||
|
||||
type Reaction
|
||||
integer :: MT ! ENDF MT value
|
||||
real(8) :: Q_value ! Reaction Q value
|
||||
integer :: multiplicity ! Number of secondary particles released
|
||||
type(Tab1), pointer :: multiplicity_E => null() ! Energy-dependent neutron yield
|
||||
integer :: threshold ! Energy grid index of threshold
|
||||
logical :: scatter_in_cm ! scattering system in center-of-mass?
|
||||
logical :: multiplicity_with_E = .false. ! Flag to indicate E-dependent multiplicity
|
||||
real(8), allocatable :: sigma(:) ! Cross section values
|
||||
type(SecondaryDistribution) :: secondary
|
||||
|
||||
! Type-Bound procedures
|
||||
contains
|
||||
procedure :: clear => reaction_clear ! Deallocates Reaction
|
||||
end type Reaction
|
||||
|
||||
!===============================================================================
|
||||
! URRDATA contains probability tables for the unresolved resonance range.
|
||||
!===============================================================================
|
||||
|
||||
type UrrData
|
||||
integer :: n_energy ! # of incident neutron energies
|
||||
integer :: n_prob ! # of probabilities
|
||||
integer :: interp ! inteprolation (2=lin-lin, 5=log-log)
|
||||
integer :: inelastic_flag ! inelastic competition flag
|
||||
integer :: absorption_flag ! other absorption flag
|
||||
logical :: multiply_smooth ! multiply by smooth cross section?
|
||||
real(8), allocatable :: energy(:) ! incident energies
|
||||
real(8), allocatable :: prob(:,:,:) ! actual probabibility tables
|
||||
end type UrrData
|
||||
|
||||
!===============================================================================
|
||||
! NUCLIDE contains all the data for an ACE-format continuous-energy cross
|
||||
! section. The ACE format (A Compact ENDF format) is used in MCNP and several
|
||||
! other Monte Carlo codes.
|
||||
!===============================================================================
|
||||
|
||||
type Nuclide
|
||||
character(10) :: name ! name of nuclide, e.g. 92235.03c
|
||||
integer :: zaid ! Z and A identifier, e.g. 92235
|
||||
integer :: listing ! index in xs_listings
|
||||
real(8) :: awr ! weight of nucleus in neutron masses
|
||||
real(8) :: kT ! temperature in MeV (k*T)
|
||||
|
||||
! Linked list of indices in nuclides array of instances of this same nuclide
|
||||
type(VectorInt) :: nuc_list
|
||||
|
||||
! Energy grid information
|
||||
integer :: n_grid ! # of nuclide grid points
|
||||
integer, allocatable :: grid_index(:) ! log grid mapping indices
|
||||
real(8), allocatable :: energy(:) ! energy values corresponding to xs
|
||||
|
||||
! Microscopic cross sections
|
||||
real(8), allocatable :: total(:) ! total cross section
|
||||
real(8), allocatable :: elastic(:) ! elastic scattering
|
||||
real(8), allocatable :: fission(:) ! fission
|
||||
real(8), allocatable :: nu_fission(:) ! neutron production
|
||||
real(8), allocatable :: absorption(:) ! absorption (MT > 100)
|
||||
real(8), allocatable :: heating(:) ! heating
|
||||
|
||||
! Resonance scattering info
|
||||
logical :: resonant = .false. ! resonant scatterer?
|
||||
character(10) :: name_0K = '' ! name of 0K nuclide, e.g. 92235.00c
|
||||
character(16) :: scheme ! target velocity sampling scheme
|
||||
integer :: n_grid_0K ! number of 0K energy grid points
|
||||
real(8), allocatable :: energy_0K(:) ! energy grid for 0K xs
|
||||
real(8), allocatable :: elastic_0K(:) ! Microscopic elastic cross section
|
||||
real(8), allocatable :: xs_cdf(:) ! CDF of v_rel times cross section
|
||||
real(8) :: E_min ! lower cutoff energy for res scattering
|
||||
real(8) :: E_max ! upper cutoff energy for res scattering
|
||||
|
||||
! Fission information
|
||||
logical :: fissionable ! nuclide is fissionable?
|
||||
logical :: has_partial_fission ! nuclide has partial fission reactions?
|
||||
integer :: n_fission ! # of fission reactions
|
||||
integer, allocatable :: index_fission(:) ! indices in reactions
|
||||
|
||||
! Total fission neutron emission
|
||||
integer :: nu_t_type
|
||||
real(8), allocatable :: nu_t_data(:)
|
||||
|
||||
! Prompt fission neutron emission
|
||||
integer :: nu_p_type
|
||||
real(8), allocatable :: nu_p_data(:)
|
||||
|
||||
! Delayed fission neutron emission
|
||||
integer :: nu_d_type
|
||||
integer :: n_precursor ! # of delayed neutron precursors
|
||||
real(8), allocatable :: nu_d_data(:)
|
||||
real(8), allocatable :: nu_d_precursor_data(:)
|
||||
type(AngleEnergyContainer), allocatable :: nu_d_edist(:)
|
||||
|
||||
! Unresolved resonance data
|
||||
logical :: urr_present
|
||||
integer :: urr_inelastic
|
||||
type(UrrData), pointer :: urr_data => null()
|
||||
|
||||
! Multipole data
|
||||
logical :: mp_present = .false.
|
||||
type(MultipoleArray), pointer :: multipole => null()
|
||||
|
||||
! Reactions
|
||||
integer :: n_reaction ! # of reactions
|
||||
type(Reaction), allocatable :: reactions(:)
|
||||
type(DictIntInt) :: reaction_index ! map MT values to index in reactions
|
||||
! array; used at tally-time
|
||||
|
||||
! Type-Bound procedures
|
||||
contains
|
||||
procedure :: clear => nuclide_clear ! Deallocates Nuclide
|
||||
end type Nuclide
|
||||
|
||||
!===============================================================================
|
||||
! NUCLIDE0K temporarily contains all 0K cross section data and other parameters
|
||||
! needed to treat resonance scattering before transferring them to NUCLIDE
|
||||
!===============================================================================
|
||||
|
||||
type Nuclide0K
|
||||
character(10) :: nuclide ! name of nuclide, e.g. U-238
|
||||
character(16) :: scheme = 'ares' ! target velocity sampling scheme
|
||||
character(10) :: name ! name of nuclide, e.g. 92235.03c
|
||||
character(10) :: name_0K ! name of 0K nuclide, e.g. 92235.00c
|
||||
real(8) :: E_min = 0.01e-6_8 ! lower cutoff energy for res scattering
|
||||
real(8) :: E_max = 1000.0e-6_8 ! upper cutoff energy for res scattering
|
||||
end type Nuclide0K
|
||||
|
||||
!===============================================================================
|
||||
! DISTENERGYSAB contains the secondary energy/angle distributions for inelastic
|
||||
! thermal scattering collisions which utilize a continuous secondary energy
|
||||
! representation.
|
||||
!===============================================================================
|
||||
|
||||
type DistEnergySab
|
||||
integer :: n_e_out
|
||||
real(8), allocatable :: e_out(:)
|
||||
real(8), allocatable :: e_out_pdf(:)
|
||||
real(8), allocatable :: e_out_cdf(:)
|
||||
real(8), allocatable :: mu(:,:)
|
||||
end type DistEnergySab
|
||||
|
||||
!===============================================================================
|
||||
! SALPHABETA contains S(a,b) data for thermal neutron scattering, typically off
|
||||
! of light isotopes such as water, graphite, Be, etc
|
||||
!===============================================================================
|
||||
|
||||
type SAlphaBeta
|
||||
character(10) :: name ! name of table, e.g. lwtr.10t
|
||||
real(8) :: awr ! weight of nucleus in neutron masses
|
||||
real(8) :: kT ! temperature in MeV (k*T)
|
||||
integer :: n_zaid ! Number of valid zaids
|
||||
integer, allocatable :: zaid(:) ! List of valid Z and A identifiers, e.g. 6012
|
||||
|
||||
! threshold for S(a,b) treatment (usually ~4 eV)
|
||||
real(8) :: threshold_inelastic
|
||||
real(8) :: threshold_elastic = ZERO
|
||||
|
||||
! Inelastic scattering data
|
||||
integer :: n_inelastic_e_in ! # of incoming E for inelastic
|
||||
integer :: n_inelastic_e_out ! # of outgoing E for inelastic
|
||||
integer :: n_inelastic_mu ! # of outgoing angles for inelastic
|
||||
integer :: secondary_mode ! secondary mode (equal/skewed/continuous)
|
||||
real(8), allocatable :: inelastic_e_in(:)
|
||||
real(8), allocatable :: inelastic_sigma(:)
|
||||
! The following are used only if secondary_mode is 0 or 1
|
||||
real(8), allocatable :: inelastic_e_out(:,:)
|
||||
real(8), allocatable :: inelastic_mu(:,:,:)
|
||||
! The following is used only if secondary_mode is 3
|
||||
! The different implementation is necessary because the continuous
|
||||
! representation has a variable number of outgoing energy points for each
|
||||
! incoming energy
|
||||
type(DistEnergySab), allocatable :: inelastic_data(:) ! One for each Ein
|
||||
|
||||
! Elastic scattering data
|
||||
integer :: elastic_mode ! elastic mode (discrete/exact)
|
||||
integer :: n_elastic_e_in ! # of incoming E for elastic
|
||||
integer :: n_elastic_mu ! # of outgoing angles for elastic
|
||||
real(8), allocatable :: elastic_e_in(:)
|
||||
real(8), allocatable :: elastic_P(:)
|
||||
real(8), allocatable :: elastic_mu(:,:)
|
||||
end type SAlphaBeta
|
||||
|
||||
!===============================================================================
|
||||
! XSLISTING contains data read from a cross_sections.xml file
|
||||
!===============================================================================
|
||||
|
||||
type XsListing
|
||||
character(12) :: name ! table name, e.g. 92235.70c
|
||||
character(12) :: alias ! table alias, e.g. U-235.70c
|
||||
integer :: type ! type of table (cont-E neutron, S(A,b), etc)
|
||||
integer :: zaid ! ZAID identifier = 1000*Z + A
|
||||
integer :: filetype ! ASCII or BINARY
|
||||
integer :: location ! location of table within library
|
||||
integer :: recl ! record length for library
|
||||
integer :: entries ! number of entries per record
|
||||
real(8) :: awr ! atomic weight ratio (# of neutron masses)
|
||||
real(8) :: kT ! Boltzmann constant * temperature (MeV)
|
||||
logical :: metastable ! is this nuclide metastable?
|
||||
character(MAX_FILE_LEN) :: path ! path to library containing table
|
||||
end type XsListing
|
||||
|
||||
!===============================================================================
|
||||
! NUCLIDEMICROXS contains cached microscopic cross sections for a
|
||||
! particular nuclide at the current energy
|
||||
!===============================================================================
|
||||
|
||||
type NuclideMicroXS
|
||||
integer :: index_grid ! index on nuclide energy grid
|
||||
integer :: index_temp ! temperature index for nuclide
|
||||
real(8) :: last_E = ZERO ! last evaluated energy
|
||||
real(8) :: interp_factor ! interpolation factor on nuc. energy grid
|
||||
real(8) :: total ! microscropic total xs
|
||||
real(8) :: elastic ! microscopic elastic scattering xs
|
||||
real(8) :: absorption ! microscopic absorption xs
|
||||
real(8) :: fission ! microscopic fission xs
|
||||
real(8) :: nu_fission ! microscopic production xs
|
||||
|
||||
! Information for S(a,b) use
|
||||
integer :: index_sab ! index in sab_tables (zero means no table)
|
||||
integer :: last_index_sab = 0 ! index in sab_tables last used by this nuclide
|
||||
real(8) :: elastic_sab ! microscopic elastic scattering on S(a,b) table
|
||||
|
||||
! Information for URR probability table use
|
||||
logical :: use_ptable ! in URR range with probability tables?
|
||||
real(8) :: last_prn
|
||||
|
||||
! Information for Doppler broadening
|
||||
real(8) :: last_sqrtkT = ZERO ! last temperature in sqrt(Boltzmann constant * temperature (MeV))
|
||||
end type NuclideMicroXS
|
||||
|
||||
!===============================================================================
|
||||
! MATERIALMACROXS contains cached macroscopic cross sections for the material a
|
||||
! particle is traveling through
|
||||
!===============================================================================
|
||||
|
||||
type MaterialMacroXS
|
||||
real(8) :: total ! macroscopic total xs
|
||||
real(8) :: elastic ! macroscopic elastic scattering xs
|
||||
real(8) :: absorption ! macroscopic absorption xs
|
||||
real(8) :: fission ! macroscopic fission xs
|
||||
real(8) :: nu_fission ! macroscopic production xs
|
||||
end type MaterialMacroXS
|
||||
|
||||
contains
|
||||
|
||||
!===============================================================================
|
||||
! REACTION_CLEAR resets and deallocates data in Reaction.
|
||||
!===============================================================================
|
||||
|
||||
subroutine reaction_clear(this)
|
||||
class(Reaction), intent(inout) :: this ! The Reaction object to clear
|
||||
|
||||
if (associated(this % multiplicity_E)) deallocate(this % multiplicity_E)
|
||||
end subroutine reaction_clear
|
||||
|
||||
!===============================================================================
|
||||
! NUCLIDE_CLEAR resets and deallocates data in Nuclide.
|
||||
!===============================================================================
|
||||
|
||||
subroutine nuclide_clear(this)
|
||||
class(Nuclide), intent(inout) :: this
|
||||
|
||||
integer :: i ! Loop counter
|
||||
|
||||
if (associated(this % urr_data)) deallocate(this % urr_data)
|
||||
|
||||
if (this % mp_present) then
|
||||
deallocate(this % multipole)
|
||||
end if
|
||||
|
||||
if (allocated(this % reactions)) then
|
||||
do i = 1, size(this % reactions)
|
||||
call this % reactions(i) % clear()
|
||||
end do
|
||||
end if
|
||||
|
||||
call this % reaction_index % clear()
|
||||
|
||||
if (associated(this % multipole)) deallocate(this % multipole)
|
||||
|
||||
end subroutine nuclide_clear
|
||||
|
||||
end module ace_header
|
||||
29
src/angleenergy_header.F90
Normal file
29
src/angleenergy_header.F90
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
module angleenergy_header
|
||||
|
||||
!===============================================================================
|
||||
! ANGLEENERGY (abstract) defines a correlated or uncorrelated angle-energy
|
||||
! distribution that is a function of incoming energy. Each derived type must
|
||||
! implement a sample() subroutine that returns an outgoing energy and scattering
|
||||
! cosine given an incoming energy.
|
||||
!===============================================================================
|
||||
|
||||
type, abstract :: AngleEnergy
|
||||
contains
|
||||
procedure(angleenergy_sample_), deferred :: sample
|
||||
end type AngleEnergy
|
||||
|
||||
abstract interface
|
||||
subroutine angleenergy_sample_(this, E_in, E_out, mu)
|
||||
import AngleEnergy
|
||||
class(AngleEnergy), intent(in) :: this
|
||||
real(8), intent(in) :: E_in
|
||||
real(8), intent(out) :: E_out
|
||||
real(8), intent(out) :: mu
|
||||
end subroutine angleenergy_sample_
|
||||
end interface
|
||||
|
||||
type :: AngleEnergyContainer
|
||||
class(AngleEnergy), allocatable :: obj
|
||||
end type AngleEnergyContainer
|
||||
|
||||
end module angleenergy_header
|
||||
|
|
@ -14,7 +14,7 @@ module bank_header
|
|||
real(C_DOUBLE) :: wgt ! weight of bank site
|
||||
real(C_DOUBLE) :: xyz(3) ! location of bank particle
|
||||
real(C_DOUBLE) :: uvw(3) ! diretional cosines
|
||||
real(C_DOUBLE) :: E ! energy
|
||||
real(C_DOUBLE) :: E ! energy / energy group if in MG mode.
|
||||
integer(C_INT) :: delayed_group ! delayed group
|
||||
end type Bank
|
||||
|
||||
|
|
|
|||
|
|
@ -49,13 +49,13 @@ contains
|
|||
|
||||
subroutine compute_xs()
|
||||
|
||||
use constants, only: FILTER_MESH, FILTER_ENERGYIN, FILTER_ENERGYOUT, &
|
||||
FILTER_SURFACE, IN_RIGHT, OUT_RIGHT, IN_FRONT, &
|
||||
OUT_FRONT, IN_TOP, OUT_TOP, CMFD_NOACCEL, ZERO, &
|
||||
ONE, TINY_BIT
|
||||
use constants, only: FILTER_MESH, FILTER_ENERGYIN, FILTER_ENERGYOUT, &
|
||||
FILTER_SURFACE, IN_RIGHT, OUT_RIGHT, IN_FRONT, &
|
||||
OUT_FRONT, IN_TOP, OUT_TOP, CMFD_NOACCEL, ZERO, &
|
||||
ONE, TINY_BIT
|
||||
use error, only: fatal_error
|
||||
use global, only: cmfd, n_cmfd_tallies, cmfd_tallies, meshes,&
|
||||
matching_bins
|
||||
matching_bins
|
||||
use mesh, only: mesh_indices_to_bin
|
||||
use mesh_header, only: RegularMesh
|
||||
use string, only: to_str
|
||||
|
|
@ -625,10 +625,10 @@ contains
|
|||
|
||||
subroutine compute_dhat()
|
||||
|
||||
use constants, only: CMFD_NOACCEL, ZERO
|
||||
use global, only: cmfd, cmfd_coremap, dhat_reset
|
||||
use output, only: write_message
|
||||
use string, only: to_str
|
||||
use constants, only: CMFD_NOACCEL, ZERO
|
||||
use global, only: cmfd, cmfd_coremap, dhat_reset
|
||||
use output, only: write_message
|
||||
use string, only: to_str
|
||||
|
||||
integer :: nx ! maximum number of cells in x direction
|
||||
integer :: ny ! maximum number of cells in y direction
|
||||
|
|
|
|||
|
|
@ -89,9 +89,9 @@ contains
|
|||
|
||||
subroutine calc_fission_source()
|
||||
|
||||
use constants, only: CMFD_NOACCEL, ZERO, TWO
|
||||
use global, only: cmfd, cmfd_coremap, master, entropy_on, current_batch
|
||||
use string, only: to_str
|
||||
use constants, only: CMFD_NOACCEL, ZERO, TWO
|
||||
use global, only: cmfd, cmfd_coremap, master, entropy_on, current_batch
|
||||
use string, only: to_str
|
||||
|
||||
#ifdef MPI
|
||||
use global, only: mpi_err
|
||||
|
|
|
|||
|
|
@ -45,14 +45,14 @@ contains
|
|||
subroutine read_cmfd_xml()
|
||||
|
||||
use constants, only: ZERO, ONE
|
||||
use error, only: fatal_error, warning
|
||||
use error, only: fatal_error, warning
|
||||
use global
|
||||
use output, only: write_message
|
||||
use string, only: to_lower
|
||||
use output, only: write_message
|
||||
use string, only: to_lower
|
||||
use xml_interface
|
||||
use, intrinsic :: ISO_FORTRAN_ENV
|
||||
|
||||
integer :: i
|
||||
integer :: i, g
|
||||
integer :: ng
|
||||
integer :: n_params
|
||||
integer, allocatable :: iarray(:)
|
||||
|
|
@ -70,7 +70,7 @@ contains
|
|||
inquire(FILE=filename, EXIST=file_exists)
|
||||
if (.not. file_exists) then
|
||||
! CMFD is optional unless it is in on from settings
|
||||
if (cmfd_on) then
|
||||
if (cmfd_run) then
|
||||
call fatal_error("No CMFD XML file, '" // trim(filename) // "' does not&
|
||||
& exist!")
|
||||
end if
|
||||
|
|
@ -102,6 +102,23 @@ contains
|
|||
if(.not.allocated(cmfd%egrid)) allocate(cmfd%egrid(ng))
|
||||
call get_node_array(node_mesh, "energy", cmfd%egrid)
|
||||
cmfd % indices(4) = ng - 1 ! sets energy group dimension
|
||||
! If using MG mode, check to see if these egrid points at least match
|
||||
! the MG Data breakpoints
|
||||
if (.not. run_CE) then
|
||||
do i = 1, ng
|
||||
found = .false.
|
||||
do g = 1, energy_groups + 1
|
||||
if (cmfd % egrid(i) == energy_bins(g)) then
|
||||
found = .true.
|
||||
exit
|
||||
end if
|
||||
end do
|
||||
if (.not. found) then
|
||||
call fatal_error("CMFD energy mesh boundaries must align with&
|
||||
& boundaries of multi-group data!")
|
||||
end if
|
||||
end do
|
||||
end if
|
||||
else
|
||||
if(.not.allocated(cmfd % egrid)) allocate(cmfd % egrid(2))
|
||||
cmfd % egrid = [ ZERO, 20.0_8 ]
|
||||
|
|
|
|||
|
|
@ -11,10 +11,10 @@ module constants
|
|||
integer, parameter :: VERSION_RELEASE = 1
|
||||
|
||||
! Revision numbers for binary files
|
||||
integer, parameter :: REVISION_STATEPOINT = 14
|
||||
integer, parameter :: REVISION_STATEPOINT = 15
|
||||
integer, parameter :: REVISION_PARTICLE_RESTART = 1
|
||||
integer, parameter :: REVISION_TRACK = 1
|
||||
integer, parameter :: REVISION_SUMMARY = 2
|
||||
integer, parameter :: REVISION_SUMMARY = 3
|
||||
|
||||
! ============================================================================
|
||||
! ADJUSTABLE PARAMETERS
|
||||
|
|
@ -163,9 +163,14 @@ module constants
|
|||
|
||||
! Angular distribution type
|
||||
integer, parameter :: &
|
||||
ANGLE_ISOTROPIC = 1, & ! Isotropic angular distribution
|
||||
ANGLE_32_EQUI = 2, & ! 32 equiprobable bins
|
||||
ANGLE_TABULAR = 3 ! Tabular angular distribution
|
||||
ANGLE_ISOTROPIC = 1, & ! Isotropic angular distribution (CE)
|
||||
ANGLE_32_EQUI = 2, & ! 32 equiprobable bins (CE)
|
||||
ANGLE_TABULAR = 3, & ! Tabular angular distribution (CE or MG)
|
||||
ANGLE_LEGENDRE = 4, & ! Legendre angular distribution (MG)
|
||||
ANGLE_HISTOGRAM = 5 ! Histogram angular distribution (MG)
|
||||
|
||||
! Number of mu bins to use when converting Legendres to tabular type
|
||||
integer, parameter :: DEFAULT_NMU = 33
|
||||
|
||||
! Secondary energy mode for S(a,b) inelastic scattering
|
||||
integer, parameter :: &
|
||||
|
|
@ -209,12 +214,23 @@ module constants
|
|||
ACE_THERMAL = 2, & ! thermal S(a,b) scattering data
|
||||
ACE_DOSIMETRY = 3 ! dosimetry cross sections
|
||||
|
||||
! MGXS Table Types
|
||||
integer, parameter :: &
|
||||
MGXS_ISOTROPIC = 1, & ! Isotropically Weighted Data
|
||||
MGXS_ANGLE = 2 ! Data by Angular Bins
|
||||
|
||||
! Fission neutron emission (nu) type
|
||||
integer, parameter :: &
|
||||
NU_NONE = 0, & ! No nu values (non-fissionable)
|
||||
NU_POLYNOMIAL = 1, & ! Nu values given by polynomial
|
||||
NU_TABULAR = 2 ! Nu values given by tabular distribution
|
||||
|
||||
! Secondary particle emission type
|
||||
integer, parameter :: &
|
||||
EMISSION_PROMPT = 1, & ! Prompt emission of secondary particle
|
||||
EMISSION_DELAYED = 2, & ! Delayed emission of secondary particle
|
||||
EMISSION_TOTAL = 3 ! Yield represents total emission (prompt + delayed)
|
||||
|
||||
! Cross section filetypes
|
||||
integer, parameter :: &
|
||||
ASCII = 1, & ! ASCII cross section file
|
||||
|
|
@ -357,10 +373,11 @@ module constants
|
|||
! ============================================================================
|
||||
! RANDOM NUMBER STREAM CONSTANTS
|
||||
|
||||
integer, parameter :: N_STREAMS = 3
|
||||
integer, parameter :: STREAM_TRACKING = 1
|
||||
integer, parameter :: STREAM_TALLIES = 2
|
||||
integer, parameter :: STREAM_SOURCE = 3
|
||||
integer, parameter :: N_STREAMS = 4
|
||||
integer, parameter :: STREAM_TRACKING = 1
|
||||
integer, parameter :: STREAM_TALLIES = 2
|
||||
integer, parameter :: STREAM_SOURCE = 3
|
||||
integer, parameter :: STREAM_URR_PTABLE = 4
|
||||
|
||||
! ============================================================================
|
||||
! MISCELLANEOUS CONSTANTS
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
module cross_section
|
||||
|
||||
use ace_header, only: Nuclide, SAlphaBeta, Reaction, UrrData
|
||||
use constants
|
||||
use energy_grid, only: grid_method, log_spacing
|
||||
use error, only: fatal_error
|
||||
use fission, only: nu_total
|
||||
use global
|
||||
use list_header, only: ListElemInt
|
||||
use material_header, only: Material
|
||||
use math, only: w, broaden_n_polynomials
|
||||
use multipole_header, only: FORM_RM, FORM_MLBW, MP_EA, RM_RT, RM_RA, RM_RF, &
|
||||
MLBW_RT, MLBW_RX, MLBW_RA, MLBW_RF, FIT_T, FIT_A, FIT_F, &
|
||||
MultipoleArray, max_poly, max_L, max_poles
|
||||
MLBW_RT, MLBW_RX, MLBW_RA, MLBW_RF, FIT_T, FIT_A,&
|
||||
FIT_F, MultipoleArray, max_poly, max_L, max_poles
|
||||
use nuclide_header
|
||||
use particle_header, only: Particle
|
||||
use random_lcg, only: prn
|
||||
use random_lcg, only: prn, future_prn, prn_set_stream
|
||||
use sab_header, only: SAlphaBeta
|
||||
use search, only: binary_search
|
||||
|
||||
implicit none
|
||||
|
|
@ -164,8 +164,8 @@ contains
|
|||
integer :: i_high ! upper logarithmic mapping index
|
||||
real(8) :: f ! interp factor on nuclide energy grid
|
||||
real(8) :: sigT, sigA, sigF ! Intermediate multipole variables
|
||||
type(Nuclide), pointer :: nuc
|
||||
type(Material), pointer :: mat
|
||||
type(NuclideCE), pointer :: nuc
|
||||
type(Material), pointer :: mat
|
||||
|
||||
! Set pointer to nuclide and material
|
||||
nuc => nuclides(i_nuclide)
|
||||
|
|
@ -185,7 +185,7 @@ contains
|
|||
|
||||
if (nuc % fissionable) then
|
||||
micro_xs(i_nuclide) % fission = sigF
|
||||
micro_xs(i_nuclide) % nu_fission = sigF * nu_total(nuc, E)
|
||||
micro_xs(i_nuclide) % nu_fission = sigF * nuc % nu(E, EMISSION_TOTAL)
|
||||
else
|
||||
micro_xs(i_nuclide) % fission = ZERO
|
||||
micro_xs(i_nuclide) % nu_fission = ZERO
|
||||
|
|
@ -403,154 +403,136 @@ contains
|
|||
integer, intent(in) :: i_nuclide ! index into nuclides array
|
||||
real(8), intent(in) :: E ! energy
|
||||
|
||||
integer :: i ! loop index
|
||||
integer :: i_energy ! index for energy
|
||||
integer :: i_low ! band index at lower bounding energy
|
||||
integer :: i_up ! band index at upper bounding energy
|
||||
integer :: same_nuc_idx ! index of same nuclide
|
||||
real(8) :: f ! interpolation factor
|
||||
real(8) :: r ! pseudo-random number
|
||||
real(8) :: elastic ! elastic cross section
|
||||
real(8) :: capture ! (n,gamma) cross section
|
||||
real(8) :: fission ! fission cross section
|
||||
real(8) :: inelastic ! inelastic cross section
|
||||
logical :: same_nuc ! do we know the xs for this nuclide at this energy?
|
||||
type(UrrData), pointer :: urr
|
||||
type(Nuclide), pointer :: nuc
|
||||
|
||||
micro_xs(i_nuclide) % use_ptable = .true.
|
||||
|
||||
! get pointer to probability table
|
||||
nuc => nuclides(i_nuclide)
|
||||
urr => nuc % urr_data
|
||||
associate (nuc => nuclides(i_nuclide), urr => nuclides(i_nuclide) % urr_data)
|
||||
! determine energy table
|
||||
i_energy = 1
|
||||
do
|
||||
if (E < urr % energy(i_energy + 1)) exit
|
||||
i_energy = i_energy + 1
|
||||
end do
|
||||
|
||||
! determine energy table
|
||||
i_energy = 1
|
||||
do
|
||||
if (E < urr % energy(i_energy + 1)) exit
|
||||
i_energy = i_energy + 1
|
||||
end do
|
||||
! determine interpolation factor on table
|
||||
f = (E - urr % energy(i_energy)) / &
|
||||
(urr % energy(i_energy + 1) - urr % energy(i_energy))
|
||||
|
||||
! determine interpolation factor on table
|
||||
f = (E - urr % energy(i_energy)) / &
|
||||
(urr % energy(i_energy + 1) - urr % energy(i_energy))
|
||||
! sample probability table using the cumulative distribution
|
||||
|
||||
! sample probability table using the cumulative distribution
|
||||
! Random numbers for xs calculation are sampled from a separated stream.
|
||||
! This guarantees the randomness and, at the same time, makes sure we reuse
|
||||
! random number for the same nuclide at different temperatures, therefore
|
||||
! preserving correlation of temperature in probability tables.
|
||||
call prn_set_stream(STREAM_URR_PTABLE)
|
||||
r = future_prn(int(nuc_zaid_dict % get_key(nuc % zaid), 8))
|
||||
call prn_set_stream(STREAM_TRACKING)
|
||||
|
||||
! if we're dealing with a nuclide that we've previously encountered at
|
||||
! this energy but a different temperature, use the original random number to
|
||||
! preserve correlation of temperature in probability tables
|
||||
same_nuc = .false.
|
||||
do i = 1, nuc % nuc_list % size()
|
||||
if (E /= ZERO .and. E == micro_xs(nuc % nuc_list % data(i)) % last_E) then
|
||||
same_nuc = .true.
|
||||
same_nuc_idx = i
|
||||
exit
|
||||
end if
|
||||
end do
|
||||
i_low = 1
|
||||
do
|
||||
if (urr % prob(i_energy, URR_CUM_PROB, i_low) > r) exit
|
||||
i_low = i_low + 1
|
||||
end do
|
||||
i_up = 1
|
||||
do
|
||||
if (urr % prob(i_energy + 1, URR_CUM_PROB, i_up) > r) exit
|
||||
i_up = i_up + 1
|
||||
end do
|
||||
|
||||
if (same_nuc) then
|
||||
r = micro_xs(nuc % nuc_list % data(same_nuc_idx)) % last_prn
|
||||
else
|
||||
r = prn()
|
||||
micro_xs(i_nuclide) % last_prn = r
|
||||
end if
|
||||
! determine elastic, fission, and capture cross sections from probability
|
||||
! table
|
||||
if (urr % interp == LINEAR_LINEAR) then
|
||||
elastic = (ONE - f) * urr % prob(i_energy, URR_ELASTIC, i_low) + &
|
||||
f * urr % prob(i_energy + 1, URR_ELASTIC, i_up)
|
||||
fission = (ONE - f) * urr % prob(i_energy, URR_FISSION, i_low) + &
|
||||
f * urr % prob(i_energy + 1, URR_FISSION, i_up)
|
||||
capture = (ONE - f) * urr % prob(i_energy, URR_N_GAMMA, i_low) + &
|
||||
f * urr % prob(i_energy + 1, URR_N_GAMMA, i_up)
|
||||
elseif (urr % interp == LOG_LOG) then
|
||||
! Get logarithmic interpolation factor
|
||||
f = log(E / urr % energy(i_energy)) / &
|
||||
log(urr % energy(i_energy + 1) / urr % energy(i_energy))
|
||||
|
||||
i_low = 1
|
||||
do
|
||||
if (urr % prob(i_energy, URR_CUM_PROB, i_low) > r) exit
|
||||
i_low = i_low + 1
|
||||
end do
|
||||
i_up = 1
|
||||
do
|
||||
if (urr % prob(i_energy + 1, URR_CUM_PROB, i_up) > r) exit
|
||||
i_up = i_up + 1
|
||||
end do
|
||||
|
||||
! determine elastic, fission, and capture cross sections from probability
|
||||
! table
|
||||
if (urr % interp == LINEAR_LINEAR) then
|
||||
elastic = (ONE - f) * urr % prob(i_energy, URR_ELASTIC, i_low) + &
|
||||
f * urr % prob(i_energy + 1, URR_ELASTIC, i_up)
|
||||
fission = (ONE - f) * urr % prob(i_energy, URR_FISSION, i_low) + &
|
||||
f * urr % prob(i_energy + 1, URR_FISSION, i_up)
|
||||
capture = (ONE - f) * urr % prob(i_energy, URR_N_GAMMA, i_low) + &
|
||||
f * urr % prob(i_energy + 1, URR_N_GAMMA, i_up)
|
||||
elseif (urr % interp == LOG_LOG) then
|
||||
! Get logarithmic interpolation factor
|
||||
f = log(E / urr % energy(i_energy)) / &
|
||||
log(urr % energy(i_energy + 1) / urr % energy(i_energy))
|
||||
|
||||
! Calculate elastic cross section/factor
|
||||
elastic = ZERO
|
||||
if (urr % prob(i_energy, URR_ELASTIC, i_low) > ZERO .and. &
|
||||
urr % prob(i_energy + 1, URR_ELASTIC, i_up) > ZERO) then
|
||||
elastic = exp((ONE - f) * log(urr % prob(i_energy, URR_ELASTIC, &
|
||||
i_low)) + f * log(urr % prob(i_energy + 1, URR_ELASTIC, &
|
||||
i_up)))
|
||||
end if
|
||||
|
||||
! Calculate fission cross section/factor
|
||||
fission = ZERO
|
||||
if (urr % prob(i_energy, URR_FISSION, i_low) > ZERO .and. &
|
||||
urr % prob(i_energy + 1, URR_FISSION, i_up) > ZERO) then
|
||||
fission = exp((ONE - f) * log(urr % prob(i_energy, URR_FISSION, &
|
||||
i_low)) + f * log(urr % prob(i_energy + 1, URR_FISSION, &
|
||||
i_up)))
|
||||
end if
|
||||
|
||||
! Calculate capture cross section/factor
|
||||
capture = ZERO
|
||||
if (urr % prob(i_energy, URR_N_GAMMA, i_low) > ZERO .and. &
|
||||
urr % prob(i_energy + 1, URR_N_GAMMA, i_up) > ZERO) then
|
||||
capture = exp((ONE - f) * log(urr % prob(i_energy, URR_N_GAMMA, &
|
||||
i_low)) + f * log(urr % prob(i_energy + 1, URR_N_GAMMA, &
|
||||
i_up)))
|
||||
end if
|
||||
end if
|
||||
|
||||
! Determine treatment of inelastic scattering
|
||||
inelastic = ZERO
|
||||
if (urr % inelastic_flag > 0) then
|
||||
! Get index on energy grid and interpolation factor
|
||||
i_energy = micro_xs(i_nuclide) % index_grid
|
||||
f = micro_xs(i_nuclide) % interp_factor
|
||||
|
||||
! Determine inelastic scattering cross section
|
||||
associate (rxn => nuc % reactions(nuc % urr_inelastic))
|
||||
if (i_energy >= rxn % threshold) then
|
||||
inelastic = (ONE - f) * rxn % sigma(i_energy - rxn%threshold + 1) + &
|
||||
f * rxn % sigma(i_energy - rxn%threshold + 2)
|
||||
! Calculate elastic cross section/factor
|
||||
elastic = ZERO
|
||||
if (urr % prob(i_energy, URR_ELASTIC, i_low) > ZERO .and. &
|
||||
urr % prob(i_energy + 1, URR_ELASTIC, i_up) > ZERO) then
|
||||
elastic = exp((ONE - f) * log(urr % prob(i_energy, URR_ELASTIC, &
|
||||
i_low)) + f * log(urr % prob(i_energy + 1, URR_ELASTIC, &
|
||||
i_up)))
|
||||
end if
|
||||
end associate
|
||||
end if
|
||||
|
||||
! Multiply by smooth cross-section if needed
|
||||
if (urr % multiply_smooth) then
|
||||
elastic = elastic * micro_xs(i_nuclide) % elastic
|
||||
capture = capture * (micro_xs(i_nuclide) % absorption - &
|
||||
micro_xs(i_nuclide) % fission)
|
||||
fission = fission * micro_xs(i_nuclide) % fission
|
||||
end if
|
||||
! Calculate fission cross section/factor
|
||||
fission = ZERO
|
||||
if (urr % prob(i_energy, URR_FISSION, i_low) > ZERO .and. &
|
||||
urr % prob(i_energy + 1, URR_FISSION, i_up) > ZERO) then
|
||||
fission = exp((ONE - f) * log(urr % prob(i_energy, URR_FISSION, &
|
||||
i_low)) + f * log(urr % prob(i_energy + 1, URR_FISSION, &
|
||||
i_up)))
|
||||
end if
|
||||
|
||||
! Check for negative values
|
||||
if (elastic < ZERO) elastic = ZERO
|
||||
if (fission < ZERO) fission = ZERO
|
||||
if (capture < ZERO) capture = ZERO
|
||||
! Calculate capture cross section/factor
|
||||
capture = ZERO
|
||||
if (urr % prob(i_energy, URR_N_GAMMA, i_low) > ZERO .and. &
|
||||
urr % prob(i_energy + 1, URR_N_GAMMA, i_up) > ZERO) then
|
||||
capture = exp((ONE - f) * log(urr % prob(i_energy, URR_N_GAMMA, &
|
||||
i_low)) + f * log(urr % prob(i_energy + 1, URR_N_GAMMA, &
|
||||
i_up)))
|
||||
end if
|
||||
end if
|
||||
|
||||
! Set elastic, absorption, fission, and total cross sections. Note that the
|
||||
! total cross section is calculated as sum of partials rather than using the
|
||||
! table-provided value
|
||||
micro_xs(i_nuclide) % elastic = elastic
|
||||
micro_xs(i_nuclide) % absorption = capture + fission
|
||||
micro_xs(i_nuclide) % fission = fission
|
||||
micro_xs(i_nuclide) % total = elastic + inelastic + capture + fission
|
||||
! Determine treatment of inelastic scattering
|
||||
inelastic = ZERO
|
||||
if (urr % inelastic_flag > 0) then
|
||||
! Get index on energy grid and interpolation factor
|
||||
i_energy = micro_xs(i_nuclide) % index_grid
|
||||
f = micro_xs(i_nuclide) % interp_factor
|
||||
|
||||
! Determine nu-fission cross section
|
||||
if (nuc % fissionable) then
|
||||
micro_xs(i_nuclide) % nu_fission = nu_total(nuc, E) * &
|
||||
micro_xs(i_nuclide) % fission
|
||||
end if
|
||||
! Determine inelastic scattering cross section
|
||||
associate (rxn => nuc % reactions(nuc % urr_inelastic))
|
||||
if (i_energy >= rxn % threshold) then
|
||||
inelastic = (ONE - f) * rxn % sigma(i_energy - rxn%threshold + 1) + &
|
||||
f * rxn % sigma(i_energy - rxn%threshold + 2)
|
||||
end if
|
||||
end associate
|
||||
end if
|
||||
|
||||
! Multiply by smooth cross-section if needed
|
||||
if (urr % multiply_smooth) then
|
||||
elastic = elastic * micro_xs(i_nuclide) % elastic
|
||||
capture = capture * (micro_xs(i_nuclide) % absorption - &
|
||||
micro_xs(i_nuclide) % fission)
|
||||
fission = fission * micro_xs(i_nuclide) % fission
|
||||
end if
|
||||
|
||||
! Check for negative values
|
||||
if (elastic < ZERO) elastic = ZERO
|
||||
if (fission < ZERO) fission = ZERO
|
||||
if (capture < ZERO) capture = ZERO
|
||||
|
||||
! Set elastic, absorption, fission, and total cross sections. Note that the
|
||||
! total cross section is calculated as sum of partials rather than using the
|
||||
! table-provided value
|
||||
micro_xs(i_nuclide) % elastic = elastic
|
||||
micro_xs(i_nuclide) % absorption = capture + fission
|
||||
micro_xs(i_nuclide) % fission = fission
|
||||
micro_xs(i_nuclide) % total = elastic + inelastic + capture + fission
|
||||
|
||||
! Determine nu-fission cross section
|
||||
if (nuc % fissionable) then
|
||||
micro_xs(i_nuclide) % nu_fission = nuc % nu(E, EMISSION_TOTAL) * &
|
||||
micro_xs(i_nuclide) % fission
|
||||
end if
|
||||
end associate
|
||||
|
||||
end subroutine calculate_urr_xs
|
||||
|
||||
|
|
@ -768,9 +750,9 @@ contains
|
|||
!===============================================================================
|
||||
|
||||
pure function elastic_xs_0K(E, nuc) result(xs_out)
|
||||
real(8), intent(in) :: E ! trial energy
|
||||
type(Nuclide), intent(in) :: nuc ! target nuclide at temperature
|
||||
real(8) :: xs_out ! 0K xs at trial energy
|
||||
real(8), intent(in) :: E ! trial energy
|
||||
type(NuclideCE), intent(in) :: nuc ! target nuclide at temperature
|
||||
real(8) :: xs_out ! 0K xs at trial energy
|
||||
|
||||
integer :: i_grid ! index on nuclide energy grid
|
||||
real(8) :: f ! interp factor on nuclide energy grid
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
module distribution_multivariate
|
||||
|
||||
use constants, only: ONE, TWO, PI
|
||||
use constants, only: ONE, TWO, PI
|
||||
use distribution_univariate, only: Distribution
|
||||
use math, only: rotate_angle
|
||||
use random_lcg, only: prn
|
||||
use random_lcg, only: prn
|
||||
use math, only: rotate_angle
|
||||
|
||||
implicit none
|
||||
|
||||
|
|
@ -16,15 +16,15 @@ module distribution_multivariate
|
|||
type, abstract :: UnitSphereDistribution
|
||||
real(8) :: reference_uvw(3)
|
||||
contains
|
||||
procedure(iSample), deferred :: sample
|
||||
procedure(unitsphere_distribution_sample_), deferred :: sample
|
||||
end type UnitSphereDistribution
|
||||
|
||||
abstract interface
|
||||
function iSample(this) result(uvw)
|
||||
function unitsphere_distribution_sample_(this) result(uvw)
|
||||
import UnitSphereDistribution
|
||||
class(UnitSphereDistribution), intent(in) :: this
|
||||
real(8) :: uvw(3)
|
||||
end function iSample
|
||||
end function unitsphere_distribution_sample_
|
||||
end interface
|
||||
|
||||
!===============================================================================
|
||||
|
|
@ -58,15 +58,15 @@ module distribution_multivariate
|
|||
|
||||
type, abstract :: SpatialDistribution
|
||||
contains
|
||||
procedure(iSampleSpatial), deferred :: sample
|
||||
procedure(spatial_distribution_sample_), deferred :: sample
|
||||
end type SpatialDistribution
|
||||
|
||||
abstract interface
|
||||
function iSampleSpatial(this) result(xyz)
|
||||
function spatial_distribution_sample_(this) result(xyz)
|
||||
import SpatialDistribution
|
||||
class(SpatialDistribution), intent(in) :: this
|
||||
real(8) :: xyz(3)
|
||||
end function iSampleSpatial
|
||||
end function spatial_distribution_sample_
|
||||
end interface
|
||||
|
||||
type, extends(SpatialDistribution) :: CartesianIndependent
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
module distribution_univariate
|
||||
|
||||
use constants, only: ZERO, ONE, HALF, HISTOGRAM, LINEAR_LINEAR, &
|
||||
use constants, only: ZERO, ONE, HALF, HISTOGRAM, LINEAR_LINEAR, &
|
||||
MAX_LINE_LEN, MAX_WORD_LEN
|
||||
use error, only: fatal_error
|
||||
use math, only: maxwell_spectrum, watt_spectrum
|
||||
use error, only: fatal_error
|
||||
use random_lcg, only: prn
|
||||
use string, only: to_lower
|
||||
use math, only: maxwell_spectrum, watt_spectrum
|
||||
use string, only: to_lower
|
||||
use xml_interface
|
||||
|
||||
implicit none
|
||||
|
|
@ -16,7 +16,7 @@ module distribution_univariate
|
|||
|
||||
type, abstract :: Distribution
|
||||
contains
|
||||
procedure(iSample), deferred :: sample
|
||||
procedure(distribution_sample_), deferred :: sample
|
||||
end type Distribution
|
||||
|
||||
type DistributionContainer
|
||||
|
|
@ -24,11 +24,11 @@ module distribution_univariate
|
|||
end type DistributionContainer
|
||||
|
||||
abstract interface
|
||||
function iSample(this) result(x)
|
||||
function distribution_sample_(this) result(x)
|
||||
import Distribution
|
||||
class(Distribution), intent(in) :: this
|
||||
real(8) :: x
|
||||
end function iSample
|
||||
end function distribution_sample_
|
||||
end interface
|
||||
|
||||
!===============================================================================
|
||||
|
|
|
|||
|
|
@ -4,16 +4,15 @@ module eigenvalue
|
|||
use message_passing
|
||||
#endif
|
||||
|
||||
use constants, only: ZERO
|
||||
use error, only: fatal_error, warning
|
||||
use constants, only: ZERO
|
||||
use error, only: fatal_error, warning
|
||||
use global
|
||||
use math, only: t_percentile
|
||||
use mesh, only: count_bank_sites
|
||||
use mesh_header, only: RegularMesh
|
||||
use particle_header, only: Particle
|
||||
use random_lcg, only: prn, set_particle_seed, prn_skip
|
||||
use search, only: binary_search
|
||||
use string, only: to_str
|
||||
use math, only: t_percentile
|
||||
use mesh, only: count_bank_sites
|
||||
use mesh_header, only: RegularMesh
|
||||
use random_lcg, only: prn, set_particle_seed, advance_prn_seed
|
||||
use search, only: binary_search
|
||||
use string, only: to_str
|
||||
|
||||
implicit none
|
||||
|
||||
|
|
@ -100,7 +99,7 @@ contains
|
|||
|
||||
call set_particle_seed(int((current_batch - 1)*gen_per_batch + &
|
||||
current_gen,8))
|
||||
call prn_skip(start)
|
||||
call advance_prn_seed(start)
|
||||
|
||||
! Determine how many fission sites we need to sample from the source bank
|
||||
! and the probability for selecting a site.
|
||||
|
|
|
|||
|
|
@ -1,12 +1,51 @@
|
|||
module endf_header
|
||||
|
||||
implicit none
|
||||
use constants, only: ZERO, HISTOGRAM, LINEAR_LINEAR, LINEAR_LOG, &
|
||||
LOG_LINEAR, LOG_LOG
|
||||
use search, only: binary_search
|
||||
|
||||
implicit none
|
||||
|
||||
type, abstract :: Function1D
|
||||
contains
|
||||
procedure(function1d_evaluate_), deferred :: evaluate
|
||||
end type Function1D
|
||||
|
||||
abstract interface
|
||||
pure function function1d_evaluate_(this, x) result(y)
|
||||
import Function1D
|
||||
class(Function1D), intent(in) :: this
|
||||
real(8), intent(in) :: x
|
||||
real(8) :: y
|
||||
end function function1d_evaluate_
|
||||
end interface
|
||||
|
||||
!===============================================================================
|
||||
! TAB1 represents a one-dimensional interpolable function
|
||||
! CONSTANT1D represents a constant one-dimensional function
|
||||
!===============================================================================
|
||||
|
||||
type Tab1
|
||||
type, extends(Function1D) :: Constant1D
|
||||
real(8) :: y
|
||||
contains
|
||||
procedure :: evaluate => constant1d_evaluate
|
||||
end type Constant1D
|
||||
|
||||
!===============================================================================
|
||||
! POLYNOMIAL represents a one-dimensional function expressed as a polynomial
|
||||
!===============================================================================
|
||||
|
||||
type, extends(Function1D) :: Polynomial
|
||||
real(8), allocatable :: coef(:) ! coefficients
|
||||
contains
|
||||
procedure :: evaluate => polynomial_evaluate
|
||||
procedure :: from_ace => polynomial_from_ace
|
||||
end type Polynomial
|
||||
|
||||
!===============================================================================
|
||||
! TABULATED1D represents a one-dimensional interpolable function
|
||||
!===============================================================================
|
||||
|
||||
type, extends(Function1D) :: Tabulated1D
|
||||
integer :: n_regions = 0 ! # of interpolation regions
|
||||
integer, allocatable :: nbt(:) ! values separating interpolation regions
|
||||
integer, allocatable :: int(:) ! interpolation scheme
|
||||
|
|
@ -14,18 +53,78 @@ module endf_header
|
|||
real(8), allocatable :: x(:) ! values of abscissa
|
||||
real(8), allocatable :: y(:) ! values of ordinate
|
||||
contains
|
||||
procedure :: from_ace
|
||||
end type Tab1
|
||||
procedure :: from_ace => tabulated1d_from_ace
|
||||
procedure :: evaluate => tabulated1d_evaluate
|
||||
end type Tabulated1D
|
||||
|
||||
contains
|
||||
|
||||
subroutine from_ace(this, xss, idx)
|
||||
class(Tab1), intent(inout) :: this
|
||||
!===============================================================================
|
||||
! Constant1D implementation
|
||||
!===============================================================================
|
||||
|
||||
pure function constant1d_evaluate(this, x) result(y)
|
||||
class(Constant1D), intent(in) :: this
|
||||
real(8), intent(in) :: x
|
||||
real(8) :: y
|
||||
|
||||
y = this % y
|
||||
end function constant1d_evaluate
|
||||
|
||||
!===============================================================================
|
||||
! Polynomial implementation
|
||||
!===============================================================================
|
||||
|
||||
subroutine polynomial_from_ace(this, xss, idx)
|
||||
class(Polynomial), intent(inout) :: this
|
||||
real(8), intent(in) :: xss(:)
|
||||
integer, intent(in) :: idx
|
||||
|
||||
integer :: nc ! number of coefficients (order - 1)
|
||||
|
||||
! Clear space
|
||||
if (allocated(this % coef)) deallocate(this % coef)
|
||||
|
||||
! Determine number of coefficients
|
||||
nc = nint(xss(idx))
|
||||
|
||||
! Allocate space for and read coefficients
|
||||
allocate(this % coef(nc))
|
||||
this % coef(:) = xss(idx + 1 : idx + nc)
|
||||
end subroutine polynomial_from_ace
|
||||
|
||||
pure function polynomial_evaluate(this, x) result(y)
|
||||
class(Polynomial), intent(in) :: this
|
||||
real(8), intent(in) :: x
|
||||
real(8) :: y
|
||||
|
||||
integer :: i
|
||||
|
||||
! Use Horner's rule to evaluate polynomial. Note that coefficients are
|
||||
! ordered in increasing powers of x.
|
||||
y = ZERO
|
||||
do i = size(this % coef), 1, -1
|
||||
y = y*x + this % coef(i)
|
||||
end do
|
||||
end function polynomial_evaluate
|
||||
|
||||
!===============================================================================
|
||||
! Tabulated1D implementation
|
||||
!===============================================================================
|
||||
|
||||
subroutine tabulated1d_from_ace(this, xss, idx)
|
||||
class(Tabulated1D), intent(inout) :: this
|
||||
real(8), intent(in) :: xss(:)
|
||||
integer, intent(in) :: idx
|
||||
|
||||
integer :: nr, ne
|
||||
|
||||
! Clear space
|
||||
if (allocated(this % nbt)) deallocate(this % nbt)
|
||||
if (allocated(this % int)) deallocate(this % int)
|
||||
if (allocated(this % x)) deallocate(this % x)
|
||||
if (allocated(this % y)) deallocate(this % y)
|
||||
|
||||
! Determine number of regions
|
||||
nr = nint(xss(idx))
|
||||
this%n_regions = nr
|
||||
|
|
@ -47,6 +146,81 @@ contains
|
|||
allocate(this%y(ne))
|
||||
this%x(:) = xss(idx + 2*nr + 2 : idx + 2*nr + 1 + ne)
|
||||
this%y(:) = xss(idx + 2*nr + 2 + ne : idx + 2*nr + 1 + 2*ne)
|
||||
end subroutine from_ace
|
||||
end subroutine tabulated1d_from_ace
|
||||
|
||||
pure function tabulated1d_evaluate(this, x) result(y)
|
||||
class(Tabulated1D), intent(in) :: this
|
||||
real(8), intent(in) :: x ! x value to find y at
|
||||
real(8) :: y ! y(x)
|
||||
|
||||
integer :: i ! bin in which to interpolate
|
||||
integer :: j ! index for interpolation region
|
||||
integer :: n_regions ! number of interpolation regions
|
||||
integer :: n_pairs ! number of tabulated values
|
||||
integer :: interp ! ENDF interpolation scheme
|
||||
real(8) :: r ! interpolation factor
|
||||
real(8) :: x0, x1 ! bounding x values
|
||||
real(8) :: y0, y1 ! bounding y values
|
||||
|
||||
! determine number of interpolation regions and pairs
|
||||
n_regions = this % n_regions
|
||||
n_pairs = this % n_pairs
|
||||
|
||||
! find which bin the abscissa is in -- if the abscissa is outside the
|
||||
! tabulated range, the first or last point is chosen, i.e. no interpolation
|
||||
! is done outside the energy range
|
||||
if (x < this % x(1)) then
|
||||
y = this % y(1)
|
||||
return
|
||||
elseif (x > this % x(n_pairs)) then
|
||||
y = this % y(n_pairs)
|
||||
return
|
||||
else
|
||||
i = binary_search(this % x, n_pairs, x)
|
||||
end if
|
||||
|
||||
! determine interpolation scheme
|
||||
if (n_regions == 0) then
|
||||
interp = LINEAR_LINEAR
|
||||
elseif (n_regions == 1) then
|
||||
interp = this % int(1)
|
||||
elseif (n_regions > 1) then
|
||||
do j = 1, n_regions
|
||||
if (i < this % nbt(j)) then
|
||||
interp = this % int(j)
|
||||
exit
|
||||
end if
|
||||
end do
|
||||
end if
|
||||
|
||||
! handle special case of histogram interpolation
|
||||
if (interp == HISTOGRAM) then
|
||||
y = this % y(i)
|
||||
return
|
||||
end if
|
||||
|
||||
! determine bounding values
|
||||
x0 = this % x(i)
|
||||
x1 = this % x(i + 1)
|
||||
y0 = this % y(i)
|
||||
y1 = this % y(i + 1)
|
||||
|
||||
! determine interpolation factor and interpolated value
|
||||
select case (interp)
|
||||
case (LINEAR_LINEAR)
|
||||
r = (x - x0)/(x1 - x0)
|
||||
y = y0 + r*(y1 - y0)
|
||||
case (LINEAR_LOG)
|
||||
r = log(x/x0)/log(x1/x0)
|
||||
y = y0 + r*(y1 - y0)
|
||||
case (LOG_LINEAR)
|
||||
r = (x - x0)/(x1 - x0)
|
||||
y = y0*exp(r*log(y1/y0))
|
||||
case (LOG_LOG)
|
||||
r = log(x/x0)/log(x1/x0)
|
||||
y = y0*exp(r*log(y1/y0))
|
||||
end select
|
||||
|
||||
end function tabulated1d_evaluate
|
||||
|
||||
end module endf_header
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
module energy_distribution
|
||||
|
||||
use constants, only: ZERO, ONE, TWO, PI, HISTOGRAM, LINEAR_LINEAR
|
||||
use endf_header, only: Tab1
|
||||
use interpolation, only: interpolate_tab1
|
||||
use math, only: maxwell_spectrum, watt_spectrum
|
||||
use random_lcg, only: prn
|
||||
use search, only: binary_search
|
||||
use constants, only: ZERO, ONE, TWO, PI, HISTOGRAM, LINEAR_LINEAR
|
||||
use endf_header, only: Tabulated1D
|
||||
use math, only: maxwell_spectrum, watt_spectrum
|
||||
use random_lcg, only: prn
|
||||
use search, only: binary_search
|
||||
|
||||
!===============================================================================
|
||||
! ENERGYDISTRIBUTION (abstract) defines an energy distribution that is a
|
||||
|
|
@ -16,16 +15,16 @@ module energy_distribution
|
|||
|
||||
type, abstract :: EnergyDistribution
|
||||
contains
|
||||
procedure(iSampleEnergy), deferred :: sample
|
||||
procedure(energy_distribution_sample_), deferred :: sample
|
||||
end type EnergyDistribution
|
||||
|
||||
abstract interface
|
||||
function iSampleEnergy(this, E_in) result(E_out)
|
||||
function energy_distribution_sample_(this, E_in) result(E_out)
|
||||
import EnergyDistribution
|
||||
class(EnergyDistribution), intent(in) :: this
|
||||
real(8), intent(in) :: E_in
|
||||
real(8) :: E_out
|
||||
end function iSampleEnergy
|
||||
end function energy_distribution_sample_
|
||||
end interface
|
||||
|
||||
type :: EnergyDistributionContainer
|
||||
|
|
@ -83,8 +82,8 @@ module energy_distribution
|
|||
integer :: n_region
|
||||
integer, allocatable :: breakpoints(:)
|
||||
integer, allocatable :: interpolation(:)
|
||||
real(8), allocatable :: energy_in(:)
|
||||
type(CTTable), allocatable :: energy_out(:)
|
||||
real(8), allocatable :: energy(:)
|
||||
type(CTTable), allocatable :: distribution(:)
|
||||
contains
|
||||
procedure :: sample => continuous_sample
|
||||
end type ContinuousTabular
|
||||
|
|
@ -95,7 +94,7 @@ module energy_distribution
|
|||
!===============================================================================
|
||||
|
||||
type, extends(EnergyDistribution) :: MaxwellEnergy
|
||||
type(Tab1) :: theta ! incoming-energy-dependent parameter
|
||||
type(Tabulated1D) :: theta ! incoming-energy-dependent parameter
|
||||
real(8) :: u ! restriction energy
|
||||
contains
|
||||
procedure :: sample => maxwellenergy_sample
|
||||
|
|
@ -107,7 +106,7 @@ module energy_distribution
|
|||
!===============================================================================
|
||||
|
||||
type, extends(EnergyDistribution) :: Evaporation
|
||||
type(Tab1) :: theta
|
||||
type(Tabulated1D) :: theta
|
||||
real(8) :: u
|
||||
contains
|
||||
procedure :: sample => evaporation_sample
|
||||
|
|
@ -119,28 +118,13 @@ module energy_distribution
|
|||
!===============================================================================
|
||||
|
||||
type, extends(EnergyDistribution) :: WattEnergy
|
||||
type(Tab1) :: a
|
||||
type(Tab1) :: b
|
||||
type(Tabulated1D) :: a
|
||||
type(Tabulated1D) :: b
|
||||
real(8) :: u
|
||||
contains
|
||||
procedure :: sample => watt_sample
|
||||
end type WattEnergy
|
||||
|
||||
!===============================================================================
|
||||
! NBODYPHASESPACE gives the energy distribution for particles emitted from
|
||||
! neutron and charged-particle reactions. This corresponds to ACE law 66 and
|
||||
! ENDF File 6, LAW=6.
|
||||
!===============================================================================
|
||||
|
||||
type, extends(EnergyDistribution) :: NBodyPhaseSpace
|
||||
integer :: n_bodies
|
||||
real(8) :: mass_ratio
|
||||
real(8) :: A
|
||||
real(8) :: Q
|
||||
contains
|
||||
procedure :: sample => nbody_sample
|
||||
end type NBodyPhaseSpace
|
||||
|
||||
contains
|
||||
|
||||
function equiprobable_sample(this, E_in) result(E_out)
|
||||
|
|
@ -202,6 +186,7 @@ contains
|
|||
end if
|
||||
end function equiprobable_sample
|
||||
|
||||
|
||||
function level_inelastic_sample(this, E_in) result(E_out)
|
||||
class(LevelInelastic), intent(in) :: this
|
||||
real(8), intent(in) :: E_in
|
||||
|
|
@ -210,6 +195,7 @@ contains
|
|||
E_out = this%mass_ratio*(E_in - this%threshold)
|
||||
end function level_inelastic_sample
|
||||
|
||||
|
||||
function continuous_sample(this, E_in) result(E_out)
|
||||
class(ContinuousTabular), intent(in) :: this
|
||||
real(8), intent(in) :: E_in ! incoming energy
|
||||
|
|
@ -238,17 +224,17 @@ contains
|
|||
|
||||
! Find energy bin and calculate interpolation factor -- if the energy is
|
||||
! outside the range of the tabulated energies, choose the first or last bins
|
||||
n_energy_in = size(this%energy_in)
|
||||
if (E_in < this%energy_in(1)) then
|
||||
n_energy_in = size(this%energy)
|
||||
if (E_in < this%energy(1)) then
|
||||
i = 1
|
||||
r = ZERO
|
||||
elseif (E_in > this%energy_in(n_energy_in)) then
|
||||
elseif (E_in > this%energy(n_energy_in)) then
|
||||
i = n_energy_in - 1
|
||||
r = ONE
|
||||
else
|
||||
i = binary_search(this%energy_in, n_energy_in, E_in)
|
||||
r = (E_in - this%energy_in(i)) / &
|
||||
(this%energy_in(i+1) - this%energy_in(i))
|
||||
i = binary_search(this%energy, n_energy_in, E_in)
|
||||
r = (E_in - this%energy(i)) / &
|
||||
(this%energy(i+1) - this%energy(i))
|
||||
end if
|
||||
|
||||
! Sample between the ith and (i+1)th bin
|
||||
|
|
@ -263,23 +249,23 @@ contains
|
|||
end if
|
||||
|
||||
! Interpolation for energy E1 and EK
|
||||
n_energy_out = size(this%energy_out(i)%e_out)
|
||||
E_i_1 = this%energy_out(i)%e_out(1)
|
||||
E_i_K = this%energy_out(i)%e_out(n_energy_out)
|
||||
n_energy_out = size(this%distribution(i)%e_out)
|
||||
E_i_1 = this%distribution(i)%e_out(1)
|
||||
E_i_K = this%distribution(i)%e_out(n_energy_out)
|
||||
|
||||
n_energy_out = size(this%energy_out(i+1)%e_out)
|
||||
E_i1_1 = this%energy_out(i+1)%e_out(1)
|
||||
E_i1_K = this%energy_out(i+1)%e_out(n_energy_out)
|
||||
n_energy_out = size(this%distribution(i+1)%e_out)
|
||||
E_i1_1 = this%distribution(i+1)%e_out(1)
|
||||
E_i1_K = this%distribution(i+1)%e_out(n_energy_out)
|
||||
|
||||
E_1 = E_i_1 + r*(E_i1_1 - E_i_1)
|
||||
E_K = E_i_K + r*(E_i1_K - E_i_K)
|
||||
|
||||
! Determine outgoing energy bin
|
||||
n_energy_out = size(this%energy_out(l)%e_out)
|
||||
n_energy_out = size(this%distribution(l)%e_out)
|
||||
r1 = prn()
|
||||
c_k = this%energy_out(l)%c(1)
|
||||
c_k = this%distribution(l)%c(1)
|
||||
do k = 1, n_energy_out - 1
|
||||
c_k1 = this%energy_out(l)%c(k+1)
|
||||
c_k1 = this%distribution(l)%c(k+1)
|
||||
if (r1 < c_k1) exit
|
||||
c_k = c_k1
|
||||
end do
|
||||
|
|
@ -287,9 +273,9 @@ contains
|
|||
! Check to make sure k is <= NP - 1
|
||||
k = min(k, n_energy_out - 1)
|
||||
|
||||
E_l_k = this%energy_out(l)%e_out(k)
|
||||
p_l_k = this%energy_out(l)%p(k)
|
||||
if (this%energy_out(l)%interpolation == HISTOGRAM) then
|
||||
E_l_k = this%distribution(l)%e_out(k)
|
||||
p_l_k = this%distribution(l)%p(k)
|
||||
if (this%distribution(l)%interpolation == HISTOGRAM) then
|
||||
! Histogram interpolation
|
||||
if (p_l_k > ZERO) then
|
||||
E_out = E_l_k + (r1 - c_k)/p_l_k
|
||||
|
|
@ -297,10 +283,10 @@ contains
|
|||
E_out = E_l_k
|
||||
end if
|
||||
|
||||
elseif (this%energy_out(l)%interpolation == LINEAR_LINEAR) then
|
||||
elseif (this%distribution(l)%interpolation == LINEAR_LINEAR) then
|
||||
! Linear-linear interpolation
|
||||
E_l_k1 = this%energy_out(l)%e_out(k+1)
|
||||
p_l_k1 = this%energy_out(l)%p(k+1)
|
||||
E_l_k1 = this%distribution(l)%e_out(k+1)
|
||||
p_l_k1 = this%distribution(l)%p(k+1)
|
||||
|
||||
frac = (p_l_k1 - p_l_k)/(E_l_k1 - E_l_k)
|
||||
if (frac == ZERO) then
|
||||
|
|
@ -321,6 +307,7 @@ contains
|
|||
end if
|
||||
end function continuous_sample
|
||||
|
||||
|
||||
function maxwellenergy_sample(this, E_in) result(E_out)
|
||||
class(MaxwellEnergy), intent(in) :: this
|
||||
real(8), intent(in) :: E_in ! incoming energy
|
||||
|
|
@ -329,7 +316,7 @@ contains
|
|||
real(8) :: theta ! Maxwell distribution parameter
|
||||
|
||||
! Get temperature corresponding to incoming energy
|
||||
theta = interpolate_tab1(this%theta, E_in)
|
||||
theta = this % theta % evaluate(E_in)
|
||||
|
||||
do
|
||||
! Sample maxwell fission spectrum
|
||||
|
|
@ -349,9 +336,9 @@ contains
|
|||
real(8) :: x, y, v
|
||||
|
||||
! Get temperature corresponding to incoming energy
|
||||
theta = interpolate_tab1(this%theta, E_in)
|
||||
theta = this % theta % evaluate(E_in)
|
||||
|
||||
y = (E_in - this%U)/theta
|
||||
y = (E_in - this%u)/theta
|
||||
v = 1 - exp(-y)
|
||||
|
||||
! Sample outgoing energy based on evaporation spectrum probability
|
||||
|
|
@ -372,10 +359,10 @@ contains
|
|||
real(8) :: a, b ! Watt spectrum parameters
|
||||
|
||||
! Determine Watt parameter 'a' from tabulated function
|
||||
a = interpolate_tab1(this%a, E_in)
|
||||
a = this % a % evaluate(E_in)
|
||||
|
||||
! Determine Watt parameter 'b' from tabulated function
|
||||
b = interpolate_tab1(this%b, E_in)
|
||||
b = this % b % evaluate(E_in)
|
||||
|
||||
do
|
||||
! Sample energy-dependent Watt fission spectrum
|
||||
|
|
@ -386,44 +373,4 @@ contains
|
|||
end do
|
||||
end function watt_sample
|
||||
|
||||
function nbody_sample(this, E_in) result(E_out)
|
||||
class(NBodyPhaseSpace), intent(in) :: this
|
||||
real(8), intent(in) :: E_in ! incoming energy
|
||||
real(8) :: E_out ! sampled outgoing energy
|
||||
|
||||
real(8) :: Ap ! total mass of particles in neutron masses
|
||||
real(8) :: E_max ! maximum possible COM energy
|
||||
real(8) :: x, y, v
|
||||
real(8) :: r1, r2, r3, r4, r5, r6
|
||||
|
||||
! Determine E_max parameter
|
||||
Ap = this%mass_ratio
|
||||
E_max = (Ap - ONE)/Ap * (this%A/(this%A + ONE)*E_in + this%Q)
|
||||
|
||||
! x is essentially a Maxwellian distribution
|
||||
x = maxwell_spectrum(ONE)
|
||||
|
||||
select case (this%n_bodies)
|
||||
case (3)
|
||||
y = maxwell_spectrum(ONE)
|
||||
case (4)
|
||||
r1 = prn()
|
||||
r2 = prn()
|
||||
r3 = prn()
|
||||
y = -log(r1*r2*r3)
|
||||
case (5)
|
||||
r1 = prn()
|
||||
r2 = prn()
|
||||
r3 = prn()
|
||||
r4 = prn()
|
||||
r5 = prn()
|
||||
r6 = prn()
|
||||
y = -log(r1*r2*r3*r4) - log(r5) * cos(PI/TWO*r6)**2
|
||||
end select
|
||||
|
||||
! Now determine v and E_out
|
||||
v = x/(x+y)
|
||||
E_out = E_max * v
|
||||
end function nbody_sample
|
||||
|
||||
end module energy_distribution
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ contains
|
|||
integer :: i ! index in nuclides array
|
||||
integer :: j ! index in materials array
|
||||
type(ListReal) :: list
|
||||
type(Nuclide), pointer :: nuc
|
||||
type(NuclideCE), pointer :: nuc
|
||||
type(Material), pointer :: mat
|
||||
|
||||
call write_message("Creating unionized energy grid...", 5)
|
||||
|
|
@ -70,7 +70,7 @@ contains
|
|||
real(8) :: E_max ! Maximum energy in MeV
|
||||
real(8) :: E_min ! Minimum energy in MeV
|
||||
real(8), allocatable :: umesh(:) ! Equally log-spaced energy grid
|
||||
type(Nuclide), pointer :: nuc
|
||||
type(NuclideCE), pointer :: nuc
|
||||
|
||||
! Set minimum/maximum energies
|
||||
E_max = energy_max_neutron
|
||||
|
|
@ -179,7 +179,7 @@ contains
|
|||
integer :: index_e ! index on union energy grid
|
||||
real(8) :: union_energy ! energy on union grid
|
||||
real(8) :: energy ! energy on nuclide grid
|
||||
type(Nuclide), pointer :: nuc
|
||||
type(NuclideCE), pointer :: nuc
|
||||
type(Material), pointer :: mat
|
||||
|
||||
do k = 1, n_materials
|
||||
|
|
|
|||
161
src/fission.F90
161
src/fission.F90
|
|
@ -1,161 +0,0 @@
|
|||
module fission
|
||||
|
||||
use ace_header, only: Nuclide
|
||||
use constants
|
||||
use error, only: fatal_error
|
||||
use interpolation, only: interpolate_tab1
|
||||
use search, only: binary_search
|
||||
|
||||
implicit none
|
||||
|
||||
contains
|
||||
|
||||
!===============================================================================
|
||||
! NU_TOTAL calculates the total number of neutrons emitted per fission for a
|
||||
! given nuclide and incoming neutron energy
|
||||
!===============================================================================
|
||||
|
||||
pure function nu_total(nuc, E) result(nu)
|
||||
type(Nuclide), intent(in) :: nuc ! nuclide from which to find nu
|
||||
real(8), intent(in) :: E ! energy of incoming neutron
|
||||
real(8) :: nu ! number of total neutrons emitted per fission
|
||||
|
||||
integer :: i ! loop index
|
||||
integer :: NC ! number of polynomial coefficients
|
||||
real(8) :: c ! polynomial coefficient
|
||||
|
||||
if (nuc % nu_t_type == NU_NONE) then
|
||||
nu = ERROR_REAL
|
||||
elseif (nuc % nu_t_type == NU_POLYNOMIAL) then
|
||||
! determine number of coefficients
|
||||
NC = int(nuc % nu_t_data(1))
|
||||
|
||||
! sum up polynomial in energy
|
||||
nu = ZERO
|
||||
do i = 0, NC - 1
|
||||
c = nuc % nu_t_data(i+2)
|
||||
nu = nu + c * E**i
|
||||
end do
|
||||
elseif (nuc % nu_t_type == NU_TABULAR) then
|
||||
! use ENDF interpolation laws to determine nu
|
||||
nu = interpolate_tab1(nuc % nu_t_data, E)
|
||||
end if
|
||||
|
||||
end function nu_total
|
||||
|
||||
!===============================================================================
|
||||
! NU_PROMPT calculates the total number of prompt neutrons emitted per fission
|
||||
! for a given nuclide and incoming neutron energy
|
||||
!===============================================================================
|
||||
|
||||
pure function nu_prompt(nuc, E) result(nu)
|
||||
type(Nuclide), intent(in) :: nuc ! nuclide from which to find nu
|
||||
real(8), intent(in) :: E ! energy of incoming neutron
|
||||
real(8) :: nu ! number of prompt neutrons emitted per fission
|
||||
|
||||
integer :: i ! loop index
|
||||
integer :: NC ! number of polynomial coefficients
|
||||
real(8) :: c ! polynomial coefficient
|
||||
|
||||
if (nuc % nu_p_type == NU_NONE) then
|
||||
! since no prompt or delayed data is present, this means all neutron
|
||||
! emission is prompt -- WARNING: This currently returns zero. The calling
|
||||
! routine needs to know this situation is occurring since we don't want
|
||||
! to call nu_total unnecessarily if it has already been called.
|
||||
nu = ZERO
|
||||
elseif (nuc % nu_p_type == NU_POLYNOMIAL) then
|
||||
! determine number of coefficients
|
||||
NC = int(nuc % nu_p_data(1))
|
||||
|
||||
! sum up polynomial in energy
|
||||
nu = ZERO
|
||||
do i = 0, NC - 1
|
||||
c = nuc % nu_p_data(i+2)
|
||||
nu = nu + c * E**i
|
||||
end do
|
||||
elseif (nuc % nu_p_type == NU_TABULAR) then
|
||||
! use ENDF interpolation laws to determine nu
|
||||
nu = interpolate_tab1(nuc % nu_p_data, E)
|
||||
end if
|
||||
|
||||
end function nu_prompt
|
||||
|
||||
!===============================================================================
|
||||
! NU_DELAYED calculates the total number of delayed neutrons emitted per fission
|
||||
! for a given nuclide and incoming neutron energy
|
||||
!===============================================================================
|
||||
|
||||
pure function nu_delayed(nuc, E) result(nu)
|
||||
type(Nuclide), intent(in) :: nuc ! nuclide from which to find nu
|
||||
real(8), intent(in) :: E ! energy of incoming neutron
|
||||
real(8) :: nu ! number of delayed neutrons emitted per fission
|
||||
|
||||
if (nuc % nu_d_type == NU_NONE) then
|
||||
! since no prompt or delayed data is present, this means all neutron
|
||||
! emission is prompt -- WARNING: This currently returns zero. The calling
|
||||
! routine needs to know this situation is occurring since we don't want
|
||||
! to call nu_delayed unnecessarily if it has already been called.
|
||||
nu = ZERO
|
||||
elseif (nuc % nu_d_type == NU_TABULAR) then
|
||||
! use ENDF interpolation laws to determine nu
|
||||
nu = interpolate_tab1(nuc % nu_d_data, E)
|
||||
end if
|
||||
|
||||
end function nu_delayed
|
||||
|
||||
!===============================================================================
|
||||
! YIELD_DELAYED calculates the fractional yield of delayed neutrons emitted for
|
||||
! a given nuclide and incoming neutron energy in a given delayed group.
|
||||
!===============================================================================
|
||||
|
||||
pure function yield_delayed(nuc, E, g) result(yield)
|
||||
type(Nuclide), intent(in) :: nuc ! nuclide from which to find nu
|
||||
real(8), intent(in) :: E ! energy of incoming neutron
|
||||
real(8) :: yield ! delayed neutron precursor yield
|
||||
integer, intent(in) :: g ! the delayed neutron precursor group
|
||||
integer :: d ! precursor group
|
||||
integer :: lc ! index before start of energies/nu values
|
||||
integer :: NR ! number of interpolation regions
|
||||
integer :: NE ! number of energies tabulated
|
||||
|
||||
yield = ZERO
|
||||
|
||||
if (g > nuc % n_precursor .or. g < 1) then
|
||||
! if the precursor group is outside the range of precursor groups for
|
||||
! the input nuclide, return ZERO.
|
||||
yield = ZERO
|
||||
else if (nuc % nu_d_type == NU_NONE) then
|
||||
! since no prompt or delayed data is present, this means all neutron
|
||||
! emission is prompt -- WARNING: This currently returns zero. The calling
|
||||
! routine needs to know this situation is occurring since we don't want
|
||||
! to call yield_delayed unnecessarily if it has already been called.
|
||||
yield = ZERO
|
||||
else if (nuc % nu_d_type == NU_TABULAR) then
|
||||
|
||||
lc = 1
|
||||
|
||||
! loop over delayed groups and determine the yield for the desired group
|
||||
do d = 1, nuc % n_precursor
|
||||
|
||||
! determine number of interpolation regions and energies
|
||||
NR = int(nuc % nu_d_precursor_data(lc + 1))
|
||||
NE = int(nuc % nu_d_precursor_data(lc + 2 + 2*NR))
|
||||
|
||||
! check if this is the desired group
|
||||
if (d == g) then
|
||||
|
||||
! determine delayed neutron precursor yield for group g
|
||||
yield = interpolate_tab1(nuc % nu_d_precursor_data( &
|
||||
lc+1:lc+2+2*NR+2*NE), E)
|
||||
|
||||
exit
|
||||
end if
|
||||
|
||||
! advance pointer
|
||||
lc = lc + 2 + 2*NR + 2*NE + 1
|
||||
end do
|
||||
end if
|
||||
|
||||
end function yield_delayed
|
||||
|
||||
end module fission
|
||||
|
|
@ -31,12 +31,10 @@ module geometry_header
|
|||
integer :: outer ! universe to tile outside the lat
|
||||
logical :: is_3d ! Lattice has cells on z axis
|
||||
integer, allocatable :: offset(:,:,:,:) ! Distribcell offsets
|
||||
|
||||
contains
|
||||
|
||||
procedure(are_valid_indices_), deferred :: are_valid_indices
|
||||
procedure(get_indices_), deferred :: get_indices
|
||||
procedure(get_local_xyz_), deferred :: get_local_xyz
|
||||
contains
|
||||
procedure(lattice_are_valid_indices_), deferred :: are_valid_indices
|
||||
procedure(lattice_get_indices_), deferred :: get_indices
|
||||
procedure(lattice_get_local_xyz_), deferred :: get_local_xyz
|
||||
end type Lattice
|
||||
|
||||
abstract interface
|
||||
|
|
@ -45,33 +43,33 @@ module geometry_header
|
|||
! ARE_VALID_INDICES returns .true. if the given lattice indices fit within the
|
||||
! bounds of the lattice. Returns false otherwise.
|
||||
|
||||
function are_valid_indices_(this, i_xyz) result(is_valid)
|
||||
function lattice_are_valid_indices_(this, i_xyz) result(is_valid)
|
||||
import Lattice
|
||||
class(Lattice), intent(in) :: this
|
||||
integer, intent(in) :: i_xyz(3)
|
||||
logical :: is_valid
|
||||
end function are_valid_indices_
|
||||
end function lattice_are_valid_indices_
|
||||
|
||||
!===============================================================================
|
||||
! GET_INDICES returns the indices in a lattice for the given global xyz.
|
||||
|
||||
function get_indices_(this, global_xyz) result(i_xyz)
|
||||
function lattice_get_indices_(this, global_xyz) result(i_xyz)
|
||||
import Lattice
|
||||
class(Lattice), intent(in) :: this
|
||||
real(8), intent(in) :: global_xyz(3)
|
||||
integer :: i_xyz(3)
|
||||
end function get_indices_
|
||||
end function lattice_get_indices_
|
||||
|
||||
!===============================================================================
|
||||
! GET_LOCAL_XYZ returns the translated local version of the given global xyz.
|
||||
|
||||
function get_local_xyz_(this, global_xyz, i_xyz) result(local_xyz)
|
||||
function lattice_get_local_xyz_(this, global_xyz, i_xyz) result(local_xyz)
|
||||
import Lattice
|
||||
class(Lattice), intent(in) :: this
|
||||
real(8), intent(in) :: global_xyz(3)
|
||||
integer, intent(in) :: i_xyz(3)
|
||||
real(8) :: local_xyz(3)
|
||||
end function get_local_xyz_
|
||||
end function lattice_get_local_xyz_
|
||||
end interface
|
||||
|
||||
!===============================================================================
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
module global
|
||||
|
||||
use ace_header, only: Nuclide, SAlphaBeta, xsListing, NuclideMicroXS, &
|
||||
MaterialMacroXS, Nuclide0K
|
||||
use bank_header, only: Bank
|
||||
use cmfd_header
|
||||
use constants
|
||||
use dict_header, only: DictCharInt, DictIntInt
|
||||
use geometry_header, only: Cell, Universe, Lattice, LatticeContainer
|
||||
use macroxs_header, only: MacroXSContainer
|
||||
use material_header, only: Material
|
||||
use mesh_header, only: RegularMesh
|
||||
use nuclide_header
|
||||
use plot_header, only: ObjectPlot
|
||||
use sab_header, only: SAlphaBeta
|
||||
use set_header, only: SetInt
|
||||
use surface_header, only: SurfaceContainer
|
||||
use source_header, only: SourceDistribution
|
||||
|
|
@ -58,42 +59,82 @@ module global
|
|||
integer :: n_lost_particles
|
||||
|
||||
! ============================================================================
|
||||
! CROSS SECTION RELATED VARIABLES
|
||||
! ENERGY TREATMENT RELATED VARIABLES
|
||||
logical :: run_CE = .true. ! Run in CE mode?
|
||||
|
||||
! ============================================================================
|
||||
! CROSS SECTION RELATED VARIABLES NEEDED REGARDLESS OF CE OR MG
|
||||
|
||||
! Cross section arrays
|
||||
type(Nuclide), allocatable, target :: nuclides(:) ! Nuclide cross-sections
|
||||
type(SAlphaBeta), allocatable, target :: sab_tables(:) ! S(a,b) tables
|
||||
type(XsListing), allocatable, target :: xs_listings(:) ! cross_sections.xml listings
|
||||
|
||||
integer :: n_nuclides_total ! Number of nuclide cross section tables
|
||||
integer :: n_listings ! Number of listings in cross_sections.xml
|
||||
|
||||
! Cross section caches
|
||||
type(NuclideMicroXS), allocatable :: micro_xs(:) ! Cache for each nuclide
|
||||
type(MaterialMacroXS) :: material_xs ! Cache for current material
|
||||
|
||||
integer :: n_nuclides_total ! Number of nuclide cross section tables
|
||||
! Dictionaries to look up cross sections and listings
|
||||
type(DictCharInt) :: nuclide_dict
|
||||
type(DictCharInt) :: xs_listing_dict
|
||||
|
||||
! Default xs identifier (e.g. 70c or 300K)
|
||||
character(5):: default_xs
|
||||
|
||||
! ============================================================================
|
||||
! CONTINUOUS-ENERGY CROSS SECTION RELATED VARIABLES
|
||||
|
||||
! Cross section arrays
|
||||
type(NuclideCE), allocatable, target :: nuclides(:) ! Nuclide cross-sections
|
||||
type(SAlphaBeta), allocatable, target :: sab_tables(:) ! S(a,b) tables
|
||||
|
||||
integer :: n_sab_tables ! Number of S(a,b) thermal scattering tables
|
||||
integer :: n_listings ! Number of listings in cross_sections.xml
|
||||
|
||||
! Minimum/maximum energies
|
||||
real(8) :: energy_min_neutron = ZERO
|
||||
real(8) :: energy_max_neutron = INFINITY
|
||||
|
||||
! Dictionaries to look up cross sections and listings
|
||||
type(DictCharInt) :: nuclide_dict
|
||||
type(DictCharInt) :: sab_dict
|
||||
type(DictCharInt) :: xs_listing_dict
|
||||
|
||||
! Unreoslved resonance probablity tables
|
||||
logical :: urr_ptables_on = .true.
|
||||
|
||||
! Default xs identifier (e.g. 70c)
|
||||
character(3):: default_xs
|
||||
|
||||
! What to assume for expanding natural elements
|
||||
integer :: default_expand = ENDF_BVII1
|
||||
|
||||
! Whether or not windowed multipole cross sections should be used.
|
||||
logical :: multipole_active = .false.
|
||||
|
||||
! Total amount of nuclide ZAID and dictionary of nuclide ZAID and index
|
||||
integer(8) :: n_nuc_zaid_total
|
||||
type(DictIntInt) :: nuc_zaid_dict
|
||||
|
||||
! ============================================================================
|
||||
! MULTI-GROUP CROSS SECTION RELATED VARIABLES
|
||||
|
||||
! Cross section arrays
|
||||
type(NuclideMGContainer), allocatable, target :: nuclides_MG(:)
|
||||
|
||||
! Cross section caches
|
||||
type(MacroXSContainer), target, allocatable :: macro_xs(:)
|
||||
|
||||
! Number of energy groups
|
||||
integer :: energy_groups
|
||||
|
||||
! Energy group structure
|
||||
real(8), allocatable :: energy_bins(:)
|
||||
|
||||
! Midpoint of the energy group structure
|
||||
real(8), allocatable :: energy_bin_avg(:)
|
||||
|
||||
! Inverse velocities of the energy groups (provided or estimated)
|
||||
real(8), allocatable :: inverse_velocities(:)
|
||||
|
||||
! Maximum Data Order
|
||||
integer :: max_order
|
||||
|
||||
! ============================================================================
|
||||
! TALLY-RELATED VARIABLES
|
||||
|
||||
|
|
@ -394,7 +435,7 @@ module global
|
|||
type(SetInt) :: sourcepoint_batch
|
||||
|
||||
! Various output options
|
||||
logical :: output_summary = .false.
|
||||
logical :: output_summary = .true.
|
||||
logical :: output_xs = .false.
|
||||
logical :: output_tallies = .true.
|
||||
|
||||
|
|
@ -448,6 +489,14 @@ contains
|
|||
deallocate(nuclides_0K)
|
||||
end if
|
||||
|
||||
if (allocated(nuclides_MG)) then
|
||||
deallocate(nuclides_MG)
|
||||
end if
|
||||
|
||||
if (allocated(macro_xs)) then
|
||||
deallocate(macro_xs)
|
||||
end if
|
||||
|
||||
if (allocated(sab_tables)) deallocate(sab_tables)
|
||||
if (allocated(xs_listings)) deallocate(xs_listings)
|
||||
if (allocated(micro_xs)) deallocate(micro_xs)
|
||||
|
|
|
|||
|
|
@ -1,30 +1,31 @@
|
|||
module initialize
|
||||
|
||||
use ace, only: read_xs, same_nuclide_list
|
||||
use bank_header, only: Bank
|
||||
use ace, only: read_ace_xs
|
||||
use bank_header, only: Bank
|
||||
use constants
|
||||
use dict_header, only: DictIntInt, ElemKeyValueII
|
||||
use set_header, only: SetInt
|
||||
use energy_grid, only: logarithmic_grid, grid_method, unionized_grid
|
||||
use error, only: fatal_error, warning
|
||||
use geometry, only: neighbor_lists, count_instance, calc_offsets, &
|
||||
maximum_levels
|
||||
use geometry_header, only: Cell, Universe, Lattice, RectLattice, HexLattice,&
|
||||
&BASE_UNIVERSE
|
||||
use dict_header, only: DictIntInt, ElemKeyValueII
|
||||
use set_header, only: SetInt
|
||||
use energy_grid, only: logarithmic_grid, grid_method, unionized_grid
|
||||
use error, only: fatal_error, warning
|
||||
use geometry, only: neighbor_lists, count_instance, calc_offsets, &
|
||||
maximum_levels
|
||||
use geometry_header, only: Cell, Universe, Lattice, RectLattice, HexLattice,&
|
||||
&BASE_UNIVERSE
|
||||
use global
|
||||
use hdf5_interface, only: file_open, read_dataset, file_close, hdf5_bank_t,&
|
||||
hdf5_tallyresult_t, hdf5_integer8_t
|
||||
use input_xml, only: read_input_xml, read_cross_sections_xml, &
|
||||
cells_in_univ_dict, read_plots_xml
|
||||
use material_header, only: Material
|
||||
use output, only: title, header, print_version, write_message, &
|
||||
print_usage, write_xs_summary, print_plot
|
||||
use random_lcg, only: initialize_prng
|
||||
use state_point, only: load_state_point
|
||||
use string, only: to_str, str_to_int, starts_with, ends_with
|
||||
use summary, only: write_summary
|
||||
use tally_header, only: TallyObject, TallyResult, TallyFilter
|
||||
use tally_initialize, only: configure_tallies
|
||||
use hdf5_interface, only: file_open, read_dataset, file_close, hdf5_bank_t,&
|
||||
hdf5_tallyresult_t, hdf5_integer8_t
|
||||
use input_xml, only: read_input_xml, cells_in_univ_dict, read_plots_xml
|
||||
use material_header, only: Material
|
||||
use mgxs_data, only: read_mgxs, create_macro_xs
|
||||
use output, only: title, header, print_version, write_message, &
|
||||
print_usage, write_xs_summary, print_plot
|
||||
use random_lcg, only: initialize_prng
|
||||
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_initialize,only: configure_tallies
|
||||
use tally, only: init_tally_routines
|
||||
|
||||
#ifdef MPI
|
||||
use message_passing
|
||||
|
|
@ -114,30 +115,40 @@ contains
|
|||
|
||||
! Read ACE-format cross sections
|
||||
call time_read_xs%start()
|
||||
call read_xs()
|
||||
if (run_CE) then
|
||||
call read_ace_xs()
|
||||
else
|
||||
call read_mgxs()
|
||||
end if
|
||||
call time_read_xs%stop()
|
||||
|
||||
! Create linked lists for multiple instances of the same nuclide
|
||||
call same_nuclide_list()
|
||||
! Construct information needed for nuclear data
|
||||
if (run_CE) then
|
||||
! Set undefined cell temperatures to match the material data.
|
||||
call lookup_material_temperatures()
|
||||
|
||||
! Set undefined cell temperatures to match the material data.
|
||||
call lookup_material_temperatures()
|
||||
|
||||
! Construct unionized or log energy grid for cross-sections
|
||||
select case (grid_method)
|
||||
case (GRID_NUCLIDE)
|
||||
continue
|
||||
case (GRID_MAT_UNION)
|
||||
call time_unionize%start()
|
||||
call unionized_grid()
|
||||
call time_unionize%stop()
|
||||
case (GRID_LOGARITHM)
|
||||
call logarithmic_grid()
|
||||
end select
|
||||
! Construct unionized or log energy grid for cross-sections
|
||||
select case (grid_method)
|
||||
case (GRID_NUCLIDE)
|
||||
continue
|
||||
case (GRID_MAT_UNION)
|
||||
call time_unionize%start()
|
||||
call unionized_grid()
|
||||
call time_unionize%stop()
|
||||
case (GRID_LOGARITHM)
|
||||
call logarithmic_grid()
|
||||
end select
|
||||
else
|
||||
! Create material macroscopic data for MGXS
|
||||
call create_macro_xs()
|
||||
end if
|
||||
|
||||
! Allocate and setup tally stride, matching_bins, and tally maps
|
||||
call configure_tallies()
|
||||
|
||||
! Set up tally procedure pointers
|
||||
call init_tally_routines()
|
||||
|
||||
! Determine how much work each processor should do
|
||||
call calculate_work()
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,205 +0,0 @@
|
|||
module interpolation
|
||||
|
||||
use constants
|
||||
use endf_header, only: Tab1
|
||||
use search, only: binary_search
|
||||
use string, only: to_str
|
||||
|
||||
implicit none
|
||||
|
||||
interface interpolate_tab1
|
||||
module procedure interpolate_tab1_array, interpolate_tab1_object
|
||||
end interface interpolate_tab1
|
||||
|
||||
contains
|
||||
|
||||
!===============================================================================
|
||||
! INTERPOLATE_TAB1_ARRAY interpolates a function between two points based on
|
||||
! particular interpolation scheme. The data needs to be organized as a ENDF TAB1
|
||||
! type function containing the interpolation regions, break points, and
|
||||
! tabulated x's and y's.
|
||||
!===============================================================================
|
||||
|
||||
pure function interpolate_tab1_array(data, x, loc_start) result(y)
|
||||
|
||||
real(8), intent(in) :: data(:) ! array of data
|
||||
real(8), intent(in) :: x ! x value to find y at
|
||||
integer, intent(in), optional :: loc_start ! starting location in data
|
||||
real(8) :: y ! y(x)
|
||||
|
||||
integer :: i ! bin in which to interpolate
|
||||
integer :: j ! index for interpolation region
|
||||
integer :: loc_0 ! starting location
|
||||
integer :: n_regions ! number of interpolation regions
|
||||
integer :: n_points ! number of tabulated values
|
||||
integer :: interp ! ENDF interpolation scheme
|
||||
integer :: loc_breakpoints ! location of breakpoints in data
|
||||
integer :: loc_interp ! location of interpolation schemes in data
|
||||
integer :: loc_x ! location of x's in data
|
||||
integer :: loc_y ! location of y's in data
|
||||
real(8) :: r ! interpolation factor
|
||||
real(8) :: x0, x1 ! bounding x values
|
||||
real(8) :: y0, y1 ! bounding y values
|
||||
|
||||
! determine starting location
|
||||
if (present(loc_start)) then
|
||||
loc_0 = loc_start - 1
|
||||
else
|
||||
loc_0 = 0
|
||||
end if
|
||||
|
||||
! determine number of interpolation regions
|
||||
n_regions = int(data(loc_0 + 1))
|
||||
|
||||
! set locations for breakpoints and interpolation schemes
|
||||
loc_breakpoints = loc_0 + 1
|
||||
loc_interp = loc_breakpoints + n_regions
|
||||
|
||||
! determine number of tabulated values
|
||||
n_points = int(data(loc_interp + n_regions + 1))
|
||||
|
||||
! set locations for x's and y's
|
||||
loc_x = loc_interp + n_regions + 1
|
||||
loc_y = loc_x + n_points
|
||||
|
||||
! find which bin the abscissa is in -- if the abscissa is outside the
|
||||
! tabulated range, the first or last point is chosen, i.e. no interpolation
|
||||
! is done outside the energy range
|
||||
if (x < data(loc_x + 1)) then
|
||||
y = data(loc_y + 1)
|
||||
return
|
||||
elseif (x > data(loc_x + n_points)) then
|
||||
y = data(loc_y + n_points)
|
||||
return
|
||||
else
|
||||
i = binary_search(data(loc_x + 1:loc_x + n_points), n_points, x)
|
||||
end if
|
||||
|
||||
! determine interpolation scheme
|
||||
if (n_regions == 0) then
|
||||
interp = LINEAR_LINEAR
|
||||
elseif (n_regions == 1) then
|
||||
interp = int(data(loc_interp + 1))
|
||||
elseif (n_regions > 1) then
|
||||
do j = 1, n_regions
|
||||
if (i < data(loc_breakpoints + j)) then
|
||||
interp = int(data(loc_interp + j))
|
||||
exit
|
||||
end if
|
||||
end do
|
||||
end if
|
||||
|
||||
! handle special case of histogram interpolation
|
||||
if (interp == HISTOGRAM) then
|
||||
y = data(loc_y + i)
|
||||
return
|
||||
end if
|
||||
|
||||
! determine bounding values
|
||||
x0 = data(loc_x + i)
|
||||
x1 = data(loc_x + i + 1)
|
||||
y0 = data(loc_y + i)
|
||||
y1 = data(loc_y + i + 1)
|
||||
|
||||
! determine interpolation factor and interpolated value
|
||||
select case (interp)
|
||||
case (LINEAR_LINEAR)
|
||||
r = (x - x0)/(x1 - x0)
|
||||
y = y0 + r*(y1 - y0)
|
||||
case (LINEAR_LOG)
|
||||
r = log(x/x0)/log(x1/x0)
|
||||
y = y0 + r*(y1 - y0)
|
||||
case (LOG_LINEAR)
|
||||
r = (x - x0)/(x1 - x0)
|
||||
y = y0*exp(r*log(y1/y0))
|
||||
case (LOG_LOG)
|
||||
r = log(x/x0)/log(x1/x0)
|
||||
y = y0*exp(r*log(y1/y0))
|
||||
end select
|
||||
|
||||
end function interpolate_tab1_array
|
||||
|
||||
!===============================================================================
|
||||
! INTERPOLATE_TAB1_OBJECT interpolates a function between two points based on
|
||||
! particular interpolation scheme. The data needs to be organized as a ENDF TAB1
|
||||
! type function containing the interpolation regions, break points, and
|
||||
! tabulated x's and y's.
|
||||
!===============================================================================
|
||||
|
||||
pure function interpolate_tab1_object(obj, x) result(y)
|
||||
|
||||
type(Tab1), intent(in) :: obj ! ENDF Tab1 interpolable function
|
||||
real(8), intent(in) :: x ! x value to find y at
|
||||
real(8) :: y ! y(x)
|
||||
|
||||
integer :: i ! bin in which to interpolate
|
||||
integer :: j ! index for interpolation region
|
||||
integer :: n_regions ! number of interpolation regions
|
||||
integer :: n_pairs ! number of tabulated values
|
||||
integer :: interp ! ENDF interpolation scheme
|
||||
real(8) :: r ! interpolation factor
|
||||
real(8) :: x0, x1 ! bounding x values
|
||||
real(8) :: y0, y1 ! bounding y values
|
||||
|
||||
! determine number of interpolation regions and pairs
|
||||
n_regions = obj % n_regions
|
||||
n_pairs = obj % n_pairs
|
||||
|
||||
! find which bin the abscissa is in -- if the abscissa is outside the
|
||||
! tabulated range, the first or last point is chosen, i.e. no interpolation
|
||||
! is done outside the energy range
|
||||
if (x < obj % x(1)) then
|
||||
y = obj % y(1)
|
||||
return
|
||||
elseif (x > obj % x(n_pairs)) then
|
||||
y = obj % y(n_pairs)
|
||||
return
|
||||
else
|
||||
i = binary_search(obj % x, n_pairs, x)
|
||||
end if
|
||||
|
||||
! determine interpolation scheme
|
||||
if (n_regions == 0) then
|
||||
interp = LINEAR_LINEAR
|
||||
elseif (n_regions == 1) then
|
||||
interp = obj % int(1)
|
||||
elseif (n_regions > 1) then
|
||||
do j = 1, n_regions
|
||||
if (i < obj % nbt(j)) then
|
||||
interp = obj % int(j)
|
||||
exit
|
||||
end if
|
||||
end do
|
||||
end if
|
||||
|
||||
! handle special case of histogram interpolation
|
||||
if (interp == HISTOGRAM) then
|
||||
y = obj % y(i)
|
||||
return
|
||||
end if
|
||||
|
||||
! determine bounding values
|
||||
x0 = obj % x(i)
|
||||
x1 = obj % x(i + 1)
|
||||
y0 = obj % y(i)
|
||||
y1 = obj % y(i + 1)
|
||||
|
||||
! determine interpolation factor and interpolated value
|
||||
select case (interp)
|
||||
case (LINEAR_LINEAR)
|
||||
r = (x - x0)/(x1 - x0)
|
||||
y = y0 + r*(y1 - y0)
|
||||
case (LINEAR_LOG)
|
||||
r = log(x/x0)/log(x1/x0)
|
||||
y = y0 + r*(y1 - y0)
|
||||
case (LOG_LINEAR)
|
||||
r = (x - x0)/(x1 - x0)
|
||||
y = y0*exp(r*log(y1/y0))
|
||||
case (LOG_LOG)
|
||||
r = log(x/x0)/log(x1/x0)
|
||||
y = y0*exp(r*log(y1/y0))
|
||||
end select
|
||||
|
||||
end function interpolate_tab1_object
|
||||
|
||||
end module interpolation
|
||||
860
src/macroxs_header.F90
Normal file
860
src/macroxs_header.F90
Normal file
|
|
@ -0,0 +1,860 @@
|
|||
module macroxs_header
|
||||
|
||||
use constants, only: MAX_FILE_LEN, ZERO, ONE, TWO, PI
|
||||
use list_header, only: ListInt
|
||||
use material_header, only: material
|
||||
use math, only: calc_pn, calc_rn, expand_harmonic, find_angle
|
||||
use nuclide_header
|
||||
use random_lcg, only: prn
|
||||
use scattdata_header
|
||||
|
||||
implicit none
|
||||
|
||||
!===============================================================================
|
||||
! MACROXS_* contains cached macroscopic cross sections for the material a
|
||||
! particle is traveling through
|
||||
!===============================================================================
|
||||
|
||||
type, abstract :: MacroXS
|
||||
! Data Order
|
||||
integer :: order
|
||||
|
||||
contains
|
||||
procedure(macroxs_init_), deferred :: init ! initializes object
|
||||
procedure(macroxs_get_xs_), deferred :: get_xs ! Return xs
|
||||
! Sample the outgoing energy from a fission event
|
||||
procedure(macroxs_sample_fission_), deferred :: sample_fission_energy
|
||||
! Sample the outgoing energy and angle from a scatter event
|
||||
procedure(macroxs_sample_scatter_), deferred :: sample_scatter
|
||||
! Calculate the material specific MGXS data from the nuclides
|
||||
procedure(macroxs_calculate_xs_), deferred :: calculate_xs
|
||||
end type MacroXS
|
||||
|
||||
abstract interface
|
||||
subroutine macroxs_init_(this, mat, nuclides, groups, get_kfiss, get_fiss, &
|
||||
max_order, scatt_type, legendre_mu_points, &
|
||||
error_code, error_text)
|
||||
import MacroXS, Material, NuclideMGContainer, MAX_LINE_LEN
|
||||
class(MacroXS), intent(inout) :: this ! The MacroXS to initialize
|
||||
type(Material), pointer, intent(in) :: mat ! base material
|
||||
type(NuclideMGContainer), intent(in) :: nuclides(:) ! List of nuclides to harvest from
|
||||
integer, intent(in) :: groups ! Number of E groups
|
||||
logical, intent(in) :: get_kfiss ! Should we get kfiss data?
|
||||
logical, intent(in) :: get_fiss ! Should we get fiss data?
|
||||
integer, intent(in) :: max_order ! Maximum requested order
|
||||
integer, intent(in) :: scatt_type ! Legendre or Tabular Scatt?
|
||||
integer, intent(in) :: legendre_mu_points ! Treat as Leg or Tabular?
|
||||
integer, intent(inout) :: error_code ! Code signifying error
|
||||
character(MAX_LINE_LEN), intent(inout) :: error_text ! Error message to print
|
||||
end subroutine macroxs_init_
|
||||
|
||||
function macroxs_get_xs_(this, g, xstype, gout, uvw) result(xs)
|
||||
import MacroXS
|
||||
class(MacroXS), intent(in) :: this ! The MacroXS to initialize
|
||||
integer, intent(in) :: g ! Incoming Energy group
|
||||
character(*) , intent(in) :: xstype ! Cross Section Type
|
||||
integer, optional, intent(in) :: gout ! Outgoing Energy group
|
||||
real(8), optional, intent(in) :: uvw(3) ! Requested Angle
|
||||
real(8) :: xs ! Resultant xs
|
||||
end function macroxs_get_xs_
|
||||
|
||||
function macroxs_sample_fission_(this, gin, uvw) result(gout)
|
||||
import MacroXS
|
||||
class(MacroXS), intent(in) :: this ! Data to work with
|
||||
integer, intent(in) :: gin ! Incoming energy group
|
||||
real(8), intent(in) :: uvw(3) ! Particle Direction
|
||||
integer :: gout ! Sampled outgoing group
|
||||
|
||||
end function macroxs_sample_fission_
|
||||
|
||||
subroutine macroxs_sample_scatter_(this, uvw, gin, gout, mu, wgt)
|
||||
import MacroXS
|
||||
class(MacroXS), intent(in) :: this
|
||||
real(8), intent(in) :: uvw(3) ! Incoming neutron direction
|
||||
integer, intent(in) :: gin ! Incoming neutron group
|
||||
integer, intent(out) :: gout ! Sampled outgoin group
|
||||
real(8), intent(out) :: mu ! Sampled change in angle
|
||||
real(8), intent(inout) :: wgt ! Particle weight
|
||||
end subroutine macroxs_sample_scatter_
|
||||
|
||||
subroutine macroxs_calculate_xs_(this, gin, uvw, xs)
|
||||
import MacroXS, MaterialMacroXS
|
||||
class(MacroXS), intent(in) :: this
|
||||
integer, intent(in) :: gin ! Incoming neutron group
|
||||
real(8), intent(in) :: uvw(3) ! Incoming neutron direction
|
||||
type(MaterialMacroXS), intent(inout) :: xs
|
||||
end subroutine macroxs_calculate_xs_
|
||||
end interface
|
||||
|
||||
type, extends(MacroXS) :: MacroXSIso
|
||||
! Microscopic cross sections
|
||||
real(8), allocatable :: total(:) ! total cross section
|
||||
real(8), allocatable :: absorption(:) ! absorption cross section
|
||||
class(ScattData), allocatable :: scatter ! scattering information
|
||||
real(8), allocatable :: nu_fission(:) ! nu-fission
|
||||
real(8), allocatable :: k_fission(:) ! kappa-fission
|
||||
real(8), allocatable :: fission(:) ! fission x/s
|
||||
real(8), allocatable :: scattxs(:) ! scattering xs
|
||||
real(8), allocatable :: chi(:,:) ! fission spectra
|
||||
|
||||
contains
|
||||
procedure :: init => macroxsiso_init ! inits object
|
||||
procedure :: get_xs => macroxsiso_get_xs ! Returns xs
|
||||
procedure :: sample_fission_energy => macroxsiso_sample_fission_energy
|
||||
procedure :: sample_scatter => macroxsiso_sample_scatter
|
||||
procedure :: calculate_xs => macroxsiso_calculate_xs
|
||||
end type MacroXSIso
|
||||
|
||||
type, extends(MacroXS) :: MacroXSAngle
|
||||
! Macroscopic cross sections
|
||||
real(8), allocatable :: total(:,:,:) ! total cross section
|
||||
real(8), allocatable :: absorption(:,:,:) ! absorption cross section
|
||||
type(ScattDataContainer), allocatable :: scatter(:,:) ! scattering information
|
||||
real(8), allocatable :: nu_fission(:,:,:) ! nu-fission
|
||||
real(8), allocatable :: k_fission(:,:,:) ! kappa-fission
|
||||
real(8), allocatable :: fission(:,:,:) ! fission x/s
|
||||
real(8), allocatable :: chi(:,:,:,:) ! fission spectra
|
||||
real(8), allocatable :: scattxs(:,:,:) ! scattering xs
|
||||
real(8), allocatable :: polar(:) ! polar angles
|
||||
real(8), allocatable :: azimuthal(:) ! azimuthal angles
|
||||
|
||||
contains
|
||||
procedure :: init => macroxsangle_init ! inits object
|
||||
procedure :: get_xs => macroxsangle_get_xs ! Returns xs
|
||||
procedure :: sample_fission_energy => macroxsangle_sample_fission_energy
|
||||
procedure :: sample_scatter => macroxsangle_sample_scatter
|
||||
procedure :: calculate_xs => macroxsangle_calculate_xs
|
||||
end type MacroXSAngle
|
||||
|
||||
!===============================================================================
|
||||
! MACROXSCONTAINER pointer array for storing MacroXS objects.
|
||||
!===============================================================================
|
||||
|
||||
type MacroXSContainer
|
||||
class(MacroXS), allocatable :: obj
|
||||
end type MacroXSContainer
|
||||
|
||||
contains
|
||||
|
||||
!===============================================================================
|
||||
! MACROXS*_INIT sets the MacroXS Data
|
||||
!===============================================================================
|
||||
|
||||
subroutine macroxsiso_init(this, mat, nuclides, groups, get_kfiss, get_fiss, &
|
||||
max_order, scatt_type, legendre_mu_points, error_code, error_text)
|
||||
class(MacroXSIso), intent(inout) :: this ! The MacroXS to initialize
|
||||
type(Material), pointer, intent(in) :: mat ! base material
|
||||
type(NuclideMGContainer), intent(in) :: nuclides(:) ! List of nuclides to harvest from
|
||||
integer, intent(in) :: groups ! Number of E groups
|
||||
logical, intent(in) :: get_kfiss ! Should we get kfiss data?
|
||||
logical, intent(in) :: get_fiss ! Should we get fiss data?
|
||||
integer, intent(in) :: max_order ! Maximum requested order
|
||||
integer, intent(in) :: scatt_type ! How is data presented
|
||||
integer, intent(in) :: legendre_mu_points ! Treat as Leg or Tabular?
|
||||
integer, intent(inout) :: error_code ! Code signifying error
|
||||
character(MAX_LINE_LEN), intent(inout) :: error_text ! Error message to print
|
||||
|
||||
integer :: i ! loop index over nuclides
|
||||
integer :: gin, gout ! group indices
|
||||
real(8) :: atom_density ! atom density of a nuclide
|
||||
integer :: imu
|
||||
real(8) :: norm
|
||||
integer :: mat_max_order, order, l
|
||||
real(8), allocatable :: temp_mult(:,:)
|
||||
real(8), allocatable :: temp_energy(:,:)
|
||||
real(8), allocatable :: scatt_coeffs(:,:,:)
|
||||
|
||||
! Initialize error data
|
||||
error_code = 0
|
||||
error_text = ''
|
||||
|
||||
! If we have tabular only data, then make sure all datasets have same size
|
||||
if (scatt_type == ANGLE_HISTOGRAM) then
|
||||
! Check all scattering data of same size
|
||||
order = nuclides(mat % nuclide(1)) % obj % order
|
||||
do i = 2, mat % n_nuclides
|
||||
if (order /= nuclides(mat % nuclide(i)) % obj % order) then
|
||||
error_code = 1
|
||||
error_text = "All Histogram Scattering Entries Must Be Same Length!"
|
||||
return
|
||||
end if
|
||||
end do
|
||||
! Ok, got our order, store it
|
||||
this % order = order
|
||||
|
||||
! Allocate stuff for later
|
||||
allocate(scatt_coeffs(order, groups, groups))
|
||||
scatt_coeffs = ZERO
|
||||
allocate(ScattDataHistogram :: this % scatter)
|
||||
|
||||
else if (scatt_type == ANGLE_TABULAR) then
|
||||
! Check all scattering data of same size
|
||||
order = nuclides(mat % nuclide(1)) % obj % order
|
||||
do i = 2, mat % n_nuclides
|
||||
if (order /= nuclides(mat % nuclide(i)) % obj % order) then
|
||||
error_code = 1
|
||||
error_text = "All Tabular Scattering Entries Must Be Same Length!"
|
||||
return
|
||||
end if
|
||||
end do
|
||||
! Ok, got our order, store it
|
||||
this % order = order
|
||||
|
||||
! Allocate stuff for later
|
||||
allocate(scatt_coeffs(order, groups, groups))
|
||||
scatt_coeffs = ZERO
|
||||
allocate(ScattDataTabular :: this % scatter)
|
||||
|
||||
else if (scatt_type == ANGLE_LEGENDRE) then
|
||||
! Otherwise find the maximum scattering order
|
||||
! Need to determine the maximum scattering order of all data in this material
|
||||
mat_max_order = 0
|
||||
do i = 1, mat % n_nuclides
|
||||
if (nuclides(mat % nuclide(i)) % obj % order > mat_max_order) then
|
||||
mat_max_order = nuclides(mat % nuclide(i)) % obj % order
|
||||
end if
|
||||
end do
|
||||
|
||||
! Now need to compare this material maximum scattering order with
|
||||
! the problem wide max scatt order and use whichever is lower
|
||||
order = min(mat_max_order, max_order)
|
||||
this % order = order + 1
|
||||
|
||||
! Now we can allocate our scatt_coeffs object accordingly
|
||||
allocate(scatt_coeffs(order + 1, groups, groups))
|
||||
scatt_coeffs = ZERO
|
||||
if (legendre_mu_points == 1) then
|
||||
allocate(ScattDataLegendre :: this % scatter)
|
||||
else
|
||||
allocate(ScattDataTabular :: this % scatter)
|
||||
end if
|
||||
end if
|
||||
|
||||
! Allocate and initialize data within macro_xs(i_mat) object
|
||||
allocate(this % total(groups))
|
||||
this % total = ZERO
|
||||
allocate(this % absorption(groups))
|
||||
this % absorption = ZERO
|
||||
if (get_fiss) then
|
||||
allocate(this % fission(groups))
|
||||
this % fission = ZERO
|
||||
end if
|
||||
if (get_kfiss) then
|
||||
allocate(this % k_fission(groups))
|
||||
this % k_fission = ZERO
|
||||
end if
|
||||
allocate(this % nu_fission(groups))
|
||||
this % nu_fission = ZERO
|
||||
allocate(this % chi(groups, groups))
|
||||
this % chi = ZERO
|
||||
allocate(temp_energy(groups, groups))
|
||||
temp_energy = ZERO
|
||||
allocate(temp_mult(groups, groups))
|
||||
temp_mult = ZERO
|
||||
allocate(this % scattxs(groups))
|
||||
|
||||
! Add contribution from each nuclide in material
|
||||
do i = 1, mat % n_nuclides
|
||||
! Copy atom density of nuclide in material
|
||||
atom_density = mat % atom_density(i)
|
||||
|
||||
! Perform our operations which depend upon the type
|
||||
select type(nuc => nuclides(mat % nuclide(i)) % obj)
|
||||
type is (NuclideIso)
|
||||
|
||||
! Add contributions to total, absorption, and fission data (if necessary)
|
||||
this % total = this % total + atom_density * nuc % total
|
||||
this % absorption = this % absorption + &
|
||||
atom_density * nuc % absorption
|
||||
if (nuc % fissionable) then
|
||||
if (allocated(nuc % chi)) then
|
||||
do gin = 1, groups
|
||||
do gout = 1, groups
|
||||
this % chi(gout,gin) = this % chi(gout,gin) + atom_density * &
|
||||
nuc % chi(gout) * nuc % nu_fission(gin,1)
|
||||
end do
|
||||
end do
|
||||
this % nu_fission = this % nu_fission + atom_density * &
|
||||
nuc % nu_fission(:,1)
|
||||
else
|
||||
this % chi = this % chi + atom_density * nuc % nu_fission
|
||||
do gin = 1, groups
|
||||
this % nu_fission(gin) = this % nu_fission(gin) + atom_density * &
|
||||
sum(nuc % nu_fission(:,gin))
|
||||
end do
|
||||
end if
|
||||
if (get_fiss) then
|
||||
this % fission = this % fission + atom_density * nuc % fission
|
||||
end if
|
||||
if (get_kfiss) then
|
||||
this % k_fission = this % k_fission + atom_density * nuc % k_fission
|
||||
end if
|
||||
end if
|
||||
|
||||
! Now time to do the scattering
|
||||
do gin = 1, groups
|
||||
do gout = 1, groups
|
||||
if (scatt_type == ANGLE_HISTOGRAM .or. scatt_type == ANGLE_TABULAR) then
|
||||
! Transfer matrix
|
||||
temp_energy(gout,gin) = temp_energy(gout,gin) + atom_density * &
|
||||
sum(nuc % scatter(gout,gin,:))
|
||||
|
||||
! Determine the angular distribution
|
||||
do imu = 1, order
|
||||
scatt_coeffs(imu, gout, gin) = scatt_coeffs(imu, gout, gin) + &
|
||||
nuc % scatter(gout,gin,imu) * &
|
||||
atom_density
|
||||
end do
|
||||
|
||||
else if (scatt_type == ANGLE_LEGENDRE) then
|
||||
! Transfer matrix
|
||||
temp_energy(gout,gin) = temp_energy(gout,gin) + atom_density * &
|
||||
nuc % scatter(gout,gin,1)
|
||||
|
||||
! Determine the angular distribution coefficients so we can later
|
||||
! expand do the complete distribution
|
||||
do l = 1, min(nuc % order, order) + 1
|
||||
scatt_coeffs(l, gout, gin) = scatt_coeffs(l, gout, gin) + &
|
||||
nuc % scatter(gout,gin,l) * &
|
||||
atom_density
|
||||
end do
|
||||
|
||||
end if
|
||||
|
||||
! Multiplicity matrix
|
||||
temp_mult(gout,gin) = temp_mult(gout,gin) + atom_density * &
|
||||
nuc % mult(gout,gin)
|
||||
end do
|
||||
end do
|
||||
type is (NuclideAngle)
|
||||
error_code = 1
|
||||
error_text = "Invalid Passing of NuclideAngle to MacroXSIso Object"
|
||||
return
|
||||
end select
|
||||
end do
|
||||
|
||||
! Store the scattering xs
|
||||
if (scatt_type == ANGLE_HISTOGRAM .or. scatt_type == ANGLE_TABULAR) then
|
||||
this % scattxs(:) = sum(sum(scatt_coeffs(:,:,:),dim=1),dim=1)
|
||||
else if (scatt_type == ANGLE_LEGENDRE) then
|
||||
this % scattxs(:) = sum(scatt_coeffs(1,:,:),dim=1)
|
||||
end if
|
||||
|
||||
! Normalize the scatt_coeffs
|
||||
do gin = 1, groups
|
||||
do gout = 1, groups
|
||||
if (scatt_type == ANGLE_HISTOGRAM .or. scatt_type == ANGLE_TABULAR) then
|
||||
norm = sum(scatt_coeffs(:,gout,gin))
|
||||
else if (scatt_type == ANGLE_LEGENDRE) then
|
||||
norm = scatt_coeffs(1,gout,gin)
|
||||
end if
|
||||
if (norm /= ZERO) then
|
||||
scatt_coeffs(:, gout, gin) = scatt_coeffs(:, gout,gin) / norm
|
||||
end if
|
||||
end do
|
||||
! Now normalize temp_energy (outgoing scattering energy probabilities)
|
||||
norm = sum(temp_energy(:,gin))
|
||||
if (norm > ZERO) then
|
||||
temp_energy(:,gin) = temp_energy(:,gin) / norm
|
||||
end if
|
||||
end do
|
||||
|
||||
if (scatt_type == ANGLE_LEGENDRE .and. legendre_mu_points /= 1) then
|
||||
call this % scatter % init(legendre_mu_points, temp_energy, temp_mult, &
|
||||
scatt_coeffs)
|
||||
else
|
||||
call this % scatter % init(this % order, temp_energy, temp_mult, &
|
||||
scatt_coeffs)
|
||||
end if
|
||||
|
||||
! Now normalize chi
|
||||
if (mat % fissionable) then
|
||||
do gin = 1, groups
|
||||
! Normalize Chi
|
||||
norm = sum(this % chi(:,gin))
|
||||
if (norm > ZERO) then
|
||||
this % chi(:,gin) = this % chi(:,gin) / norm
|
||||
end if
|
||||
end do
|
||||
end if
|
||||
|
||||
! Deallocate temporaries for the next material
|
||||
deallocate(scatt_coeffs, temp_energy, temp_mult)
|
||||
|
||||
end subroutine macroxsiso_init
|
||||
|
||||
subroutine macroxsangle_init(this, mat, nuclides, groups, get_kfiss, get_fiss, &
|
||||
max_order, scatt_type, legendre_mu_points, error_code, error_text)
|
||||
class(MacroXSAngle), intent(inout) :: this ! The MacroXS to initialize
|
||||
type(Material), pointer, intent(in) :: mat ! base material
|
||||
type(NuclideMGContainer), intent(in) :: nuclides(:) ! List of nuclides to harvest from
|
||||
integer, intent(in) :: groups ! Number of E groups
|
||||
logical, intent(in) :: get_kfiss ! Should we get kfiss data?
|
||||
logical, intent(in) :: get_fiss ! Should we get fiss data?
|
||||
integer, intent(in) :: max_order ! Maximum requested order
|
||||
integer, intent(in) :: scatt_type ! Legendre or Tabular Scatt?
|
||||
integer, intent(in) :: legendre_mu_points ! Treat as Leg or Tabular?
|
||||
integer, intent(inout) :: error_code ! Code signifying error
|
||||
character(MAX_LINE_LEN), intent(inout) :: error_text ! Error message to print
|
||||
|
||||
integer :: i ! loop index over nuclides
|
||||
integer :: gin, gout ! group indices
|
||||
real(8) :: atom_density ! atom density of a nuclide
|
||||
integer :: ipol, iazi, npol, nazi
|
||||
integer :: imu
|
||||
real(8) :: norm
|
||||
integer :: mat_max_order, order, l
|
||||
real(8), allocatable :: temp_mult(:,:,:,:)
|
||||
real(8), allocatable :: temp_energy(:,:,:,:)
|
||||
real(8), allocatable :: scatt_coeffs(:,:,:,:,:)
|
||||
|
||||
! Initialize error data
|
||||
error_code = 0
|
||||
error_text = ''
|
||||
|
||||
! Get the number of each polar and azi angles and make sure all the
|
||||
! NuclideAngle types have the same number of these angles
|
||||
npol = -1
|
||||
nazi = -1
|
||||
do i = 1, mat % n_nuclides
|
||||
select type(nuc => nuclides(mat % nuclide(i)) % obj)
|
||||
type is (NuclideAngle)
|
||||
if (npol == -1) then
|
||||
npol = nuc % n_pol
|
||||
nazi = nuc % n_azi
|
||||
allocate(this % polar(npol))
|
||||
this % polar = nuc % polar
|
||||
allocate(this % azimuthal(nazi))
|
||||
this % azimuthal = nuc % azimuthal
|
||||
else
|
||||
if ((npol /= nuc % n_pol) .or. (nazi /= nuc % n_azi)) then
|
||||
error_code = 1
|
||||
error_text = "All Angular Data Must Be Same Length!"
|
||||
end if
|
||||
end if
|
||||
end select
|
||||
end do
|
||||
|
||||
! If we have tabular only data, then make sure all datasets have same size
|
||||
if (scatt_type == ANGLE_HISTOGRAM) then
|
||||
! Check all scattering data of same size
|
||||
order = nuclides(mat % nuclide(1)) % obj % order
|
||||
do i = 2, mat % n_nuclides
|
||||
if (order /= nuclides(mat % nuclide(i)) % obj % order) then
|
||||
error_code = 1
|
||||
error_text = "All Histogram Scattering Entries Must Be Same Length!"
|
||||
return
|
||||
end if
|
||||
end do
|
||||
! Ok, got our order, store it
|
||||
this % order = order
|
||||
|
||||
! Allocate stuff for later
|
||||
allocate(scatt_coeffs(order, groups, groups, nazi, npol))
|
||||
scatt_coeffs = ZERO
|
||||
allocate(this % scatter(nazi, npol))
|
||||
do ipol = 1, npol
|
||||
do iazi = 1, nazi
|
||||
allocate(ScattDataHistogram :: this % scatter(iazi, ipol) % obj)
|
||||
end do
|
||||
end do
|
||||
|
||||
else if (scatt_type == ANGLE_TABULAR) then
|
||||
! Check all scattering data of same size
|
||||
order = nuclides(mat % nuclide(1)) % obj % order
|
||||
do i = 2, mat % n_nuclides
|
||||
if (order /= nuclides(mat % nuclide(i)) % obj % order) then
|
||||
error_code = 1
|
||||
error_text = "All Tabular Scattering Entries Must Be Same Length!"
|
||||
return
|
||||
end if
|
||||
end do
|
||||
! Ok, got our order, store it
|
||||
this % order = order
|
||||
|
||||
! Allocate stuff for later
|
||||
allocate(scatt_coeffs(order, groups, groups, nazi, npol))
|
||||
scatt_coeffs = ZERO
|
||||
allocate(this % scatter(nazi, npol))
|
||||
do ipol = 1, npol
|
||||
do iazi = 1, nazi
|
||||
allocate(ScattDataTabular :: this % scatter(iazi, ipol) % obj)
|
||||
end do
|
||||
end do
|
||||
|
||||
else if (scatt_type == ANGLE_LEGENDRE) then
|
||||
! Otherwise find the maximum scattering order
|
||||
! Need to determine the maximum scattering order of all data in this material
|
||||
mat_max_order = 0
|
||||
do i = 1, mat % n_nuclides
|
||||
if (nuclides(mat % nuclide(i)) % obj % order > mat_max_order) then
|
||||
mat_max_order = nuclides(mat % nuclide(i)) % obj % order
|
||||
end if
|
||||
end do
|
||||
|
||||
! Now need to compare this material maximum scattering order with
|
||||
! the problem wide max scatt order and use whichever is lower
|
||||
order = min(mat_max_order, max_order)
|
||||
this % order = order + 1
|
||||
|
||||
! Now we can allocate our scatt_coeffs object accordingly
|
||||
allocate(scatt_coeffs(order + 1, groups, groups, nazi, npol))
|
||||
scatt_coeffs = ZERO
|
||||
allocate(this % scatter(nazi, npol))
|
||||
do ipol = 1, npol
|
||||
do iazi = 1, nazi
|
||||
if (legendre_mu_points == 1) then
|
||||
allocate(ScattDataLegendre :: this % scatter(iazi, ipol) % obj)
|
||||
else
|
||||
allocate(ScattDataTabular :: this % scatter(iazi, ipol) % obj)
|
||||
end if
|
||||
end do
|
||||
end do
|
||||
end if
|
||||
|
||||
! Allocate and initialize data within macro_xs(i_mat) object
|
||||
allocate(this % total(groups,nazi,npol))
|
||||
this % total = ZERO
|
||||
allocate(this % absorption(groups,nazi,npol))
|
||||
this % absorption = ZERO
|
||||
if (get_fiss) then
|
||||
allocate(this % fission(groups,nazi,npol))
|
||||
this % fission = ZERO
|
||||
end if
|
||||
if (get_kfiss) then
|
||||
allocate(this % k_fission(groups,nazi,npol))
|
||||
this % k_fission = ZERO
|
||||
end if
|
||||
allocate(this % nu_fission(groups,nazi,npol))
|
||||
this % nu_fission = ZERO
|
||||
allocate(this % chi(groups, groups, nazi, npol))
|
||||
this % chi = ZERO
|
||||
allocate(temp_energy(groups,groups,nazi,npol))
|
||||
temp_energy = ZERO
|
||||
allocate(temp_mult(groups,groups,nazi,npol))
|
||||
temp_mult = ZERO
|
||||
allocate(this % scattxs(groups,nazi,npol))
|
||||
|
||||
! Add contribution from each nuclide in material
|
||||
do i = 1, mat % n_nuclides
|
||||
! Copy atom density of nuclide in material
|
||||
atom_density = mat % atom_density(i)
|
||||
|
||||
! Perform our operations which depend upon the type
|
||||
select type(nuc => nuclides(mat % nuclide(i)) % obj)
|
||||
type is (NuclideIso)
|
||||
error_code = 1
|
||||
error_text = "Invalid Passing of NuclideIso to MacroXSAngle Object"
|
||||
return
|
||||
type is (NuclideAngle)
|
||||
! Add contributions to total, absorption, and fission data (if necessary)
|
||||
this % total = this % total + atom_density * nuc % total
|
||||
this % absorption = this % absorption + &
|
||||
atom_density * nuc % absorption
|
||||
if (nuc % fissionable) then
|
||||
if (allocated(nuc % chi)) then
|
||||
do gin = 1, groups
|
||||
do gout = 1, groups
|
||||
this % chi(gout,gin,:,:) = this % chi(gout,gin,:,:) + atom_density * &
|
||||
nuc % chi(gout,:,:) * nuc % nu_fission(gin,1,:,:)
|
||||
end do
|
||||
end do
|
||||
this % nu_fission = this % nu_fission + atom_density * &
|
||||
nuc % nu_fission(:,1,:,:)
|
||||
else
|
||||
this % chi = this % chi + atom_density * nuc % nu_fission
|
||||
do gin = 1, groups
|
||||
this % nu_fission(gin,:,:) = this % nu_fission(gin,:,:) + atom_density * &
|
||||
sum(nuc % nu_fission(:,gin,:,:),dim=1)
|
||||
end do
|
||||
end if
|
||||
if (get_fiss) then
|
||||
this % fission = this % fission + atom_density * nuc % fission
|
||||
end if
|
||||
if (get_kfiss) then
|
||||
this % k_fission = this % k_fission + atom_density * nuc % k_fission
|
||||
end if
|
||||
end if
|
||||
|
||||
! Now time to do the scattering
|
||||
do gin = 1, groups
|
||||
do gout = 1, groups
|
||||
if (scatt_type == ANGLE_HISTOGRAM .or. scatt_type == ANGLE_TABULAR) then
|
||||
! Transfer matrix
|
||||
temp_energy(gout,gin,:,:) = temp_energy(gout,gin,:,:) + atom_density * &
|
||||
sum(nuc % scatter(gout,gin,:,:,:),dim=1)
|
||||
|
||||
! Determine the angular distribution
|
||||
do imu = 1, order
|
||||
scatt_coeffs(imu,gout,gin,:,:) = scatt_coeffs(imu,gout,gin,:,:) + &
|
||||
nuc % scatter(gout,gin,imu,:,:) * &
|
||||
atom_density
|
||||
end do
|
||||
else if (scatt_type == ANGLE_LEGENDRE) then
|
||||
! Transfer matrix
|
||||
temp_energy(gout,gin,:,:) = temp_energy(gout,gin,:,:) + atom_density * &
|
||||
nuc % scatter(gout,gin,1,:,:)
|
||||
|
||||
! Determine the angular distribution coefficients so we can later
|
||||
! expand do the complete distribution
|
||||
do l = 1, min(nuc % order, order) + 1
|
||||
scatt_coeffs(l, gout, gin,:,:) = scatt_coeffs(l, gout, gin,:,:) + &
|
||||
nuc % scatter(gout,gin,l,:,:) * &
|
||||
atom_density
|
||||
end do
|
||||
end if
|
||||
|
||||
! Multiplicity matrix
|
||||
temp_mult(gout,gin,:,:) = temp_mult(gout,gin,:,:) + atom_density * &
|
||||
nuc % mult(gout,gin,:,:)
|
||||
end do
|
||||
end do
|
||||
end select
|
||||
end do
|
||||
|
||||
! Store the scattering xs
|
||||
if (scatt_type == ANGLE_HISTOGRAM .or. scatt_type == ANGLE_TABULAR) then
|
||||
this % scattxs(:,:,:) = sum(sum(scatt_coeffs(:,:,:,:,:),dim=1),dim=1)
|
||||
else if (scatt_type == ANGLE_LEGENDRE) then
|
||||
this % scattxs(:,:,:) = sum(scatt_coeffs(1,:,:,:,:),dim=1)
|
||||
end if
|
||||
|
||||
! Normalize the scatt_coeffs
|
||||
do ipol = 1, npol
|
||||
do iazi = 1, nazi
|
||||
do gin = 1, groups
|
||||
do gout = 1, groups
|
||||
if (scatt_type == ANGLE_HISTOGRAM .or. scatt_type == ANGLE_TABULAR) then
|
||||
norm = sum(scatt_coeffs(:,gout,gin,iazi,ipol))
|
||||
else if (scatt_type == ANGLE_LEGENDRE) then
|
||||
norm = scatt_coeffs(1,gout,gin,iazi,ipol)
|
||||
end if
|
||||
if (norm /= ZERO) then
|
||||
scatt_coeffs(:,gout,gin,iazi,ipol) = &
|
||||
scatt_coeffs(:,gout,gin,iazi,ipol) / norm
|
||||
end if
|
||||
end do
|
||||
! Now normalize temp_energy (outgoing scattering energy probabilities)
|
||||
norm = sum(temp_energy(:,gin,iazi,ipol))
|
||||
if (norm > ZERO) then
|
||||
temp_energy(:,gin,iazi,ipol) = temp_energy(:,gin,iazi,ipol) / norm
|
||||
end if
|
||||
end do
|
||||
|
||||
if (scatt_type == ANGLE_LEGENDRE .and. legendre_mu_points /= 1) then
|
||||
call this % scatter(iazi, ipol) % obj % init(legendre_mu_points, &
|
||||
temp_energy(:,:,iazi,ipol), temp_mult(:,:,iazi,ipol), &
|
||||
scatt_coeffs(:,:,:,iazi,ipol))
|
||||
else
|
||||
call this % scatter(iazi, ipol) % obj % init(this % order, &
|
||||
temp_energy(:,:,iazi,ipol), temp_mult(:,:,iazi,ipol), &
|
||||
scatt_coeffs(:,:,:,iazi,ipol))
|
||||
end if
|
||||
|
||||
end do
|
||||
end do
|
||||
|
||||
! Now go through and normalize chi
|
||||
if (mat % fissionable) then
|
||||
do ipol = 1, npol
|
||||
do iazi = 1, nazi
|
||||
do gin = 1, groups
|
||||
! Normalize Chi
|
||||
norm = sum(this % chi(:,gin,iazi,ipol))
|
||||
if (norm > ZERO) then
|
||||
this % chi(:,gin,iazi,ipol) = this % chi(:,gin,iazi,ipol) / norm
|
||||
end if
|
||||
end do
|
||||
end do
|
||||
end do
|
||||
end if
|
||||
|
||||
! Deallocate temporaries for the next material
|
||||
deallocate(scatt_coeffs, temp_energy, temp_mult)
|
||||
|
||||
end subroutine macroxsangle_init
|
||||
|
||||
!===============================================================================
|
||||
! MACROXS_*_GET_XS returns the requested data type
|
||||
!===============================================================================
|
||||
|
||||
function macroxsiso_get_xs(this, g, xstype, gout, uvw) result(xs)
|
||||
class(MacroXSIso), intent(in) :: this ! The MacroXS to initialize
|
||||
integer, intent(in) :: g ! Incoming Energy group
|
||||
character(*) , intent(in) :: xstype ! Type of xs requested
|
||||
integer, optional, intent(in) :: gout ! Outgoing Energy group
|
||||
real(8), optional, intent(in) :: uvw(3) ! Requested Angle
|
||||
real(8) :: xs ! Requested x/s
|
||||
|
||||
select case(xstype)
|
||||
case('total')
|
||||
xs = this % total(g)
|
||||
case('absorption')
|
||||
xs = this % absorption(g)
|
||||
case('fission')
|
||||
xs = this % fission(g)
|
||||
case('k_fission')
|
||||
xs = this % k_fission(g)
|
||||
case('nu_fission')
|
||||
xs = this % nu_fission(g)
|
||||
case('scatter')
|
||||
xs = this % scattxs(g)
|
||||
case('mult')
|
||||
if (present(gout)) then
|
||||
xs = this % scatter % mult(gout,g)
|
||||
else
|
||||
xs = sum(this % scatter % mult(:,g))
|
||||
end if
|
||||
end select
|
||||
|
||||
end function macroxsiso_get_xs
|
||||
|
||||
function macroxsangle_get_xs(this, g, xstype, gout,uvw) result(xs)
|
||||
class(MacroXSAngle), intent(in) :: this ! The MacroXS to initialize
|
||||
integer, intent(in) :: g ! Incoming Energy group
|
||||
character(*) , intent(in) :: xstype ! Type of xs requested
|
||||
integer, optional, intent(in) :: gout ! Outgoing Energy group
|
||||
real(8), optional, intent(in) :: uvw(3) ! Requested Angle
|
||||
real(8) :: xs ! Requested x/s
|
||||
|
||||
integer :: iazi, ipol
|
||||
|
||||
if (present(uvw)) then
|
||||
call find_angle(this % polar, this % azimuthal, uvw, iazi, ipol)
|
||||
select case(xstype)
|
||||
case('total')
|
||||
xs = this % total(g,iazi,ipol)
|
||||
case('absorption')
|
||||
xs = this % absorption(g,iazi,ipol)
|
||||
case('fission')
|
||||
xs = this % fission(g,iazi,ipol)
|
||||
case('k_fission')
|
||||
xs = this % k_fission(g,iazi,ipol)
|
||||
case('nu_fission')
|
||||
xs = this % nu_fission(g,iazi,ipol)
|
||||
case('scatter')
|
||||
xs = this % scattxs(g,iazi,ipol)
|
||||
case('mult')
|
||||
if (present(gout)) then
|
||||
xs = this % scatter(iazi,ipol) % obj % mult(gout,g)
|
||||
else
|
||||
xs = sum(this % scatter(iazi,ipol) % obj % mult(:,g))
|
||||
end if
|
||||
end select
|
||||
end if
|
||||
|
||||
end function macroxsangle_get_xs
|
||||
|
||||
!===============================================================================
|
||||
! MACROXS_*_SAMPLE_FISSION_ENERGY samples the outgoing energy from a fission
|
||||
! event
|
||||
!===============================================================================
|
||||
|
||||
function macroxsiso_sample_fission_energy(this, gin, uvw) result(gout)
|
||||
class(MacroXSIso), intent(in) :: this ! Data to work with
|
||||
integer, intent(in) :: gin ! Incoming energy group
|
||||
real(8), intent(in) :: uvw(3) ! Particle Direction
|
||||
integer :: gout ! Sampled outgoing group
|
||||
real(8) :: xi ! Our random number
|
||||
real(8) :: prob ! Running probability
|
||||
|
||||
xi = prn()
|
||||
prob = ZERO
|
||||
gout = 0
|
||||
|
||||
do while (prob < xi)
|
||||
gout = gout + 1
|
||||
prob = prob + this % chi(gout,gin)
|
||||
end do
|
||||
|
||||
end function macroxsiso_sample_fission_energy
|
||||
|
||||
function macroxsangle_sample_fission_energy(this, gin, uvw) result(gout)
|
||||
class(MacroXSAngle), intent(in) :: this ! Data to work with
|
||||
integer, intent(in) :: gin ! Incoming energy group
|
||||
real(8), intent(in) :: uvw(3) ! Particle Direction
|
||||
integer :: gout ! Sampled outgoing group
|
||||
real(8) :: xi ! Our random number
|
||||
real(8) :: prob ! Running probability
|
||||
integer :: iazi, ipol
|
||||
|
||||
call find_angle(this % polar, this % azimuthal, uvw, iazi, ipol)
|
||||
|
||||
xi = prn()
|
||||
prob = ZERO
|
||||
gout = 0
|
||||
|
||||
do while (prob < xi)
|
||||
gout = gout + 1
|
||||
prob = prob + this % chi(gout,gin,iazi,ipol)
|
||||
end do
|
||||
|
||||
end function macroxsangle_sample_fission_energy
|
||||
|
||||
!===============================================================================
|
||||
! MACROXS*_SAMPLE_SCATTER Selects outgoing energy and angle after a scatter
|
||||
! event
|
||||
!===============================================================================
|
||||
|
||||
subroutine macroxsiso_sample_scatter(this, uvw, gin, gout, mu, wgt)
|
||||
class(MacroXSIso), intent(in) :: this
|
||||
real(8), intent(in) :: uvw(3) ! Incoming neutron direction
|
||||
integer, intent(in) :: gin ! Incoming neutron group
|
||||
integer, intent(out) :: gout ! Sampled outgoin group
|
||||
real(8), intent(out) :: mu ! Sampled change in angle
|
||||
real(8), intent(inout) :: wgt ! Particle weight
|
||||
|
||||
call this % scatter % sample(gin, gout, mu, wgt)
|
||||
|
||||
end subroutine macroxsiso_sample_scatter
|
||||
|
||||
subroutine macroxsangle_sample_scatter(this, uvw, gin, gout, mu, wgt)
|
||||
class(MacroXSAngle), intent(in) :: this
|
||||
real(8), intent(in) :: uvw(3) ! Incoming neutron direction
|
||||
integer, intent(in) :: gin ! Incoming neutron group
|
||||
integer, intent(out) :: gout ! Sampled outgoin group
|
||||
real(8), intent(out) :: mu ! Sampled change in angle
|
||||
real(8), intent(inout) :: wgt ! Particle weight
|
||||
|
||||
integer :: iazi, ipol ! Angular indices
|
||||
|
||||
call find_angle(this % polar, this % azimuthal, uvw, iazi, ipol)
|
||||
call this % scatter(iazi,ipol) % obj % sample(gin,gout,mu,wgt)
|
||||
|
||||
end subroutine macroxsangle_sample_scatter
|
||||
|
||||
!===============================================================================
|
||||
! MACROXS*_CALCULATE_XS determines the multi-group macroscopic cross sections
|
||||
! for the material the particle is currently traveling through.
|
||||
!===============================================================================
|
||||
|
||||
subroutine macroxsiso_calculate_xs(this, gin, uvw, xs)
|
||||
class(MacroXSIso), intent(in) :: this
|
||||
integer, intent(in) :: gin ! Incoming neutron group
|
||||
real(8), intent(in) :: uvw(3) ! Incoming neutron direction
|
||||
type(MaterialMacroXS), intent(inout) :: xs ! Resultant MacroXS Data
|
||||
|
||||
xs % total = this % total(gin)
|
||||
xs % elastic = this % scattxs(gin)
|
||||
xs % absorption = this % absorption(gin)
|
||||
xs % nu_fission = this % nu_fission(gin)
|
||||
|
||||
end subroutine macroxsiso_calculate_xs
|
||||
|
||||
subroutine macroxsangle_calculate_xs(this, gin, uvw, xs)
|
||||
class(MacroXSAngle), intent(in) :: this
|
||||
integer, intent(in) :: gin ! Incoming neutron group
|
||||
real(8), intent(in) :: uvw(3) ! Incoming neutron direction
|
||||
type(MaterialMacroXS), intent(inout) :: xs ! Resultant MacroXS Data
|
||||
|
||||
integer :: iazi, ipol
|
||||
|
||||
call find_angle(this % polar, this % azimuthal, uvw, iazi, ipol)
|
||||
xs % total = this % total(gin, iazi, ipol)
|
||||
xs % elastic = this % scattxs(gin, iazi, ipol)
|
||||
xs % absorption = this % absorption(gin, iazi, ipol)
|
||||
xs % nu_fission = this % nu_fission(gin, iazi, ipol)
|
||||
|
||||
end subroutine macroxsangle_calculate_xs
|
||||
|
||||
end module macroxs_header
|
||||
69
src/math.F90
69
src/math.F90
|
|
@ -573,6 +573,49 @@ contains
|
|||
|
||||
end function calc_rn
|
||||
|
||||
!===============================================================================
|
||||
! EXPAND_HARMONIC expands a given series of real spherical harmonics
|
||||
!===============================================================================
|
||||
|
||||
pure function expand_harmonic(data, order, uvw) result(val)
|
||||
real(8), intent(in) :: data(:)
|
||||
integer, intent(in) :: order
|
||||
real(8), intent(in) :: uvw(3)
|
||||
real(8) :: val
|
||||
|
||||
integer :: l, lm_lo, lm_hi
|
||||
|
||||
val = data(1)
|
||||
lm_lo = 2
|
||||
lm_hi = 4
|
||||
do l = 1, order - 1
|
||||
val = val + sqrt(TWO * real(l,8) + ONE) * &
|
||||
dot_product(calc_rn(l,uvw), data(lm_lo:lm_hi))
|
||||
lm_lo = lm_hi + 1
|
||||
lm_hi = lm_lo + 2 * (l + 1)
|
||||
end do
|
||||
|
||||
end function expand_harmonic
|
||||
|
||||
!===============================================================================
|
||||
! EVALUATE_LEGENDRE Find the value of f(x) given a set of Legendre coefficients
|
||||
! and the value of x
|
||||
!===============================================================================
|
||||
|
||||
pure function evaluate_legendre(data, x) result(val)
|
||||
real(8), intent(in) :: data(:)
|
||||
real(8), intent(in) :: x
|
||||
real(8) :: val
|
||||
|
||||
integer :: l
|
||||
|
||||
val = HALF * data(1)
|
||||
do l = 1, size(data) - 1
|
||||
val = val + (real(l,8) + HALF) * data(l + 1) * calc_pn(l,x)
|
||||
end do
|
||||
|
||||
end function evaluate_legendre
|
||||
|
||||
!===============================================================================
|
||||
! ROTATE_ANGLE rotates direction cosines through a polar angle whose cosine is
|
||||
! mu and through an azimuthal angle sampled uniformly. Note that this is done
|
||||
|
|
@ -762,4 +805,30 @@ contains
|
|||
end do
|
||||
end subroutine broaden_n_polynomials
|
||||
|
||||
!===============================================================================
|
||||
! find_angle finds the closest angle on the data grid and returns that index
|
||||
!===============================================================================
|
||||
|
||||
pure subroutine find_angle(polar, azimuthal, uvw, i_azi, i_pol)
|
||||
real(8), intent(in) :: polar(:) ! Polar angles [0,pi]
|
||||
real(8), intent(in) :: azimuthal(:) ! Azi. angles [-pi,pi]
|
||||
real(8), intent(in) :: uvw(3) ! Direction of motion
|
||||
integer, intent(inout) :: i_pol ! Closest polar bin
|
||||
integer, intent(inout) :: i_azi ! Closest azi bin
|
||||
|
||||
real(8) :: my_pol, my_azi, dangle
|
||||
|
||||
! Convert uvw to polar and azi
|
||||
|
||||
my_pol = acos(uvw(3))
|
||||
my_azi = atan2(uvw(2), uvw(1))
|
||||
|
||||
! Search for equi-binned angles
|
||||
dangle = PI / real(size(polar),8)
|
||||
i_pol = floor(my_pol / dangle + ONE)
|
||||
dangle = TWO * PI / real(size(azimuthal),8)
|
||||
i_azi = floor((my_azi + PI) / dangle + ONE)
|
||||
|
||||
end subroutine find_angle
|
||||
|
||||
end module math
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ module mesh
|
|||
use constants
|
||||
use global
|
||||
use mesh_header
|
||||
use particle_header, only: Particle
|
||||
use search, only: binary_search
|
||||
|
||||
#ifdef MPI
|
||||
|
|
@ -160,10 +159,10 @@ contains
|
|||
sites_outside)
|
||||
|
||||
type(RegularMesh), pointer :: m ! mesh to count sites
|
||||
type(Bank), intent(in) :: bank_array(:) ! fission or source bank
|
||||
real(8), intent(out) :: cnt(:,:,:,:) ! weight of sites in each
|
||||
type(Bank), intent(in) :: bank_array(:) ! fission or source bank
|
||||
real(8), intent(out) :: cnt(:,:,:,:) ! weight of sites in each
|
||||
! cell and energy group
|
||||
real(8), intent(in), optional :: energies(:) ! energy grid to search
|
||||
real(8), intent(in), optional :: energies(:) ! energy grid to search
|
||||
integer(8), intent(in), optional :: size_bank ! # of bank sites (on each proc)
|
||||
logical, intent(inout), optional :: sites_outside ! were there sites outside mesh?
|
||||
|
||||
|
|
|
|||
224
src/mgxs_data.F90
Normal file
224
src/mgxs_data.F90
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
module mgxs_data
|
||||
|
||||
use constants
|
||||
use error, only: fatal_error
|
||||
use global
|
||||
use macroxs_header
|
||||
use material_header, only: Material
|
||||
use nuclide_header
|
||||
use output, only: write_message
|
||||
use set_header, only: SetChar
|
||||
use string, only: to_lower
|
||||
use xml_interface
|
||||
|
||||
implicit none
|
||||
|
||||
contains
|
||||
|
||||
!===============================================================================
|
||||
! READ_XS reads all the cross sections for the problem and stores them in
|
||||
! nuclides and sab_tables arrays
|
||||
!===============================================================================
|
||||
|
||||
subroutine read_mgxs()
|
||||
|
||||
integer :: i ! index in materials array
|
||||
integer :: j ! index over nuclides in material
|
||||
integer :: i_listing ! index in xs_listings array
|
||||
integer :: i_nuclide ! index in nuclides
|
||||
character(12) :: name ! name of isotope, e.g. 92235.03c
|
||||
character(12) :: alias ! alias of isotope, e.g. U-235.03c
|
||||
integer :: representation ! Data representation
|
||||
type(Material), pointer :: mat
|
||||
type(SetChar) :: already_read
|
||||
type(Node), pointer :: doc => null()
|
||||
type(Node), pointer :: node_xsdata
|
||||
type(NodeList), pointer :: node_xsdata_list => null()
|
||||
logical :: file_exists
|
||||
character(MAX_LINE_LEN) :: temp_str
|
||||
logical :: get_kfiss, get_fiss
|
||||
integer :: l
|
||||
|
||||
! Check if cross_sections.xml exists
|
||||
inquire(FILE=path_cross_sections, EXIST=file_exists)
|
||||
if (.not. file_exists) then
|
||||
! Could not find cross_sections.xml file
|
||||
call fatal_error("Cross sections XML file '" &
|
||||
&// trim(path_cross_sections) // "' does not exist!")
|
||||
end if
|
||||
|
||||
call write_message("Loading Cross Section Data...", 5)
|
||||
|
||||
! Parse cross_sections.xml file
|
||||
call open_xmldoc(doc, path_cross_sections)
|
||||
|
||||
! Get node list of all <xsdata>
|
||||
call get_node_list(doc, "xsdata", node_xsdata_list)
|
||||
n_listings = get_list_size(node_xsdata_list)
|
||||
|
||||
! allocate arrays for ACE table storage and cross section cache
|
||||
allocate(nuclides_MG(n_nuclides_total))
|
||||
!$omp parallel
|
||||
allocate(micro_xs(n_nuclides_total))
|
||||
!$omp end parallel
|
||||
|
||||
! Find out if we need fission & kappa fission
|
||||
! (i.e., are there any SCORE_FISSION or SCORE_KAPPA_FISSION tallies?)
|
||||
get_kfiss = .false.
|
||||
get_fiss = .false.
|
||||
do i = 1, n_tallies
|
||||
do l = 1, tallies(i) % n_score_bins
|
||||
if (tallies(i) % score_bins(l) == SCORE_KAPPA_FISSION) then
|
||||
get_kfiss = .true.
|
||||
end if
|
||||
if (tallies(i) % score_bins(l) == SCORE_FISSION) then
|
||||
get_fiss = .true.
|
||||
end if
|
||||
end do
|
||||
if (get_kfiss .and. get_fiss) exit
|
||||
end do
|
||||
|
||||
! ==========================================================================
|
||||
! READ ALL ACE CROSS SECTION TABLES
|
||||
|
||||
! Loop over all files
|
||||
MATERIAL_LOOP: do i = 1, n_materials
|
||||
mat => materials(i)
|
||||
|
||||
NUCLIDE_LOOP: do j = 1, mat % n_nuclides
|
||||
name = mat % names(j)
|
||||
|
||||
if (.not. already_read % contains(name)) then
|
||||
i_listing = xs_listing_dict % get_key(to_lower(name))
|
||||
i_nuclide = mat % nuclide(j)
|
||||
name = xs_listings(i_listing) % name
|
||||
alias = xs_listings(i_listing) % alias
|
||||
|
||||
! Get pointer to xsdata table XML node
|
||||
call get_list_item(node_xsdata_list, i_listing, node_xsdata)
|
||||
|
||||
call write_message("Loading " // trim(name) // " Data...", 5)
|
||||
|
||||
! First find out the data representation
|
||||
if (check_for_node(node_xsdata, "representation")) then
|
||||
call get_node_value(node_xsdata, "representation", temp_str)
|
||||
temp_str = trim(to_lower(temp_str))
|
||||
if (temp_str == 'isotropic' .or. temp_str == 'iso') then
|
||||
representation = MGXS_ISOTROPIC
|
||||
else if (temp_str == 'angle') then
|
||||
representation = MGXS_ANGLE
|
||||
else
|
||||
call fatal_error("Invalid Data Representation!")
|
||||
end if
|
||||
else
|
||||
! Default to isotropic representation
|
||||
representation = MGXS_ISOTROPIC
|
||||
end if
|
||||
|
||||
! Now allocate accordingly
|
||||
select case(representation)
|
||||
case(MGXS_ISOTROPIC)
|
||||
allocate(NuclideIso :: nuclides_MG(i_nuclide) % obj)
|
||||
case(MGXS_ANGLE)
|
||||
allocate(NuclideAngle :: nuclides_MG(i_nuclide) % obj)
|
||||
end select
|
||||
|
||||
! Now read in the data specific to the type we just declared
|
||||
call nuclides_MG(i_nuclide) % obj % init(node_xsdata, energy_groups, &
|
||||
get_kfiss, get_fiss)
|
||||
|
||||
! Keep track of what listing is associated with this nuclide
|
||||
nuclides_MG(i_nuclide) % obj % listing = i_listing
|
||||
|
||||
! Add name and alias to dictionary
|
||||
call already_read % add(name)
|
||||
call already_read % add(alias)
|
||||
end if
|
||||
end do NUCLIDE_LOOP
|
||||
end do MATERIAL_LOOP
|
||||
|
||||
! Avoid some valgrind leak errors
|
||||
call already_read % clear()
|
||||
|
||||
! Loop around material
|
||||
MATERIAL_LOOP3: do i = 1, n_materials
|
||||
|
||||
! Get material
|
||||
mat => materials(i)
|
||||
|
||||
! Loop around nuclides in material
|
||||
NUCLIDE_LOOP2: do j = 1, mat % n_nuclides
|
||||
! Is this fissionable?
|
||||
if (nuclides_MG(mat % nuclide(j)) % obj % fissionable) then
|
||||
mat % fissionable = .true.
|
||||
end if
|
||||
if (mat % fissionable) then
|
||||
exit NUCLIDE_LOOP2
|
||||
end if
|
||||
|
||||
end do NUCLIDE_LOOP2
|
||||
end do MATERIAL_LOOP3
|
||||
|
||||
end subroutine read_mgxs
|
||||
|
||||
!===============================================================================
|
||||
! CREATE_MACRO_XS generates the macroscopic x/s from the microscopic input data
|
||||
!===============================================================================
|
||||
|
||||
subroutine create_macro_xs()
|
||||
integer :: i_mat ! index in materials array
|
||||
integer :: i ! loop index over nuclides
|
||||
integer :: l ! Loop over score bins
|
||||
type(Material), pointer :: mat ! current material
|
||||
logical :: get_kfiss, get_fiss
|
||||
integer :: error_code
|
||||
character(MAX_LINE_LEN) :: error_text
|
||||
integer :: scatt_type
|
||||
integer :: legendre_mu_points
|
||||
|
||||
! Find out if we need fission & kappa fission
|
||||
! (i.e., are there any SCORE_FISSION or SCORE_KAPPA_FISSION tallies?)
|
||||
get_kfiss = .false.
|
||||
get_fiss = .false.
|
||||
do i = 1, n_tallies
|
||||
do l = 1, tallies(i) % n_score_bins
|
||||
if (tallies(i) % score_bins(l) == SCORE_KAPPA_FISSION) then
|
||||
get_kfiss = .true.
|
||||
end if
|
||||
if (tallies(i) % score_bins(l) == SCORE_FISSION) then
|
||||
get_fiss = .true.
|
||||
end if
|
||||
end do
|
||||
if (get_kfiss .and. get_fiss) &
|
||||
exit
|
||||
end do
|
||||
|
||||
allocate(macro_xs(n_materials))
|
||||
|
||||
do i_mat = 1, n_materials
|
||||
mat => materials(i_mat)
|
||||
|
||||
! Check to see how our nuclides are represented
|
||||
! Force all to be the same type
|
||||
! Therefore type(nuclides(mat % nuclide(1)) % obj) dictates type(macroxs)
|
||||
! At the same time, we will find the scattering type, as that will dictate
|
||||
! how we allocate the scatter object within macroxs
|
||||
legendre_mu_points = nuclides_MG(mat % nuclide(1)) % obj % legendre_mu_points
|
||||
scatt_type = nuclides_MG(mat % nuclide(1)) % obj % scatt_type
|
||||
select type(nuc => nuclides_MG(mat % nuclide(1)) % obj)
|
||||
type is (NuclideIso)
|
||||
allocate(MacroXSIso :: macro_xs(i_mat) % obj)
|
||||
type is (NuclideAngle)
|
||||
allocate(MacroXSAngle :: macro_xs(i_mat) % obj)
|
||||
end select
|
||||
|
||||
call macro_xs(i_mat) % obj % init(mat, nuclides_MG, energy_groups, &
|
||||
get_kfiss, get_fiss, max_order, &
|
||||
scatt_type, legendre_mu_points, &
|
||||
error_code, error_text)
|
||||
! Handle any errors
|
||||
if (error_code /= 0) call fatal_error(trim(error_text))
|
||||
end do
|
||||
end subroutine create_macro_xs
|
||||
|
||||
end module mgxs_data
|
||||
1179
src/nuclide_header.F90
Normal file
1179
src/nuclide_header.F90
Normal file
File diff suppressed because it is too large
Load diff
209
src/output.F90
209
src/output.F90
|
|
@ -2,7 +2,6 @@ module output
|
|||
|
||||
use, intrinsic :: ISO_FORTRAN_ENV
|
||||
|
||||
use ace_header, only: Nuclide, Reaction, UrrData
|
||||
use constants
|
||||
use endf, only: reaction_name
|
||||
use error, only: fatal_error, warning
|
||||
|
|
@ -12,8 +11,10 @@ module output
|
|||
use math, only: t_percentile
|
||||
use mesh_header, only: RegularMesh
|
||||
use mesh, only: mesh_indices_to_bin, bin_to_mesh_indices
|
||||
use nuclide_header
|
||||
use particle_header, only: LocalCoord, Particle
|
||||
use plot_header
|
||||
use sab_header, only: SAlphaBeta
|
||||
use string, only: to_upper, to_str
|
||||
use tally_header, only: TallyObject
|
||||
|
||||
|
|
@ -100,9 +101,9 @@ contains
|
|||
!===============================================================================
|
||||
|
||||
subroutine header(msg, unit, level)
|
||||
character(*), intent(in) :: msg ! header message
|
||||
integer, intent(in), optional :: unit ! unit to write to
|
||||
integer, intent(in), optional :: level ! specified header level
|
||||
character(*), intent(in) :: msg ! header message
|
||||
integer, intent(in), optional :: unit ! unit to write to
|
||||
integer, intent(in), optional :: level ! specified header level
|
||||
|
||||
integer :: n ! number of = signs on left
|
||||
integer :: m ! number of = signs on right
|
||||
|
|
@ -305,175 +306,16 @@ contains
|
|||
|
||||
! Display weight, energy, grid index, and interpolation factor
|
||||
write(ou,*) ' Weight = ' // to_str(p % wgt)
|
||||
write(ou,*) ' Energy = ' // to_str(p % E)
|
||||
if (run_CE) then
|
||||
write(ou,*) ' Energy = ' // to_str(p % E)
|
||||
else
|
||||
write(ou,*) ' Energy Group = ' // to_str(p % g)
|
||||
end if
|
||||
write(ou,*) ' Delayed Group = ' // to_str(p % delayed_group)
|
||||
write(ou,*)
|
||||
|
||||
end subroutine print_particle
|
||||
|
||||
!===============================================================================
|
||||
! PRINT_NUCLIDE displays information about a continuous-energy neutron
|
||||
! cross_section table and its reactions and secondary angle/energy distributions
|
||||
!===============================================================================
|
||||
|
||||
subroutine print_nuclide(nuc, unit)
|
||||
type(Nuclide), intent(in) :: nuc
|
||||
integer, intent(in), optional :: unit
|
||||
|
||||
integer :: i ! loop index over nuclides
|
||||
integer :: unit_ ! unit to write to
|
||||
integer :: size_xs ! memory used for cross-sections (bytes)
|
||||
integer :: size_urr ! memory used for probability tables (bytes)
|
||||
type(UrrData), pointer :: urr
|
||||
|
||||
! set default unit for writing information
|
||||
if (present(unit)) then
|
||||
unit_ = unit
|
||||
else
|
||||
unit_ = OUTPUT_UNIT
|
||||
end if
|
||||
|
||||
! Initialize totals
|
||||
size_urr = 0
|
||||
size_xs = 0
|
||||
|
||||
! Basic nuclide information
|
||||
write(unit_,*) 'Nuclide ' // trim(nuc % name)
|
||||
write(unit_,*) ' zaid = ' // trim(to_str(nuc % zaid))
|
||||
write(unit_,*) ' awr = ' // trim(to_str(nuc % awr))
|
||||
write(unit_,*) ' kT = ' // trim(to_str(nuc % kT))
|
||||
write(unit_,*) ' # of grid points = ' // trim(to_str(nuc % n_grid))
|
||||
write(unit_,*) ' Fissionable = ', nuc % fissionable
|
||||
write(unit_,*) ' # of fission reactions = ' // trim(to_str(nuc % n_fission))
|
||||
write(unit_,*) ' # of reactions = ' // trim(to_str(nuc % n_reaction))
|
||||
|
||||
! Information on each reaction
|
||||
write(unit_,*) ' Reaction Q-value COM IE'
|
||||
do i = 1, nuc % n_reaction
|
||||
associate (rxn => nuc % reactions(i))
|
||||
write(unit_,'(3X,A11,1X,F8.3,3X,L1,3X,I6)') &
|
||||
reaction_name(rxn % MT), rxn % Q_value, rxn % scatter_in_cm, &
|
||||
rxn % threshold
|
||||
|
||||
! Accumulate data size
|
||||
size_xs = size_xs + (nuc % n_grid - rxn%threshold + 1) * 8
|
||||
end associate
|
||||
end do
|
||||
|
||||
! Add memory required for summary reactions (total, absorption, fission,
|
||||
! nu-fission)
|
||||
size_xs = 8 * nuc % n_grid * 4
|
||||
|
||||
! Write information about URR probability tables
|
||||
size_urr = 0
|
||||
if (nuc % urr_present) then
|
||||
urr => nuc % urr_data
|
||||
write(unit_,*) ' Unresolved resonance probability table:'
|
||||
write(unit_,*) ' # of energies = ' // trim(to_str(urr % n_energy))
|
||||
write(unit_,*) ' # of probabilities = ' // trim(to_str(urr % n_prob))
|
||||
write(unit_,*) ' Interpolation = ' // trim(to_str(urr % interp))
|
||||
write(unit_,*) ' Inelastic flag = ' // trim(to_str(urr % inelastic_flag))
|
||||
write(unit_,*) ' Absorption flag = ' // trim(to_str(urr % absorption_flag))
|
||||
write(unit_,*) ' Multiply by smooth? ', urr % multiply_smooth
|
||||
write(unit_,*) ' Min energy = ', trim(to_str(urr % energy(1)))
|
||||
write(unit_,*) ' Max energy = ', trim(to_str(urr % energy(urr % n_energy)))
|
||||
|
||||
! Calculate memory used by probability tables and add to total
|
||||
size_urr = urr % n_energy * (urr % n_prob * 6 + 1) * 8
|
||||
end if
|
||||
|
||||
! Write memory used
|
||||
write(unit_,*) ' Memory Requirements'
|
||||
write(unit_,*) ' Cross sections = ' // trim(to_str(size_xs)) // ' bytes'
|
||||
write(unit_,*) ' Probability Tables = ' // &
|
||||
trim(to_str(size_urr)) // ' bytes'
|
||||
|
||||
! Blank line at end of nuclide
|
||||
write(unit_,*)
|
||||
|
||||
end subroutine print_nuclide
|
||||
|
||||
!===============================================================================
|
||||
! PRINT_SAB_TABLE displays information about a S(a,b) table containing data
|
||||
! describing thermal scattering from bound materials such as hydrogen in water.
|
||||
!===============================================================================
|
||||
|
||||
subroutine print_sab_table(sab, unit)
|
||||
type(SAlphaBeta), intent(in) :: sab
|
||||
integer, intent(in), optional :: unit
|
||||
|
||||
integer :: size_sab ! memory used by S(a,b) table
|
||||
integer :: unit_ ! unit to write to
|
||||
integer :: i ! Loop counter for parsing through sab % zaid
|
||||
integer :: char_count ! Counter for the number of characters on a line
|
||||
|
||||
! set default unit for writing information
|
||||
if (present(unit)) then
|
||||
unit_ = unit
|
||||
else
|
||||
unit_ = OUTPUT_UNIT
|
||||
end if
|
||||
|
||||
! Basic S(a,b) table information
|
||||
write(unit_,*) 'S(a,b) Table ' // trim(sab % name)
|
||||
write(unit_,'(A)',advance="no") ' zaids = '
|
||||
! Initialize the counter based on the above string
|
||||
char_count = 11
|
||||
do i = 1, sab % n_zaid
|
||||
! Deal with a line thats too long
|
||||
if (char_count >= 73) then ! 73 = 80 - (5 ZAID chars + 1 space + 1 comma)
|
||||
! End the line
|
||||
write(unit_,*) ""
|
||||
! Add 11 leading blanks
|
||||
write(unit_,'(A)', advance="no") " "
|
||||
! reset the counter to 11
|
||||
char_count = 11
|
||||
end if
|
||||
if (i < sab % n_zaid) then
|
||||
! Include a comma
|
||||
write(unit_,'(A)',advance="no") trim(to_str(sab % zaid(i))) // ", "
|
||||
char_count = char_count + len(trim(to_str(sab % zaid(i)))) + 2
|
||||
else
|
||||
! Don't include a comma, since we are all done
|
||||
write(unit_,'(A)',advance="no") trim(to_str(sab % zaid(i)))
|
||||
end if
|
||||
|
||||
end do
|
||||
write(unit_,*) "" ! Move to next line
|
||||
write(unit_,*) ' awr = ' // trim(to_str(sab % awr))
|
||||
write(unit_,*) ' kT = ' // trim(to_str(sab % kT))
|
||||
|
||||
! Inelastic data
|
||||
write(unit_,*) ' # of Incoming Energies (Inelastic) = ' // &
|
||||
trim(to_str(sab % n_inelastic_e_in))
|
||||
write(unit_,*) ' # of Outgoing Energies (Inelastic) = ' // &
|
||||
trim(to_str(sab % n_inelastic_e_out))
|
||||
write(unit_,*) ' # of Outgoing Angles (Inelastic) = ' // &
|
||||
trim(to_str(sab % n_inelastic_mu))
|
||||
write(unit_,*) ' Threshold for Inelastic = ' // &
|
||||
trim(to_str(sab % threshold_inelastic))
|
||||
|
||||
! Elastic data
|
||||
if (sab % n_elastic_e_in > 0) then
|
||||
write(unit_,*) ' # of Incoming Energies (Elastic) = ' // &
|
||||
trim(to_str(sab % n_elastic_e_in))
|
||||
write(unit_,*) ' # of Outgoing Angles (Elastic) = ' // &
|
||||
trim(to_str(sab % n_elastic_mu))
|
||||
write(unit_,*) ' Threshold for Elastic = ' // &
|
||||
trim(to_str(sab % threshold_elastic))
|
||||
end if
|
||||
|
||||
! Determine memory used by S(a,b) table and write out
|
||||
size_sab = 8 * (sab % n_inelastic_e_in * (2 + sab % n_inelastic_e_out * &
|
||||
(1 + sab % n_inelastic_mu)) + sab % n_elastic_e_in * &
|
||||
(2 + sab % n_elastic_mu))
|
||||
write(unit_,*) ' Memory Used = ' // trim(to_str(size_sab)) // ' bytes'
|
||||
|
||||
! Blank line at end
|
||||
write(unit_,*)
|
||||
|
||||
end subroutine print_sab_table
|
||||
|
||||
!===============================================================================
|
||||
! WRITE_XS_SUMMARY writes information about each nuclide and S(a,b) table to a
|
||||
! file called cross_sections.out. This file shows the list of reactions as well
|
||||
|
|
@ -486,8 +328,6 @@ contains
|
|||
integer :: i ! loop index
|
||||
integer :: unit_xs ! cross_sections.out file unit
|
||||
character(MAX_FILE_LEN) :: path ! path of summary file
|
||||
type(Nuclide), pointer :: nuc
|
||||
type(SAlphaBeta), pointer :: sab
|
||||
|
||||
! Create filename for log file
|
||||
path = trim(path_output) // "cross_sections.out"
|
||||
|
|
@ -498,21 +338,22 @@ contains
|
|||
! Write header
|
||||
call header("CROSS SECTION TABLES", unit=unit_xs)
|
||||
|
||||
NUCLIDE_LOOP: do i = 1, n_nuclides_total
|
||||
! Get pointer to nuclide
|
||||
nuc => nuclides(i)
|
||||
if (run_CE) then
|
||||
NUCLIDE_LOOP: do i = 1, n_nuclides_total
|
||||
! Print information about nuclide
|
||||
call nuclides(i) % print(unit=unit_xs)
|
||||
end do NUCLIDE_LOOP
|
||||
|
||||
! Print information about nuclide
|
||||
call print_nuclide(nuc, unit=unit_xs)
|
||||
end do NUCLIDE_LOOP
|
||||
|
||||
SAB_TABLES_LOOP: do i = 1, n_sab_tables
|
||||
! Get pointer to S(a,b) table
|
||||
sab => sab_tables(i)
|
||||
|
||||
! Print information about S(a,b) table
|
||||
call print_sab_table(sab, unit=unit_xs)
|
||||
end do SAB_TABLES_LOOP
|
||||
SAB_TABLES_LOOP: do i = 1, n_sab_tables
|
||||
! Print information about S(a,b) table
|
||||
call sab_tables(i) % print(unit=unit_xs)
|
||||
end do SAB_TABLES_LOOP
|
||||
else
|
||||
NuclideMG_LOOP: do i = 1, n_nuclides_total
|
||||
! Print information about nuclide
|
||||
call nuclides_mg(i) % obj % print(unit=unit_xs)
|
||||
end do NuclideMG_LOOP
|
||||
end if
|
||||
|
||||
! Close cross section summary file
|
||||
close(unit_xs)
|
||||
|
|
|
|||
|
|
@ -47,9 +47,14 @@ module particle_header
|
|||
integer :: n_coord ! number of current coordinates
|
||||
type(LocalCoord) :: coord(MAX_COORD) ! coordinates for all levels
|
||||
|
||||
! Energy Data
|
||||
real(8) :: E ! post-collision energy
|
||||
real(8) :: last_E ! pre-collision energy
|
||||
integer :: g ! post-collision energy group (MG only)
|
||||
integer :: last_g ! pre-collision energy group (MG only)
|
||||
|
||||
! Other physical data
|
||||
real(8) :: wgt ! particle weight
|
||||
real(8) :: E ! energy
|
||||
real(8) :: mu ! angle of scatter
|
||||
logical :: alive ! is particle alive?
|
||||
|
||||
|
|
@ -57,7 +62,6 @@ module particle_header
|
|||
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) :: last_E ! pre-collision energy
|
||||
real(8) :: absorb_wgt ! weight absorbed for survival biasing
|
||||
|
||||
! What event last took place
|
||||
|
|
@ -132,6 +136,7 @@ contains
|
|||
this % fission = .false.
|
||||
this % delayed_group = 0
|
||||
this % n_delayed_bank(:) = 0
|
||||
this % g = 1
|
||||
|
||||
! Set up base level coordinates
|
||||
this % coord(1) % universe = BASE_UNIVERSE
|
||||
|
|
@ -178,9 +183,11 @@ contains
|
|||
! fission, or simply as a secondary particle.
|
||||
!===============================================================================
|
||||
|
||||
subroutine initialize_from_source(this, src)
|
||||
class(Particle), intent(inout) :: this
|
||||
type(Bank), intent(in) :: src
|
||||
subroutine initialize_from_source(this, src, run_CE, energy_bin_avg)
|
||||
class(Particle), intent(inout) :: this
|
||||
type(Bank), intent(in) :: src
|
||||
logical, intent(in) :: run_CE
|
||||
real(8), allocatable, intent(in) :: energy_bin_avg(:)
|
||||
|
||||
! set defaults
|
||||
call this % initialize()
|
||||
|
|
@ -192,8 +199,14 @@ contains
|
|||
this % coord(1) % uvw = src % uvw
|
||||
this % last_xyz = src % xyz
|
||||
this % last_uvw = src % uvw
|
||||
this % E = src % E
|
||||
this % last_E = src % E
|
||||
if (run_CE) then
|
||||
this % E = src % E
|
||||
else
|
||||
this % g = int(src % E)
|
||||
this % last_g = int(src % E)
|
||||
this % E = energy_bin_avg(this % g)
|
||||
end if
|
||||
this % last_E = src % E
|
||||
|
||||
end subroutine initialize_from_source
|
||||
|
||||
|
|
@ -202,12 +215,13 @@ contains
|
|||
! the secondary bank and increments the number of sites in the secondary bank.
|
||||
!===============================================================================
|
||||
|
||||
subroutine create_secondary(this, uvw, type)
|
||||
subroutine create_secondary(this, uvw, type, run_CE)
|
||||
class(Particle), intent(inout) :: this
|
||||
real(8), intent(in) :: uvw(3)
|
||||
integer, intent(in) :: type
|
||||
logical, intent(in) :: run_CE
|
||||
|
||||
integer :: n
|
||||
integer(8) :: n
|
||||
|
||||
! Check to make sure that the hard-limit on secondary particles is not
|
||||
! exceeded.
|
||||
|
|
@ -219,8 +233,11 @@ contains
|
|||
this % secondary_bank(n) % wgt = this % wgt
|
||||
this % secondary_bank(n) % xyz(:) = this % coord(1) % xyz
|
||||
this % secondary_bank(n) % uvw(:) = uvw
|
||||
this % secondary_bank(n) % E = this % E
|
||||
this % n_secondary = n
|
||||
this % secondary_bank(this % n_secondary) % E = this % E
|
||||
if (.not. run_CE) then
|
||||
this % secondary_bank(this % n_secondary) % E = real(this % g, 8)
|
||||
end if
|
||||
|
||||
end subroutine create_secondary
|
||||
|
||||
|
|
|
|||
|
|
@ -97,6 +97,7 @@ contains
|
|||
call read_dataset(file_id, 'id', p%id)
|
||||
call read_dataset(file_id, 'weight', p%wgt)
|
||||
call read_dataset(file_id, 'energy', p%E)
|
||||
call read_dataset(file_id, 'energy_group', p%g)
|
||||
call read_dataset(file_id, 'xyz', p%coord(1)%xyz)
|
||||
call read_dataset(file_id, 'uvw', p%coord(1)%uvw)
|
||||
|
||||
|
|
@ -105,6 +106,7 @@ contains
|
|||
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)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
module particle_restart_write
|
||||
|
||||
use bank_header, only: Bank
|
||||
use bank_header, only: Bank
|
||||
use global
|
||||
use hdf5_interface
|
||||
use particle_header, only: Particle
|
||||
use string, only: to_str
|
||||
use particle_header, only: Particle
|
||||
use string, only: to_str
|
||||
|
||||
use hdf5
|
||||
|
||||
|
|
|
|||
197
src/physics.F90
197
src/physics.F90
|
|
@ -1,23 +1,23 @@
|
|||
module physics
|
||||
|
||||
use ace_header, only: Nuclide, Reaction
|
||||
use constants
|
||||
use cross_section, only: elastic_xs_0K
|
||||
use endf, only: reaction_name
|
||||
use error, only: fatal_error, warning
|
||||
use fission, only: nu_total, nu_delayed
|
||||
use global
|
||||
use interpolation, only: interpolate_tab1
|
||||
use material_header, only: Material
|
||||
use math, only: rotate_angle, maxwell_spectrum, watt_spectrum
|
||||
use math
|
||||
use mesh, only: get_mesh_indices
|
||||
use nuclide_header
|
||||
use output, only: write_message
|
||||
use particle_header, only: Particle
|
||||
use particle_restart_write, only: write_particle_restart
|
||||
use random_lcg, only: prn
|
||||
use physics_common
|
||||
use random_lcg, only: prn, advance_prn_seed, prn_set_stream
|
||||
use reaction_header, only: Reaction
|
||||
use search, only: binary_search
|
||||
use string, only: to_str
|
||||
use secondary_uncorrelated, only: UncorrelatedAngleEnergy
|
||||
use string, only: to_str
|
||||
|
||||
implicit none
|
||||
|
||||
|
|
@ -56,6 +56,13 @@ contains
|
|||
if (master) call warning("Killing neutron with extremely low energy")
|
||||
end if
|
||||
|
||||
! Advance URR seed stream 'N' times after energy changes
|
||||
if (p % E /= p % last_E) then
|
||||
call prn_set_stream(STREAM_URR_PTABLE)
|
||||
call advance_prn_seed(n_nuc_zaid_total)
|
||||
call prn_set_stream(STREAM_TRACKING)
|
||||
endif
|
||||
|
||||
end subroutine collision
|
||||
|
||||
!===============================================================================
|
||||
|
|
@ -73,7 +80,7 @@ contains
|
|||
integer :: i_nuclide ! index in nuclides array
|
||||
integer :: i_nuc_mat ! index in material's nuclides array
|
||||
integer :: i_reaction ! index in nuc % reactions array
|
||||
type(Nuclide), pointer :: nuc
|
||||
type(NuclideCE), pointer :: nuc
|
||||
|
||||
call sample_nuclide(p, 'total ', i_nuclide, i_nuc_mat)
|
||||
|
||||
|
|
@ -196,7 +203,7 @@ contains
|
|||
real(8) :: f
|
||||
real(8) :: prob
|
||||
real(8) :: cutoff
|
||||
type(Nuclide), pointer :: nuc
|
||||
type(NuclideCE), pointer :: nuc
|
||||
|
||||
! Get pointer to nuclide
|
||||
nuc => nuclides(i_nuclide)
|
||||
|
|
@ -242,7 +249,6 @@ contains
|
|||
!===============================================================================
|
||||
|
||||
subroutine absorption(p, i_nuclide)
|
||||
|
||||
type(Particle), intent(inout) :: p
|
||||
integer, intent(in) :: i_nuclide
|
||||
|
||||
|
|
@ -278,33 +284,11 @@ contains
|
|||
|
||||
end subroutine absorption
|
||||
|
||||
!===============================================================================
|
||||
! RUSSIAN_ROULETTE
|
||||
!===============================================================================
|
||||
|
||||
subroutine russian_roulette(p)
|
||||
|
||||
type(Particle), intent(inout) :: p
|
||||
|
||||
if (p % wgt < weight_cutoff) then
|
||||
if (prn() < p % wgt / weight_survive) then
|
||||
p % wgt = weight_survive
|
||||
p % last_wgt = p % wgt
|
||||
else
|
||||
p % wgt = ZERO
|
||||
p % last_wgt = ZERO
|
||||
p % alive = .false.
|
||||
end if
|
||||
end if
|
||||
|
||||
end subroutine russian_roulette
|
||||
|
||||
!===============================================================================
|
||||
! SCATTER
|
||||
!===============================================================================
|
||||
|
||||
subroutine scatter(p, i_nuclide, i_nuc_mat)
|
||||
|
||||
type(Particle), intent(inout) :: p
|
||||
integer, intent(in) :: i_nuclide
|
||||
integer, intent(in) :: i_nuc_mat
|
||||
|
|
@ -317,7 +301,7 @@ contains
|
|||
real(8) :: uvw_new(3) ! outgoing uvw for iso-in-lab scattering
|
||||
real(8) :: uvw_old(3) ! incoming uvw for iso-in-lab scattering
|
||||
real(8) :: phi ! azimuthal angle for iso-in-lab scattering
|
||||
type(Nuclide), pointer :: nuc
|
||||
type(NuclideCE), pointer :: nuc
|
||||
|
||||
! copy incoming direction
|
||||
uvw_old(:) = p % coord(1) % uvw
|
||||
|
|
@ -432,7 +416,7 @@ contains
|
|||
real(8) :: v_cm(3) ! velocity of center-of-mass
|
||||
real(8) :: v_t(3) ! velocity of target nucleus
|
||||
real(8) :: uvw_cm(3) ! directional cosines in center-of-mass
|
||||
type(Nuclide), pointer :: nuc
|
||||
type(NuclideCE), pointer :: nuc
|
||||
|
||||
! get pointer to nuclide
|
||||
nuc => nuclides(i_nuclide)
|
||||
|
|
@ -461,9 +445,9 @@ contains
|
|||
vel = sqrt(dot_product(v_n, v_n))
|
||||
|
||||
! Sample scattering angle
|
||||
select type (dist => rxn%secondary%distribution(1)%obj)
|
||||
select type (dist => rxn % products(1) % distribution(1) % obj)
|
||||
type is (UncorrelatedAngleEnergy)
|
||||
mu_cm = dist%angle%sample(E)
|
||||
mu_cm = dist % angle % sample(E)
|
||||
end select
|
||||
|
||||
! Determine direction cosines in CM
|
||||
|
|
@ -501,7 +485,6 @@ contains
|
|||
!===============================================================================
|
||||
|
||||
subroutine sab_scatter(i_nuclide, i_sab, E, uvw, mu)
|
||||
|
||||
integer, intent(in) :: i_nuclide ! index in micro_xs
|
||||
integer, intent(in) :: i_sab ! index in sab_tables
|
||||
real(8), intent(inout) :: E ! incoming/outgoing energy
|
||||
|
|
@ -759,7 +742,7 @@ contains
|
|||
!===============================================================================
|
||||
|
||||
subroutine sample_target_velocity(nuc, v_target, E, uvw, v_neut, wgt, xs_eff)
|
||||
type(Nuclide), intent(in) :: nuc ! target nuclide at temperature T
|
||||
type(NuclideCE), intent(in) :: nuc ! target nuclide at temperature T
|
||||
real(8), intent(out) :: v_target(3) ! target velocity
|
||||
real(8), intent(in) :: v_neut(3) ! neutron velocity
|
||||
real(8), intent(in) :: E ! particle energy
|
||||
|
|
@ -1004,10 +987,10 @@ contains
|
|||
!===============================================================================
|
||||
|
||||
subroutine sample_cxs_target_velocity(nuc, v_target, E, uvw)
|
||||
type(Nuclide), intent(in) :: nuc ! target nuclide at temperature
|
||||
real(8), intent(out) :: v_target(3)
|
||||
real(8), intent(in) :: E
|
||||
real(8), intent(in) :: uvw(3)
|
||||
type(NuclideCE), intent(in) :: nuc ! target nuclide at temperature
|
||||
real(8), intent(out) :: v_target(3)
|
||||
real(8), intent(in) :: E
|
||||
real(8), intent(in) :: uvw(3)
|
||||
|
||||
real(8) :: kT ! equilibrium temperature of target in MeV
|
||||
real(8) :: awr ! target/neutron mass ratio
|
||||
|
|
@ -1088,11 +1071,9 @@ contains
|
|||
integer :: nu ! actual number of neutrons produced
|
||||
integer :: ijk(3) ! indices in ufs mesh
|
||||
real(8) :: nu_t ! total nu
|
||||
real(8) :: mu ! fission neutron angular cosine
|
||||
real(8) :: phi ! fission neutron azimuthal angle
|
||||
real(8) :: weight ! weight adjustment for ufs method
|
||||
logical :: in_mesh ! source site in ufs mesh?
|
||||
type(Nuclide), pointer :: nuc
|
||||
type(NuclideCE), pointer :: nuc
|
||||
|
||||
! Get pointers
|
||||
nuc => nuclides(i_nuclide)
|
||||
|
|
@ -1160,25 +1141,12 @@ contains
|
|||
! Set weight of fission bank site
|
||||
bank_array(i) % wgt = ONE/weight
|
||||
|
||||
! Sample cosine of angle -- fission neutrons are always emitted
|
||||
! isotropically. Sometimes in ACE data, fission reactions actually have
|
||||
! an angular distribution listed, but for those that do, it's simply just
|
||||
! a uniform distribution in mu
|
||||
mu = TWO * prn() - ONE
|
||||
! Sample delayed group and angle/energy for fission reaction
|
||||
call sample_fission_neutron(nuc, nuc % reactions(i_reaction), &
|
||||
p % E, bank_array(i))
|
||||
|
||||
! Sample azimuthal angle uniformly in [0,2*pi)
|
||||
phi = TWO*PI*prn()
|
||||
bank_array(i) % uvw(1) = mu
|
||||
bank_array(i) % uvw(2) = sqrt(ONE - mu*mu) * cos(phi)
|
||||
bank_array(i) % uvw(3) = sqrt(ONE - mu*mu) * sin(phi)
|
||||
|
||||
! Sample secondary energy distribution for fission reaction and set energy
|
||||
! in fission bank
|
||||
bank_array(i) % E = sample_fission_energy(nuc, &
|
||||
nuc % reactions(i_reaction), p)
|
||||
|
||||
! Set the delayed group of the neutron
|
||||
bank_array(i) % delayed_group = p % delayed_group
|
||||
! Set delayed group on particle too
|
||||
p % delayed_group = bank_array(i) % delayed_group
|
||||
|
||||
! Increment the number of neutrons born delayed
|
||||
if (p % delayed_group > 0) then
|
||||
|
|
@ -1197,35 +1165,41 @@ contains
|
|||
end subroutine create_fission_sites
|
||||
|
||||
!===============================================================================
|
||||
! SAMPLE_FISSION_ENERGY
|
||||
! SAMPLE_FISSION_NEUTRON
|
||||
!===============================================================================
|
||||
|
||||
function sample_fission_energy(nuc, rxn, p) result(E_out)
|
||||
subroutine sample_fission_neutron(nuc, rxn, E_in, site)
|
||||
type(NuclideCE), intent(in) :: nuc
|
||||
type(Reaction), intent(in) :: rxn
|
||||
real(8), intent(in) :: E_in
|
||||
type(Bank), intent(inout) :: site
|
||||
|
||||
type(Nuclide), intent(in) :: nuc
|
||||
type(Reaction), intent(in) :: rxn
|
||||
type(Particle), intent(inout) :: p ! Particle causing fission
|
||||
real(8) :: E_out ! outgoing energy of fission neutron
|
||||
|
||||
integer :: j ! index on nu energy grid / precursor group
|
||||
integer :: lc ! index before start of energies/nu values
|
||||
integer :: NR ! number of interpolation regions
|
||||
integer :: NE ! number of energies tabulated
|
||||
integer :: n_sample ! number of times resampling
|
||||
integer :: group ! index on nu energy grid / precursor group
|
||||
integer :: n_sample ! number of resamples
|
||||
real(8) :: nu_t ! total nu
|
||||
real(8) :: nu_d ! delayed nu
|
||||
real(8) :: beta ! delayed neutron fraction
|
||||
real(8) :: xi ! random number
|
||||
real(8) :: yield ! delayed neutron precursor yield
|
||||
real(8) :: prob ! cumulative probability
|
||||
real(8) :: mu ! cosine of scattering angle
|
||||
real(8) :: phi ! azimuthal angle
|
||||
|
||||
! Determine total nu
|
||||
nu_t = nu_total(nuc, p % E)
|
||||
! Sample cosine of angle -- fission neutrons are always emitted
|
||||
! isotropically. Sometimes in ACE data, fission reactions actually have
|
||||
! an angular distribution listed, but for those that do, it's simply just
|
||||
! a uniform distribution in mu
|
||||
mu = TWO * prn() - ONE
|
||||
|
||||
! Determine delayed nu
|
||||
nu_d = nu_delayed(nuc, p % E)
|
||||
! Sample azimuthal angle uniformly in [0,2*pi)
|
||||
phi = TWO*PI*prn()
|
||||
site % uvw(1) = mu
|
||||
site % uvw(2) = sqrt(ONE - mu*mu) * cos(phi)
|
||||
site % uvw(3) = sqrt(ONE - mu*mu) * sin(phi)
|
||||
|
||||
! Determine delayed neutron fraction
|
||||
! Determine total nu, delayed nu, and delayed neutron fraction
|
||||
nu_t = nuc % nu(E_in, EMISSION_TOTAL)
|
||||
nu_d = nuc % nu(E_in, EMISSION_DELAYED)
|
||||
beta = nu_d / nu_t
|
||||
|
||||
if (prn() < beta) then
|
||||
|
|
@ -1233,51 +1207,41 @@ contains
|
|||
! DELAYED NEUTRON SAMPLED
|
||||
|
||||
! sampled delayed precursor group
|
||||
xi = prn()
|
||||
lc = 1
|
||||
xi = prn()*nu_d
|
||||
prob = ZERO
|
||||
do j = 1, nuc % n_precursor
|
||||
! determine number of interpolation regions and energies
|
||||
NR = int(nuc % nu_d_precursor_data(lc + 1))
|
||||
NE = int(nuc % nu_d_precursor_data(lc + 2 + 2*NR))
|
||||
do group = 1, nuc % n_precursor
|
||||
|
||||
! determine delayed neutron precursor yield for group j
|
||||
yield = interpolate_tab1(nuc % nu_d_precursor_data( &
|
||||
lc+1:lc+2+2*NR+2*NE), p % E)
|
||||
yield = rxn % products(1 + group) % yield % evaluate(E_in)
|
||||
|
||||
! Check if this group is sampled
|
||||
prob = prob + yield
|
||||
if (xi < prob) exit
|
||||
|
||||
! advance pointer
|
||||
lc = lc + 2 + 2*NR + 2*NE + 1
|
||||
end do
|
||||
|
||||
! if the sum of the probabilities is slightly less than one and the
|
||||
! random number is greater, j will be greater than nuc %
|
||||
! n_precursor -- check for this condition
|
||||
j = min(j, nuc % n_precursor)
|
||||
group = min(group, nuc % n_precursor)
|
||||
|
||||
! set the delayed group for the particle born from fission
|
||||
p % delayed_group = j
|
||||
site % delayed_group = group
|
||||
|
||||
! sample from energy distribution
|
||||
n_sample = 0
|
||||
do
|
||||
select type (aedist => nuc%nu_d_edist(j)%obj)
|
||||
type is (UncorrelatedAngleEnergy)
|
||||
E_out = aedist%energy%sample(p%E)
|
||||
end select
|
||||
! sample from energy/angle distribution -- note that mu has already been
|
||||
! sampled above and doesn't need to be resampled
|
||||
call rxn % products(1 + group) % sample(E_in, site % E, mu)
|
||||
|
||||
! resample if energy is greater than maximum neutron energy
|
||||
if (E_out < energy_max_neutron) exit
|
||||
if (site % E < energy_max_neutron) exit
|
||||
|
||||
! check for large number of resamples
|
||||
n_sample = n_sample + 1
|
||||
if (n_sample == MAX_SAMPLE) then
|
||||
! call write_particle_restart(p)
|
||||
call fatal_error("Resampled energy distribution maximum number of " &
|
||||
&// "times for nuclide " // nuc % name)
|
||||
// "times for nuclide " // nuc % name)
|
||||
end if
|
||||
end do
|
||||
|
||||
|
|
@ -1286,28 +1250,27 @@ contains
|
|||
! PROMPT NEUTRON SAMPLED
|
||||
|
||||
! set the delayed group for the particle born from fission to 0
|
||||
p % delayed_group = 0
|
||||
site % delayed_group = 0
|
||||
|
||||
! sample from prompt neutron energy distribution
|
||||
n_sample = 0
|
||||
do
|
||||
call rxn%secondary%sample(p%E, E_out, prob)
|
||||
call rxn % products(1) % sample(E_in, site % E, mu)
|
||||
|
||||
! resample if energy is greater than maximum neutron energy
|
||||
if (E_out < energy_max_neutron) exit
|
||||
if (site % E < energy_max_neutron) exit
|
||||
|
||||
! check for large number of resamples
|
||||
n_sample = n_sample + 1
|
||||
if (n_sample == MAX_SAMPLE) then
|
||||
! call write_particle_restart(p)
|
||||
call fatal_error("Resampled energy distribution maximum number of " &
|
||||
&// "times for nuclide " // nuc % name)
|
||||
// "times for nuclide " // nuc % name)
|
||||
end if
|
||||
end do
|
||||
|
||||
end if
|
||||
|
||||
end function sample_fission_energy
|
||||
end subroutine sample_fission_neutron
|
||||
|
||||
!===============================================================================
|
||||
! INELASTIC_SCATTER handles all reactions with a single secondary neutron (other
|
||||
|
|
@ -1315,9 +1278,9 @@ contains
|
|||
!===============================================================================
|
||||
|
||||
subroutine inelastic_scatter(nuc, rxn, p)
|
||||
type(Nuclide), intent(in) :: nuc
|
||||
type(Reaction), intent(in) :: rxn
|
||||
type(Particle), intent(inout) :: p
|
||||
type(NuclideCE), intent(in) :: nuc
|
||||
type(Reaction), intent(in) :: rxn
|
||||
type(Particle), intent(inout) :: p
|
||||
|
||||
integer :: i ! loop index
|
||||
real(8) :: E ! energy in lab (incoming/outgoing)
|
||||
|
|
@ -1331,7 +1294,7 @@ contains
|
|||
E_in = p % E
|
||||
|
||||
! sample outgoing energy and scattering cosine
|
||||
call rxn%secondary%sample(E_in, E, mu)
|
||||
call rxn % products(1) % sample(E_in, E, mu)
|
||||
|
||||
! if scattering system is in center-of-mass, transfer cosine of scattering
|
||||
! angle and outgoing energy from CM to LAB
|
||||
|
|
@ -1359,14 +1322,16 @@ contains
|
|||
! change direction of particle
|
||||
p % coord(1) % uvw = rotate_angle(p % coord(1) % uvw, mu)
|
||||
|
||||
! change weight of particle based on yield
|
||||
if (rxn % multiplicity_with_E) then
|
||||
yield = interpolate_tab1(rxn % multiplicity_E, E_in)
|
||||
p % wgt = yield * p % wgt
|
||||
else
|
||||
do i = 1, rxn % multiplicity - 1
|
||||
call p % create_secondary(p % coord(1) % uvw, NEUTRON)
|
||||
! evaluate yield
|
||||
yield = rxn % products(1) % yield % evaluate(E_in)
|
||||
if (mod(yield, ONE) == ZERO) then
|
||||
! If yield is integral, create exactly that many secondary particles
|
||||
do i = 1, nint(yield) - 1
|
||||
call p % create_secondary(p % coord(1) % uvw, NEUTRON, run_CE=.true.)
|
||||
end do
|
||||
else
|
||||
! Otherwise, change weight of particle based on yield
|
||||
p % wgt = yield * p % wgt
|
||||
end if
|
||||
|
||||
end subroutine inelastic_scatter
|
||||
|
|
|
|||
33
src/physics_common.F90
Normal file
33
src/physics_common.F90
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
module physics_common
|
||||
|
||||
use constants
|
||||
use global, only: weight_cutoff, weight_survive
|
||||
use particle_header, only: Particle
|
||||
use random_lcg, only: prn
|
||||
|
||||
implicit none
|
||||
|
||||
contains
|
||||
|
||||
!===============================================================================
|
||||
! RUSSIAN_ROULETTE
|
||||
!===============================================================================
|
||||
|
||||
subroutine russian_roulette(p)
|
||||
|
||||
type(Particle), intent(inout) :: p
|
||||
|
||||
if (p % wgt < weight_cutoff) then
|
||||
if (prn() < p % wgt / weight_survive) then
|
||||
p % wgt = weight_survive
|
||||
p % last_wgt = p % wgt
|
||||
else
|
||||
p % wgt = ZERO
|
||||
p % last_wgt = ZERO
|
||||
p % alive = .false.
|
||||
end if
|
||||
end if
|
||||
|
||||
end subroutine russian_roulette
|
||||
|
||||
end module physics_common
|
||||
271
src/physics_mg.F90
Normal file
271
src/physics_mg.F90
Normal file
|
|
@ -0,0 +1,271 @@
|
|||
module physics_mg
|
||||
! This module contains the multi-group specific physics routines so as to not
|
||||
! hinder performance of the CE versions with multiple if-thens.
|
||||
|
||||
use constants
|
||||
use error, only: fatal_error, warning
|
||||
use global
|
||||
use macroxs_header, only: MacroXS, MacroXSContainer
|
||||
use material_header, only: Material
|
||||
use math, only: rotate_angle
|
||||
use mesh, only: get_mesh_indices
|
||||
use output, only: write_message
|
||||
use particle_header, only: Particle
|
||||
use particle_restart_write, only: write_particle_restart
|
||||
use physics_common
|
||||
use random_lcg, only: prn
|
||||
use scattdata_header
|
||||
use string, only: to_str
|
||||
|
||||
implicit none
|
||||
|
||||
contains
|
||||
|
||||
!===============================================================================
|
||||
! COLLISION_MG samples a nuclide and reaction and then calls the appropriate
|
||||
! routine for that reaction
|
||||
!===============================================================================
|
||||
|
||||
subroutine collision_mg(p)
|
||||
|
||||
type(Particle), intent(inout) :: p
|
||||
|
||||
! Store pre-collision particle properties
|
||||
p % last_wgt = p % wgt
|
||||
p % last_g = p % g
|
||||
p % last_E = p % E
|
||||
p % last_uvw = p % coord(1) % uvw
|
||||
|
||||
! Add to collision counter for particle
|
||||
p % n_collision = p % n_collision + 1
|
||||
|
||||
! Sample nuclide/reaction for the material the particle is in
|
||||
call sample_reaction(p)
|
||||
|
||||
! Display information about collision
|
||||
if (verbosity >= 10 .or. trace) then
|
||||
call write_message(" " // "Energy Group = " // trim(to_str(p % g)))
|
||||
end if
|
||||
|
||||
end subroutine collision_mg
|
||||
|
||||
!===============================================================================
|
||||
! SAMPLE_REACTION samples a nuclide based on the macroscopic cross sections for
|
||||
! each nuclide within a material and then samples a reaction for that nuclide
|
||||
! and calls the appropriate routine to process the physics. Note that there is
|
||||
! special logic when suvival biasing is turned on since fission and
|
||||
! disappearance are treated implicitly.
|
||||
!===============================================================================
|
||||
|
||||
subroutine sample_reaction(p)
|
||||
|
||||
type(Particle), intent(inout) :: p
|
||||
|
||||
type(Material), pointer :: mat
|
||||
|
||||
mat => materials(p % material)
|
||||
|
||||
! Create fission bank sites. Note that while a fission reaction is sampled,
|
||||
! it never actually "happens", i.e. the weight of the particle does not
|
||||
! change when sampling fission sites. The following block handles all
|
||||
! absorption (including fission)
|
||||
|
||||
if (mat % fissionable) then
|
||||
if (run_mode == MODE_EIGENVALUE) then
|
||||
call create_fission_sites(p, fission_bank, n_bank)
|
||||
elseif (run_mode == MODE_FIXEDSOURCE) then
|
||||
call create_fission_sites(p, p % secondary_bank, p % n_secondary)
|
||||
end if
|
||||
end if
|
||||
! If survival biasing is being used, the following subroutine adjusts the
|
||||
! weight of the particle. Otherwise, it checks to see if absorption occurs
|
||||
|
||||
if (material_xs % absorption > ZERO) then
|
||||
call absorption(p)
|
||||
else
|
||||
p % absorb_wgt = ZERO
|
||||
end if
|
||||
if (.not. p % alive) return
|
||||
|
||||
! Sample a scattering reaction and determine the secondary energy of the
|
||||
! exiting neutron
|
||||
call scatter(p)
|
||||
|
||||
! Play russian roulette if survival biasing is turned on
|
||||
if (survival_biasing) then
|
||||
call russian_roulette(p)
|
||||
if (.not. p % alive) return
|
||||
end if
|
||||
|
||||
end subroutine sample_reaction
|
||||
|
||||
!===============================================================================
|
||||
! ABSORPTION
|
||||
!===============================================================================
|
||||
|
||||
subroutine absorption(p)
|
||||
|
||||
type(Particle), intent(inout) :: p
|
||||
|
||||
if (survival_biasing) then
|
||||
! Determine weight absorbed in survival biasing
|
||||
p % absorb_wgt = (p % wgt * &
|
||||
material_xs % absorption / material_xs % total)
|
||||
|
||||
! Adjust weight of particle by probability of absorption
|
||||
p % wgt = p % wgt - p % absorb_wgt
|
||||
p % last_wgt = p % wgt
|
||||
|
||||
! Score implicit absorption estimate of keff
|
||||
!$omp atomic
|
||||
global_tallies(K_ABSORPTION) % value = &
|
||||
global_tallies(K_ABSORPTION) % value + p % absorb_wgt * &
|
||||
material_xs % nu_fission / material_xs % absorption
|
||||
else
|
||||
! See if disappearance reaction happens
|
||||
if (material_xs % absorption > prn() * material_xs % total) then
|
||||
! Score absorption estimate of keff
|
||||
!$omp atomic
|
||||
global_tallies(K_ABSORPTION) % value = &
|
||||
global_tallies(K_ABSORPTION) % value + p % wgt * &
|
||||
material_xs % nu_fission / material_xs % absorption
|
||||
|
||||
p % alive = .false.
|
||||
p % event = EVENT_ABSORB
|
||||
end if
|
||||
end if
|
||||
|
||||
end subroutine absorption
|
||||
|
||||
!===============================================================================
|
||||
! SCATTER
|
||||
!===============================================================================
|
||||
|
||||
subroutine scatter(p)
|
||||
|
||||
type(Particle), intent(inout) :: p
|
||||
|
||||
call macro_xs(p % material) % obj % sample_scatter(p % coord(1) % uvw, &
|
||||
p % last_g, p % g, &
|
||||
p % mu, p % wgt)
|
||||
|
||||
! Update energy value for downstream compatability (in tallying)
|
||||
p % E = energy_bin_avg(p % g)
|
||||
|
||||
! Convert change in angle (mu) to new direction
|
||||
p % coord(1) % uvw = rotate_angle(p % coord(1) % uvw, p % mu)
|
||||
|
||||
! Set event component
|
||||
p % event = EVENT_SCATTER
|
||||
|
||||
end subroutine scatter
|
||||
|
||||
!===============================================================================
|
||||
! CREATE_FISSION_SITES determines the average total, prompt, and delayed
|
||||
! neutrons produced from fission and creates appropriate bank sites.
|
||||
!===============================================================================
|
||||
|
||||
subroutine create_fission_sites(p, bank_array, size_bank)
|
||||
type(Particle), intent(inout) :: p
|
||||
type(Bank), intent(inout) :: bank_array(:)
|
||||
integer(8), intent(inout) :: size_bank
|
||||
|
||||
integer :: i ! loop index
|
||||
integer :: nu ! actual number of neutrons produced
|
||||
integer :: ijk(3) ! indices in ufs mesh
|
||||
real(8) :: nu_t ! total nu
|
||||
real(8) :: mu ! fission neutron angular cosine
|
||||
real(8) :: phi ! fission neutron azimuthal angle
|
||||
real(8) :: weight ! weight adjustment for ufs method
|
||||
logical :: in_mesh ! source site in ufs mesh?
|
||||
class(MacroXS), pointer :: xs
|
||||
|
||||
! Get Pointers
|
||||
xs => macro_xs(p % material) % obj
|
||||
|
||||
! TODO: Heat generation from fission
|
||||
|
||||
! If uniform fission source weighting is turned on, we increase of decrease
|
||||
! the expected number of fission sites produced
|
||||
|
||||
if (ufs) then
|
||||
! Determine indices on ufs mesh for current location
|
||||
call get_mesh_indices(ufs_mesh, p % coord(1) % xyz, ijk, in_mesh)
|
||||
if (.not. in_mesh) then
|
||||
call write_particle_restart(p)
|
||||
call fatal_error("Source site outside UFS mesh!")
|
||||
end if
|
||||
|
||||
if (source_frac(1,ijk(1),ijk(2),ijk(3)) /= ZERO) then
|
||||
weight = ufs_mesh % volume_frac / source_frac(1,ijk(1),ijk(2),ijk(3))
|
||||
else
|
||||
weight = ONE
|
||||
end if
|
||||
else
|
||||
weight = ONE
|
||||
end if
|
||||
|
||||
! Determine expected number of neutrons produced
|
||||
nu_t = p % wgt / keff * weight * &
|
||||
material_xs % nu_fission / material_xs % total
|
||||
! Sample number of neutrons produced
|
||||
if (prn() > nu_t - int(nu_t)) then
|
||||
nu = int(nu_t)
|
||||
else
|
||||
nu = int(nu_t) + 1
|
||||
end if
|
||||
|
||||
! Check for bank size getting hit. For fixed source calculations, this is a
|
||||
! fatal error. For eigenvalue calculations, it just means that k-effective
|
||||
! was too high for a single batch.
|
||||
if (size_bank + nu > size(bank_array)) then
|
||||
if (run_mode == MODE_FIXEDSOURCE) then
|
||||
call fatal_error("Secondary particle bank size limit reached. If you &
|
||||
&are running a subcritical multiplication problem, k-effective &
|
||||
&may be too close to one.")
|
||||
else
|
||||
if (master) call warning("Maximum number of sites in fission bank &
|
||||
&reached. This can result in irreproducible results using different &
|
||||
&numbers of processes/threads.")
|
||||
end if
|
||||
end if
|
||||
|
||||
! Bank source neutrons
|
||||
if (nu == 0 .or. size_bank == size(bank_array)) return
|
||||
|
||||
p % fission = .true. ! Fission neutrons will be banked
|
||||
do i = int(size_bank,4) + 1, int(min(size_bank + nu, int(size(bank_array),8)),4)
|
||||
! Bank source neutrons by copying particle data
|
||||
bank_array(i) % xyz = p % coord(1) % xyz
|
||||
|
||||
! Set weight of fission bank site
|
||||
bank_array(i) % wgt = ONE/weight
|
||||
|
||||
! Sample cosine of angle -- fission neutrons are always emitted
|
||||
! isotropically. Sometimes in ACE data, fission reactions actually have
|
||||
! an angular distribution listed, but for those that do, it's simply just
|
||||
! a uniform distribution in mu
|
||||
mu = TWO * prn() - ONE
|
||||
|
||||
! Sample azimuthal angle uniformly in [0,2*pi)
|
||||
phi = TWO*PI*prn()
|
||||
bank_array(i) % uvw(1) = mu
|
||||
bank_array(i) % uvw(2) = sqrt(ONE - mu*mu) * cos(phi)
|
||||
bank_array(i) % uvw(3) = sqrt(ONE - mu*mu) * sin(phi)
|
||||
|
||||
! Sample secondary energy distribution for fission reaction and set energy
|
||||
! in fission bank
|
||||
bank_array(i) % E = &
|
||||
real(xs % sample_fission_energy(p % g, fission_bank(i) % uvw), 8)
|
||||
end do
|
||||
|
||||
! increment number of bank sites
|
||||
size_bank = min(size_bank + nu, int(size(bank_array),8))
|
||||
|
||||
! Store total weight banked for analog fission tallies
|
||||
p % n_bank = nu
|
||||
p % wgt_bank = nu/weight
|
||||
|
||||
end subroutine create_fission_sites
|
||||
|
||||
end module physics_mg
|
||||
|
|
@ -9,7 +9,7 @@ module plot
|
|||
use mesh, only: get_mesh_indices
|
||||
use mesh_header, only: RegularMesh
|
||||
use output, only: write_message
|
||||
use particle_header, only: Particle, LocalCoord
|
||||
use particle_header, only: LocalCoord, Particle
|
||||
use plot_header
|
||||
use ppmlib, only: Image, init_image, allocate_image, &
|
||||
deallocate_image, set_pixel
|
||||
|
|
@ -56,7 +56,7 @@ contains
|
|||
|
||||
subroutine position_rgb(p, pl, rgb, id)
|
||||
|
||||
type(Particle), intent(inout) :: p
|
||||
type(Particle), intent(inout) :: p
|
||||
type(ObjectPlot), pointer, intent(in) :: pl
|
||||
integer, intent(out) :: rgb(3)
|
||||
integer, intent(out) :: id
|
||||
|
|
@ -364,7 +364,7 @@ contains
|
|||
real(8) :: ll(3) ! lower left starting point for each sweep direction
|
||||
type(Particle) :: p
|
||||
type(ProgressBar) :: progress
|
||||
type(c_ptr) :: f_ptr
|
||||
type(c_ptr) :: f_ptr
|
||||
|
||||
! compute voxel widths in each direction
|
||||
vox = pl % width/dble(pl % pixels)
|
||||
|
|
|
|||
62
src/product_header.F90
Normal file
62
src/product_header.F90
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
module product_header
|
||||
|
||||
use angleenergy_header, only: AngleEnergyContainer
|
||||
use constants, only: ZERO, MAX_WORD_LEN, EMISSION_PROMPT, EMISSION_DELAYED, &
|
||||
EMISSION_TOTAL, NEUTRON, PHOTON
|
||||
use endf_header, only: Tabulated1D, Function1D, Constant1D, Polynomial
|
||||
use random_lcg, only: prn
|
||||
|
||||
!===============================================================================
|
||||
! REACTIONPRODUCT stores a data for a reaction product including its yield and
|
||||
! angle-energy distributions, each of which has a given probability of occurring
|
||||
! for a given incoming energy. In general, most products only have one
|
||||
! angle-energy distribution, but for some cases (e.g., (n,2n) in certain
|
||||
! nuclides) multiple distinct distributions exist.
|
||||
!===============================================================================
|
||||
|
||||
type :: ReactionProduct
|
||||
integer :: particle
|
||||
integer :: emission_mode ! prompt, delayed, or total emission
|
||||
real(8) :: decay_rate ! Decay rate for delayed neutron precursors
|
||||
class(Function1D), pointer :: yield => null() ! Energy-dependent neutron yield
|
||||
type(Tabulated1D), allocatable :: applicability(:)
|
||||
type(AngleEnergyContainer), allocatable :: distribution(:)
|
||||
contains
|
||||
procedure :: sample => reactionproduct_sample
|
||||
end type ReactionProduct
|
||||
|
||||
contains
|
||||
|
||||
subroutine reactionproduct_sample(this, E_in, E_out, mu)
|
||||
class(ReactionProduct), intent(in) :: this
|
||||
real(8), intent(in) :: E_in ! incoming energy
|
||||
real(8), intent(out) :: E_out ! sampled outgoing energy
|
||||
real(8), intent(out) :: mu ! sampled scattering cosine
|
||||
|
||||
integer :: i ! loop counter
|
||||
integer :: n ! number of angle-energy distributions
|
||||
real(8) :: prob ! cumulative probability
|
||||
real(8) :: c ! sampled cumulative probability
|
||||
|
||||
n = size(this%applicability)
|
||||
if (n > 1) then
|
||||
prob = ZERO
|
||||
c = prn()
|
||||
do i = 1, n
|
||||
! Determine probability that i-th energy distribution is sampled
|
||||
prob = prob + this % applicability(i) % evaluate(E_in)
|
||||
|
||||
! If i-th distribution is sampled, sample energy from the distribution
|
||||
if (c <= prob) then
|
||||
call this%distribution(i)%obj%sample(E_in, E_out, mu)
|
||||
exit
|
||||
end if
|
||||
end do
|
||||
else
|
||||
! If only one distribution is present, go ahead and sample it
|
||||
call this%distribution(1)%obj%sample(E_in, E_out, mu)
|
||||
end if
|
||||
|
||||
end subroutine reactionproduct_sample
|
||||
|
||||
end module product_header
|
||||
|
|
@ -24,9 +24,10 @@ module random_lcg
|
|||
!$omp threadprivate(prn_seed, stream)
|
||||
|
||||
public :: prn
|
||||
public :: future_prn
|
||||
public :: initialize_prng
|
||||
public :: set_particle_seed
|
||||
public :: prn_skip
|
||||
public :: advance_prn_seed
|
||||
public :: prn_set_stream
|
||||
public :: STREAM_TRACKING, STREAM_TALLIES
|
||||
|
||||
|
|
@ -52,6 +53,21 @@ contains
|
|||
|
||||
end function prn
|
||||
|
||||
!===============================================================================
|
||||
! FUTURE_PRN generates a pseudo-random number which is 'n' times ahead from the
|
||||
! current seed.
|
||||
!===============================================================================
|
||||
|
||||
function future_prn(n) result(pseudo_rn)
|
||||
|
||||
integer(8), intent(in) :: n ! number of prns to skip
|
||||
|
||||
real(8) :: pseudo_rn
|
||||
|
||||
pseudo_rn = future_seed(n, prn_seed(stream)) * prn_norm
|
||||
|
||||
end function future_prn
|
||||
|
||||
!===============================================================================
|
||||
! INITIALIZE_PRNG sets up the random number generator, determining the seed and
|
||||
! values for g, c, and m.
|
||||
|
|
@ -90,31 +106,32 @@ contains
|
|||
integer :: i
|
||||
|
||||
do i = 1, N_STREAMS
|
||||
prn_seed(i) = prn_skip_ahead(id*prn_stride, prn_seed0 + i - 1)
|
||||
prn_seed(i) = future_seed(id*prn_stride, prn_seed0 + i - 1)
|
||||
end do
|
||||
|
||||
end subroutine set_particle_seed
|
||||
|
||||
!===============================================================================
|
||||
! PRN_SKIP advances the random number seed 'n' times from the current seed
|
||||
! ADVANCE_PRN_SEED advances the random number seed 'n' times from the current
|
||||
! seed.
|
||||
!===============================================================================
|
||||
|
||||
subroutine prn_skip(n)
|
||||
subroutine advance_prn_seed(n)
|
||||
|
||||
integer(8), intent(in) :: n ! number of seeds to skip
|
||||
|
||||
prn_seed(stream) = prn_skip_ahead(n, prn_seed(stream))
|
||||
prn_seed(stream) = future_seed(n, prn_seed(stream))
|
||||
|
||||
end subroutine prn_skip
|
||||
end subroutine advance_prn_seed
|
||||
|
||||
!===============================================================================
|
||||
! PRN_SKIP_AHEAD advances the random number seed 'skip' times. This is usually
|
||||
! FUTURE_SEED advances the random number seed 'skip' times. This is usually
|
||||
! used to skip a fixed number of random numbers (the stride) so that a given
|
||||
! particle always has the same starting seed regardless of how many processors
|
||||
! are used
|
||||
!===============================================================================
|
||||
|
||||
function prn_skip_ahead(n, seed) result(new_seed)
|
||||
function future_seed(n, seed) result(new_seed)
|
||||
|
||||
integer(8), intent(in) :: n ! number of seeds to skip
|
||||
integer(8), intent(in) :: seed ! original seed
|
||||
|
|
@ -166,7 +183,7 @@ contains
|
|||
! With G and C, we can now find the new seed
|
||||
new_seed = iand(g_new*seed + c_new, prn_mask)
|
||||
|
||||
end function prn_skip_ahead
|
||||
end function future_seed
|
||||
|
||||
!===============================================================================
|
||||
! PRN_SET_STREAM changes the random number stream. If random numbers are needed
|
||||
|
|
|
|||
21
src/reaction_header.F90
Normal file
21
src/reaction_header.F90
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
module reaction_header
|
||||
|
||||
use product_header, only: ReactionProduct
|
||||
|
||||
implicit none
|
||||
|
||||
!===============================================================================
|
||||
! REACTION contains the cross-section and secondary energy and angle
|
||||
! distributions for a single reaction in a continuous-energy ACE-format table
|
||||
!===============================================================================
|
||||
|
||||
type Reaction
|
||||
integer :: MT ! ENDF MT value
|
||||
real(8) :: Q_value ! Reaction Q value
|
||||
integer :: threshold ! Energy grid index of threshold
|
||||
logical :: scatter_in_cm ! scattering system in center-of-mass?
|
||||
real(8), allocatable :: sigma(:) ! Cross section values
|
||||
type(ReactionProduct), allocatable :: products(:)
|
||||
end type Reaction
|
||||
|
||||
end module reaction_header
|
||||
|
|
@ -11,10 +11,10 @@ element materials {
|
|||
} &
|
||||
|
||||
element nuclide {
|
||||
(element name { xsd:string { maxLength = "7" } } |
|
||||
(element name { xsd:string { maxLength = "7" } } |
|
||||
attribute name { xsd:string { maxLength = "7" } }) &
|
||||
(element xs { xsd:string { maxLength = "3" } } |
|
||||
attribute xs { xsd:string { maxLength = "3" } })? &
|
||||
(element xs { xsd:string { maxLength = "5" } } |
|
||||
attribute xs { xsd:string { maxLength = "5" } })? &
|
||||
(element scattering { ( "data" | "iso-in-lab" ) } |
|
||||
attribute scattering { ( "data" | "iso-in-lab" ) })? &
|
||||
(
|
||||
|
|
@ -23,11 +23,18 @@ element materials {
|
|||
)
|
||||
}* &
|
||||
|
||||
element macroscopic {
|
||||
(element name { xsd:string } |
|
||||
attribute name { xsd:string }) &
|
||||
(element xs { xsd:string { maxLength = "5" } } |
|
||||
attribute xs { xsd:string { maxLength = "5" } })
|
||||
}* &
|
||||
|
||||
element element {
|
||||
(element name { xsd:string { maxLength = "2" } } |
|
||||
(element name { xsd:string { maxLength = "2" } } |
|
||||
attribute name { xsd:string { maxLength = "2" } }) &
|
||||
(element xs { xsd:string { maxLength = "3" } } |
|
||||
attribute xs { xsd:string { maxLength = "3" } })? &
|
||||
(element xs { xsd:string { maxLength = "5" } } |
|
||||
attribute xs { xsd:string { maxLength = "5" } })? &
|
||||
(element scattering { ( "data" | "iso-in-lab" ) } |
|
||||
attribute scattering { ( "data" | "iso-in-lab" ) })? &
|
||||
(
|
||||
|
|
@ -37,12 +44,12 @@ element materials {
|
|||
}* &
|
||||
|
||||
element sab {
|
||||
(element name { xsd:string { maxLength = "7" } } |
|
||||
(element name { xsd:string { maxLength = "7" } } |
|
||||
attribute name { xsd:string { maxLength = "7" } }) &
|
||||
(element xs { xsd:string { maxLength = "3" } } |
|
||||
attribute xs { xsd:string { maxLength = "3" } })?
|
||||
(element xs { xsd:string { maxLength = "5" } } |
|
||||
attribute xs { xsd:string { maxLength = "5" } })?
|
||||
}*
|
||||
}+ &
|
||||
|
||||
element default_xs { xsd:string { maxLength = "3" } }?
|
||||
element default_xs { xsd:string { maxLength = "5" } }?
|
||||
}
|
||||
|
|
|
|||
|
|
@ -118,6 +118,36 @@
|
|||
</interleave>
|
||||
</element>
|
||||
</zeroOrMore>
|
||||
<zeroOrMore>
|
||||
<element name="macroscopic">
|
||||
<interleave>
|
||||
<choice>
|
||||
<element name="name">
|
||||
<data type="string">
|
||||
<param name="maxLength">7</param>
|
||||
</data>
|
||||
</element>
|
||||
<attribute name="name">
|
||||
<data type="string">
|
||||
<param name="maxLength">7</param>
|
||||
</data>
|
||||
</attribute>
|
||||
</choice>
|
||||
<choice>
|
||||
<element name="xs">
|
||||
<data type="string">
|
||||
<param name="maxLength">3</param>
|
||||
</data>
|
||||
</element>
|
||||
<attribute name="xs">
|
||||
<data type="string">
|
||||
<param name="maxLength">3</param>
|
||||
</data>
|
||||
</attribute>
|
||||
</choice>
|
||||
</interleave>
|
||||
</element>
|
||||
</zeroOrMore>
|
||||
<zeroOrMore>
|
||||
<element name="element">
|
||||
<interleave>
|
||||
|
|
|
|||
61
src/relaxng/mg_cross_sections.rnc
Normal file
61
src/relaxng/mg_cross_sections.rnc
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
element cross_sections {
|
||||
|
||||
element groups { xsd:int } &
|
||||
|
||||
element group_structure { list { xsd:double+ } } &
|
||||
|
||||
element inverse_velocities { list { xsd:double+ } }? &
|
||||
|
||||
element xsdata {
|
||||
(element name { xsd:string { maxLength = "15" } } |
|
||||
attribute name { xsd:string { maxLength = "15" } }) &
|
||||
(element alias { xsd:string { maxLength = "15" } } |
|
||||
attribute alias { xsd:string { maxLength = "15" } })? &
|
||||
(element kT { xsd:double } | attribute kT { xsd:double })? &
|
||||
(element fissionable { ( "true" | "false" ) } |
|
||||
attribute fissionable { ( "true" | "false" ) }) &
|
||||
(element representation { ( "isotropic" | "angle" ) } |
|
||||
attribute representation { ( "isotropic" | "angle" ) })? &
|
||||
(element num_azimuthal { xsd:positiveInteger } |
|
||||
attribute num_azimuthal { xsd:positiveInteger })? &
|
||||
(element num_polar { xsd:positiveInteger } |
|
||||
attribute num_polar { xsd:positiveInteger })? &
|
||||
(element scatt_type { ( "legendre" | "histogram" | "tabular" ) } |
|
||||
attribute scatt_type { ( "legendre" | "histogram" | "tabular" ) })? &
|
||||
(element order { xsd:positiveInteger } |
|
||||
attribute order { xsd:positiveInteger }) &
|
||||
element tabular_legendre {
|
||||
(element enable { ( "true" | "false" ) } |
|
||||
attribute enable { ( "true" | "false" ) })? &
|
||||
(element num_points { xsd:positiveInteger } |
|
||||
attribute num_points { xsd:positiveInteger })?
|
||||
}? &
|
||||
|
||||
(element total { list { xsd:double+ } } |
|
||||
attribute total { list { xsd:double+ } })? &
|
||||
|
||||
(element absorption { list { xsd:double+ } } |
|
||||
attribute absorption { list { xsd:double+ } }) &
|
||||
|
||||
(element scatter { list { xsd:double+ } } |
|
||||
attribute scatter { list { xsd:double+ } }) &
|
||||
|
||||
(element fission { list { xsd:double+ } } |
|
||||
attribute fission { list { xsd:double+ } })? &
|
||||
|
||||
(element fission { list { xsd:double+ } } |
|
||||
attribute fission { list { xsd:double+ } })? &
|
||||
|
||||
(element k_fission { list { xsd:double+ } } |
|
||||
attribute k_fission { list { xsd:double+ } })? &
|
||||
|
||||
(element chi { list { xsd:double+ } } |
|
||||
attribute chi { list { xsd:double+ } })? &
|
||||
|
||||
(element nu_fission { list { xsd:double+ } } |
|
||||
attribute nu_fission { list { xsd:double+ } })?
|
||||
|
||||
}*
|
||||
|
||||
|
||||
}
|
||||
314
src/relaxng/mg_cross_sections.rng
Normal file
314
src/relaxng/mg_cross_sections.rng
Normal file
|
|
@ -0,0 +1,314 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<element name="cross_sections" xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
|
||||
<interleave>
|
||||
<element name="groups">
|
||||
<data type="int"/>
|
||||
</element>
|
||||
<element name="group_structure">
|
||||
<list>
|
||||
<oneOrMore>
|
||||
<data type="double"/>
|
||||
</oneOrMore>
|
||||
</list>
|
||||
</element>
|
||||
<optional>
|
||||
<element name="inverse_velocities">
|
||||
<list>
|
||||
<oneOrMore>
|
||||
<data type="double"/>
|
||||
</oneOrMore>
|
||||
</list>
|
||||
</element>
|
||||
</optional>
|
||||
<zeroOrMore>
|
||||
<element name="xsdata">
|
||||
<interleave>
|
||||
<choice>
|
||||
<element name="name">
|
||||
<data type="string">
|
||||
<param name="maxLength">15</param>
|
||||
</data>
|
||||
</element>
|
||||
<attribute name="name">
|
||||
<data type="string">
|
||||
<param name="maxLength">15</param>
|
||||
</data>
|
||||
</attribute>
|
||||
</choice>
|
||||
<optional>
|
||||
<choice>
|
||||
<element name="alias">
|
||||
<data type="string">
|
||||
<param name="maxLength">15</param>
|
||||
</data>
|
||||
</element>
|
||||
<attribute name="alias">
|
||||
<data type="string">
|
||||
<param name="maxLength">15</param>
|
||||
</data>
|
||||
</attribute>
|
||||
</choice>
|
||||
</optional>
|
||||
<optional>
|
||||
<choice>
|
||||
<element name="kT">
|
||||
<data type="double"/>
|
||||
</element>
|
||||
<attribute name="kT">
|
||||
<data type="double"/>
|
||||
</attribute>
|
||||
</choice>
|
||||
</optional>
|
||||
<choice>
|
||||
<element name="fissionable">
|
||||
<choice>
|
||||
<value>true</value>
|
||||
<value>false</value>
|
||||
</choice>
|
||||
</element>
|
||||
<attribute name="fissionable">
|
||||
<choice>
|
||||
<value>true</value>
|
||||
<value>false</value>
|
||||
</choice>
|
||||
</attribute>
|
||||
</choice>
|
||||
<optional>
|
||||
<choice>
|
||||
<element name="representation">
|
||||
<choice>
|
||||
<value>isotropic</value>
|
||||
<value>angle</value>
|
||||
</choice>
|
||||
</element>
|
||||
<attribute name="representation">
|
||||
<choice>
|
||||
<value>isotropic</value>
|
||||
<value>angle</value>
|
||||
</choice>
|
||||
</attribute>
|
||||
</choice>
|
||||
</optional>
|
||||
<optional>
|
||||
<choice>
|
||||
<element name="num_azimuthal">
|
||||
<data type="positiveInteger"/>
|
||||
</element>
|
||||
<attribute name="num_azimuthal">
|
||||
<data type="positiveInteger"/>
|
||||
</attribute>
|
||||
</choice>
|
||||
</optional>
|
||||
<optional>
|
||||
<choice>
|
||||
<element name="num_polar">
|
||||
<data type="positiveInteger"/>
|
||||
</element>
|
||||
<attribute name="num_polar">
|
||||
<data type="positiveInteger"/>
|
||||
</attribute>
|
||||
</choice>
|
||||
</optional>
|
||||
<optional>
|
||||
<choice>
|
||||
<element name="scatt_type">
|
||||
<choice>
|
||||
<value>legendre</value>
|
||||
<value>histogram</value>
|
||||
<value>tabular</value>
|
||||
</choice>
|
||||
</element>
|
||||
<attribute name="scatt_type">
|
||||
<choice>
|
||||
<value>legendre</value>
|
||||
<value>histogram</value>
|
||||
<value>tabular</value>
|
||||
</choice>
|
||||
</attribute>
|
||||
</choice>
|
||||
</optional>
|
||||
<choice>
|
||||
<element name="order">
|
||||
<data type="positiveInteger"/>
|
||||
</element>
|
||||
<attribute name="order">
|
||||
<data type="positiveInteger"/>
|
||||
</attribute>
|
||||
</choice>
|
||||
<optional>
|
||||
<element name="tabular_legendre">
|
||||
<interleave>
|
||||
<optional>
|
||||
<choice>
|
||||
<element name="enable">
|
||||
<choice>
|
||||
<value>true</value>
|
||||
<value>false</value>
|
||||
</choice>
|
||||
</element>
|
||||
<attribute name="enable">
|
||||
<choice>
|
||||
<value>true</value>
|
||||
<value>false</value>
|
||||
</choice>
|
||||
</attribute>
|
||||
</choice>
|
||||
</optional>
|
||||
<optional>
|
||||
<choice>
|
||||
<element name="num_points">
|
||||
<data type="positiveInteger"/>
|
||||
</element>
|
||||
<attribute name="num_points">
|
||||
<data type="positiveInteger"/>
|
||||
</attribute>
|
||||
</choice>
|
||||
</optional>
|
||||
</interleave>
|
||||
</element>
|
||||
</optional>
|
||||
<optional>
|
||||
<choice>
|
||||
<element name="total">
|
||||
<list>
|
||||
<oneOrMore>
|
||||
<data type="double"/>
|
||||
</oneOrMore>
|
||||
</list>
|
||||
</element>
|
||||
<attribute name="total">
|
||||
<list>
|
||||
<oneOrMore>
|
||||
<data type="double"/>
|
||||
</oneOrMore>
|
||||
</list>
|
||||
</attribute>
|
||||
</choice>
|
||||
</optional>
|
||||
<choice>
|
||||
<element name="absorption">
|
||||
<list>
|
||||
<oneOrMore>
|
||||
<data type="double"/>
|
||||
</oneOrMore>
|
||||
</list>
|
||||
</element>
|
||||
<attribute name="absorption">
|
||||
<list>
|
||||
<oneOrMore>
|
||||
<data type="double"/>
|
||||
</oneOrMore>
|
||||
</list>
|
||||
</attribute>
|
||||
</choice>
|
||||
<choice>
|
||||
<element name="scatter">
|
||||
<list>
|
||||
<oneOrMore>
|
||||
<data type="double"/>
|
||||
</oneOrMore>
|
||||
</list>
|
||||
</element>
|
||||
<attribute name="scatter">
|
||||
<list>
|
||||
<oneOrMore>
|
||||
<data type="double"/>
|
||||
</oneOrMore>
|
||||
</list>
|
||||
</attribute>
|
||||
</choice>
|
||||
<optional>
|
||||
<choice>
|
||||
<element name="fission">
|
||||
<list>
|
||||
<oneOrMore>
|
||||
<data type="double"/>
|
||||
</oneOrMore>
|
||||
</list>
|
||||
</element>
|
||||
<attribute name="fission">
|
||||
<list>
|
||||
<oneOrMore>
|
||||
<data type="double"/>
|
||||
</oneOrMore>
|
||||
</list>
|
||||
</attribute>
|
||||
</choice>
|
||||
</optional>
|
||||
<optional>
|
||||
<choice>
|
||||
<element name="fission">
|
||||
<list>
|
||||
<oneOrMore>
|
||||
<data type="double"/>
|
||||
</oneOrMore>
|
||||
</list>
|
||||
</element>
|
||||
<attribute name="fission">
|
||||
<list>
|
||||
<oneOrMore>
|
||||
<data type="double"/>
|
||||
</oneOrMore>
|
||||
</list>
|
||||
</attribute>
|
||||
</choice>
|
||||
</optional>
|
||||
<optional>
|
||||
<choice>
|
||||
<element name="k_fission">
|
||||
<list>
|
||||
<oneOrMore>
|
||||
<data type="double"/>
|
||||
</oneOrMore>
|
||||
</list>
|
||||
</element>
|
||||
<attribute name="k_fission">
|
||||
<list>
|
||||
<oneOrMore>
|
||||
<data type="double"/>
|
||||
</oneOrMore>
|
||||
</list>
|
||||
</attribute>
|
||||
</choice>
|
||||
</optional>
|
||||
<optional>
|
||||
<choice>
|
||||
<element name="chi">
|
||||
<list>
|
||||
<oneOrMore>
|
||||
<data type="double"/>
|
||||
</oneOrMore>
|
||||
</list>
|
||||
</element>
|
||||
<attribute name="chi">
|
||||
<list>
|
||||
<oneOrMore>
|
||||
<data type="double"/>
|
||||
</oneOrMore>
|
||||
</list>
|
||||
</attribute>
|
||||
</choice>
|
||||
</optional>
|
||||
<optional>
|
||||
<choice>
|
||||
<element name="nu_fission">
|
||||
<list>
|
||||
<oneOrMore>
|
||||
<data type="double"/>
|
||||
</oneOrMore>
|
||||
</list>
|
||||
</element>
|
||||
<attribute name="nu_fission">
|
||||
<list>
|
||||
<oneOrMore>
|
||||
<data type="double"/>
|
||||
</oneOrMore>
|
||||
</list>
|
||||
</attribute>
|
||||
</choice>
|
||||
</optional>
|
||||
</interleave>
|
||||
</element>
|
||||
</zeroOrMore>
|
||||
</interleave>
|
||||
</element>
|
||||
|
|
@ -36,6 +36,8 @@ element settings {
|
|||
|
||||
element energy_grid { ( "nuclide" | "log" | "logarithm" | "logarithmic" | "material-union" | "union" ) }? &
|
||||
|
||||
element energy_mode { ( "continuous-energy" | "ce" | "CE" | "multi-group" | "mg" | "MG" ) }? &
|
||||
|
||||
element entropy {
|
||||
(element dimension { list { xsd:int+ } } |
|
||||
attribute dimension { list { xsd:int+ } })? &
|
||||
|
|
@ -47,6 +49,8 @@ element settings {
|
|||
|
||||
element log_grid_bins { xsd:positiveInteger }? &
|
||||
|
||||
element max_order { xsd:nonNegativeInteger }? &
|
||||
|
||||
element natural_elements { xsd:string { maxLength = "20" } }? &
|
||||
|
||||
element no_reduce { xsd:boolean }? &
|
||||
|
|
|
|||
|
|
@ -140,6 +140,18 @@
|
|||
</choice>
|
||||
</element>
|
||||
</optional>
|
||||
<optional>
|
||||
<element name="energy_mode">
|
||||
<choice>
|
||||
<value>continuous-energy</value>
|
||||
<value>ce</value>
|
||||
<value>CE</value>
|
||||
<value>multi-group</value>
|
||||
<value>mg</value>
|
||||
<value>MG</value>
|
||||
</choice>
|
||||
</element>
|
||||
</optional>
|
||||
<optional>
|
||||
<element name="entropy">
|
||||
<interleave>
|
||||
|
|
@ -201,6 +213,11 @@
|
|||
<data type="positiveInteger"/>
|
||||
</element>
|
||||
</optional>
|
||||
<optional>
|
||||
<element name="max_order">
|
||||
<data type="nonNegativeInteger"/>
|
||||
</element>
|
||||
</optional>
|
||||
<optional>
|
||||
<element name="natural_elements">
|
||||
<data type="string">
|
||||
|
|
|
|||
150
src/sab_header.F90
Normal file
150
src/sab_header.F90
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
module sab_header
|
||||
|
||||
use, intrinsic :: ISO_FORTRAN_ENV
|
||||
|
||||
use constants
|
||||
use string, only: to_str
|
||||
|
||||
implicit none
|
||||
|
||||
!===============================================================================
|
||||
! DISTENERGYSAB contains the secondary energy/angle distributions for inelastic
|
||||
! thermal scattering collisions which utilize a continuous secondary energy
|
||||
! representation.
|
||||
!===============================================================================
|
||||
|
||||
type DistEnergySab
|
||||
integer :: n_e_out
|
||||
real(8), allocatable :: e_out(:)
|
||||
real(8), allocatable :: e_out_pdf(:)
|
||||
real(8), allocatable :: e_out_cdf(:)
|
||||
real(8), allocatable :: mu(:,:)
|
||||
end type DistEnergySab
|
||||
|
||||
!===============================================================================
|
||||
! SALPHABETA contains S(a,b) data for thermal neutron scattering, typically off
|
||||
! of light isotopes such as water, graphite, Be, etc
|
||||
!===============================================================================
|
||||
|
||||
type SAlphaBeta
|
||||
character(10) :: name ! name of table, e.g. lwtr.10t
|
||||
real(8) :: awr ! weight of nucleus in neutron masses
|
||||
real(8) :: kT ! temperature in MeV (k*T)
|
||||
integer :: n_zaid ! Number of valid zaids
|
||||
integer, allocatable :: zaid(:) ! List of valid Z and A identifiers, e.g. 6012
|
||||
|
||||
! threshold for S(a,b) treatment (usually ~4 eV)
|
||||
real(8) :: threshold_inelastic
|
||||
real(8) :: threshold_elastic = ZERO
|
||||
|
||||
! Inelastic scattering data
|
||||
integer :: n_inelastic_e_in ! # of incoming E for inelastic
|
||||
integer :: n_inelastic_e_out ! # of outgoing E for inelastic
|
||||
integer :: n_inelastic_mu ! # of outgoing angles for inelastic
|
||||
integer :: secondary_mode ! secondary mode (equal/skewed/continuous)
|
||||
real(8), allocatable :: inelastic_e_in(:)
|
||||
real(8), allocatable :: inelastic_sigma(:)
|
||||
! The following are used only if secondary_mode is 0 or 1
|
||||
real(8), allocatable :: inelastic_e_out(:,:)
|
||||
real(8), allocatable :: inelastic_mu(:,:,:)
|
||||
! The following is used only if secondary_mode is 3
|
||||
! The different implementation is necessary because the continuous
|
||||
! representation has a variable number of outgoing energy points for each
|
||||
! incoming energy
|
||||
type(DistEnergySab), allocatable :: inelastic_data(:) ! One for each Ein
|
||||
|
||||
! Elastic scattering data
|
||||
integer :: elastic_mode ! elastic mode (discrete/exact)
|
||||
integer :: n_elastic_e_in ! # of incoming E for elastic
|
||||
integer :: n_elastic_mu ! # of outgoing angles for elastic
|
||||
real(8), allocatable :: elastic_e_in(:)
|
||||
real(8), allocatable :: elastic_P(:)
|
||||
real(8), allocatable :: elastic_mu(:,:)
|
||||
contains
|
||||
procedure :: print => print_sab_table
|
||||
end type SAlphaBeta
|
||||
|
||||
contains
|
||||
|
||||
!===============================================================================
|
||||
! PRINT_SAB_TABLE displays information about a S(a,b) table containing data
|
||||
! describing thermal scattering from bound materials such as hydrogen in water.
|
||||
!===============================================================================
|
||||
|
||||
subroutine print_sab_table(this, unit)
|
||||
class(SAlphaBeta), intent(in) :: this
|
||||
integer, intent(in), optional :: unit
|
||||
|
||||
integer :: size_sab ! memory used by S(a,b) table
|
||||
integer :: unit_ ! unit to write to
|
||||
integer :: i ! Loop counter for parsing through this % zaid
|
||||
integer :: char_count ! Counter for the number of characters on a line
|
||||
|
||||
! set default unit for writing information
|
||||
if (present(unit)) then
|
||||
unit_ = unit
|
||||
else
|
||||
unit_ = OUTPUT_UNIT
|
||||
end if
|
||||
|
||||
! Basic S(a,b) table information
|
||||
write(unit_,*) 'S(a,b) Table ' // trim(this % name)
|
||||
write(unit_,'(A)',advance="no") ' zaids = '
|
||||
! Initialize the counter based on the above string
|
||||
char_count = 11
|
||||
do i = 1, this % n_zaid
|
||||
! Deal with a line thats too long
|
||||
if (char_count >= 73) then ! 73 = 80 - (5 ZAID chars + 1 space + 1 comma)
|
||||
! End the line
|
||||
write(unit_,*) ""
|
||||
! Add 11 leading blanks
|
||||
write(unit_,'(A)', advance="no") " "
|
||||
! reset the counter to 11
|
||||
char_count = 11
|
||||
end if
|
||||
if (i < this % n_zaid) then
|
||||
! Include a comma
|
||||
write(unit_,'(A)',advance="no") trim(to_str(this % zaid(i))) // ", "
|
||||
char_count = char_count + len(trim(to_str(this % zaid(i)))) + 2
|
||||
else
|
||||
! Don't include a comma, since we are all done
|
||||
write(unit_,'(A)',advance="no") trim(to_str(this % zaid(i)))
|
||||
end if
|
||||
|
||||
end do
|
||||
write(unit_,*) "" ! Move to next line
|
||||
write(unit_,*) ' awr = ' // trim(to_str(this % awr))
|
||||
write(unit_,*) ' kT = ' // trim(to_str(this % kT))
|
||||
|
||||
! Inelastic data
|
||||
write(unit_,*) ' # of Incoming Energies (Inelastic) = ' // &
|
||||
trim(to_str(this % n_inelastic_e_in))
|
||||
write(unit_,*) ' # of Outgoing Energies (Inelastic) = ' // &
|
||||
trim(to_str(this % n_inelastic_e_out))
|
||||
write(unit_,*) ' # of Outgoing Angles (Inelastic) = ' // &
|
||||
trim(to_str(this % n_inelastic_mu))
|
||||
write(unit_,*) ' Threshold for Inelastic = ' // &
|
||||
trim(to_str(this % threshold_inelastic))
|
||||
|
||||
! Elastic data
|
||||
if (this % n_elastic_e_in > 0) then
|
||||
write(unit_,*) ' # of Incoming Energies (Elastic) = ' // &
|
||||
trim(to_str(this % n_elastic_e_in))
|
||||
write(unit_,*) ' # of Outgoing Angles (Elastic) = ' // &
|
||||
trim(to_str(this % n_elastic_mu))
|
||||
write(unit_,*) ' Threshold for Elastic = ' // &
|
||||
trim(to_str(this % threshold_elastic))
|
||||
end if
|
||||
|
||||
! Determine memory used by S(a,b) table and write out
|
||||
size_sab = 8 * (this % n_inelastic_e_in * (2 + this % n_inelastic_e_out * &
|
||||
(1 + this % n_inelastic_mu)) + this % n_elastic_e_in * &
|
||||
(2 + this % n_elastic_mu))
|
||||
write(unit_,*) ' Memory Used = ' // trim(to_str(size_sab)) // ' bytes'
|
||||
|
||||
! Blank line at end
|
||||
write(unit_,*)
|
||||
|
||||
end subroutine print_sab_table
|
||||
|
||||
end module sab_header
|
||||
489
src/scattdata_header.F90
Normal file
489
src/scattdata_header.F90
Normal file
|
|
@ -0,0 +1,489 @@
|
|||
module scattdata_header
|
||||
|
||||
use constants
|
||||
use error, only: fatal_error
|
||||
use math
|
||||
use random_lcg, only: prn
|
||||
use search, only: binary_search
|
||||
|
||||
implicit none
|
||||
|
||||
!===============================================================================
|
||||
! SCATTDATA contains all the data to describe the scattering energy and
|
||||
! angular distribution
|
||||
!===============================================================================
|
||||
|
||||
type, abstract :: ScattData
|
||||
! p0 matrix on its own for sampling energy
|
||||
real(8), allocatable :: energy(:,:) ! (Gout x Gin)
|
||||
real(8), allocatable :: mult(:,:) ! (Gout x Gin)
|
||||
real(8), allocatable :: data(:,:,:) ! (Order/Nmu x Gout x Gin)
|
||||
|
||||
contains
|
||||
procedure(scattdata_init_), deferred :: init ! Initializes ScattData
|
||||
procedure(scattdata_calc_f_), deferred :: calc_f ! Calculates f, given mu
|
||||
procedure(scattdata_sample_), deferred :: sample ! sample the scatter event
|
||||
end type ScattData
|
||||
|
||||
abstract interface
|
||||
subroutine scattdata_init_(this, order, energy, mult, coeffs)
|
||||
import ScattData
|
||||
class(ScattData), intent(inout) :: this ! Object to work on
|
||||
integer, intent(in) :: order ! Data Order
|
||||
real(8), intent(in) :: energy(:,:) ! Energy Transfer Matrix
|
||||
real(8), intent(in) :: mult(:,:) ! Scatter Prod'n Matrix
|
||||
real(8), intent(in) :: coeffs(:,:,:) ! Coefficients to use
|
||||
end subroutine scattdata_init_
|
||||
|
||||
pure function scattdata_calc_f_(this, gin, gout, mu) result(f)
|
||||
import ScattData
|
||||
class(ScattData), intent(in) :: this ! The ScattData to evaluate
|
||||
integer, intent(in) :: gin ! Incoming Energy Group
|
||||
integer, intent(in) :: gout ! Outgoing Energy Group
|
||||
real(8), intent(in) :: mu ! Angle of interest
|
||||
real(8) :: f ! Return value of f(mu)
|
||||
|
||||
end function scattdata_calc_f_
|
||||
|
||||
subroutine scattdata_sample_(this, gin, gout, mu, wgt)
|
||||
import ScattData
|
||||
class(ScattData), intent(in) :: this ! Scattering Object to Use
|
||||
integer, intent(in) :: gin ! Incoming neutron group
|
||||
integer, intent(out) :: gout ! Sampled outgoin group
|
||||
real(8), intent(out) :: mu ! Sampled change in angle
|
||||
real(8), intent(inout) :: wgt ! Particle weight
|
||||
end subroutine scattdata_sample_
|
||||
end interface
|
||||
|
||||
type, extends(ScattData) :: ScattDataLegendre
|
||||
! Maximal value for rejection sampling from rectangle
|
||||
real(8), allocatable :: max_val(:,:)
|
||||
contains
|
||||
procedure :: init => scattdatalegendre_init
|
||||
procedure :: calc_f => scattdatalegendre_calc_f
|
||||
procedure :: sample => scattdatalegendre_sample
|
||||
end type ScattDataLegendre
|
||||
|
||||
type, extends(ScattData) :: ScattDataHistogram
|
||||
real(8), allocatable :: mu(:) ! Mu bins
|
||||
real(8) :: dmu ! Mu spacing
|
||||
contains
|
||||
procedure :: init => scattdatahistogram_init
|
||||
procedure :: calc_f => scattdatahistogram_calc_f
|
||||
procedure :: sample => scattdatahistogram_sample
|
||||
end type ScattDataHistogram
|
||||
|
||||
type, extends(ScattData) :: ScattDataTabular
|
||||
real(8), allocatable :: mu(:) ! Mu bins
|
||||
real(8) :: dmu ! Mu spacing
|
||||
real(8), allocatable :: fmu(:,:,:) ! PDF of f(mu)
|
||||
contains
|
||||
procedure :: init => scattdatatabular_init
|
||||
procedure :: calc_f => scattdatatabular_calc_f
|
||||
procedure :: sample => scattdatatabular_sample
|
||||
end type ScattDataTabular
|
||||
|
||||
!===============================================================================
|
||||
! SCATTDATACONTAINER allocatable array for storing ScattData Objects (for angle)
|
||||
!===============================================================================
|
||||
|
||||
type ScattDataContainer
|
||||
class(ScattData), allocatable :: obj
|
||||
end type ScattDataContainer
|
||||
|
||||
contains
|
||||
|
||||
!===============================================================================
|
||||
! SCATTDATA_INIT builds the scattdata object
|
||||
!===============================================================================
|
||||
|
||||
subroutine scattdata_init(this, order, energy, mult)
|
||||
class(ScattData), intent(inout) :: this ! Object to work on
|
||||
integer, intent(in) :: order ! Data Order
|
||||
real(8), intent(in) :: energy(:,:) ! Energy Transfer Matrix
|
||||
real(8), intent(in) :: mult(:,:) ! Scatter Prod'n Matrix
|
||||
|
||||
integer :: groups
|
||||
|
||||
groups = size(energy, dim=1)
|
||||
|
||||
allocate(this % energy(groups, groups))
|
||||
this % energy = energy
|
||||
allocate(this % mult(groups, groups))
|
||||
this % mult = mult
|
||||
allocate(this % data(order, groups, groups))
|
||||
this % data = ZERO
|
||||
|
||||
end subroutine scattdata_init
|
||||
|
||||
subroutine scattdatalegendre_init(this, order, energy, mult, coeffs)
|
||||
class(ScattDataLegendre), intent(inout) :: this ! Object to work on
|
||||
integer, intent(in) :: order ! Data Order
|
||||
real(8), intent(in) :: energy(:,:) ! Energy Transfer Matrix
|
||||
real(8), intent(in) :: mult(:,:) ! Scatter Prod'n Matrix
|
||||
real(8), intent(in) :: coeffs(:,:,:) ! Coefficients to use
|
||||
|
||||
real(8) :: dmu, mu, f
|
||||
integer :: imu, Nmu, gout, gin, groups
|
||||
|
||||
call scattdata_init(this, order, energy, mult)
|
||||
|
||||
this % data = coeffs
|
||||
|
||||
groups = size(this % energy,dim=1)
|
||||
|
||||
allocate(this % max_val(groups, groups))
|
||||
this % max_val = ZERO
|
||||
! Step through the polynomial with fixed number of points to identify
|
||||
! the maximal value.
|
||||
Nmu = 1001
|
||||
dmu = TWO / real(Nmu,8)
|
||||
do imu = 1, Nmu
|
||||
! Update mu. Do first and last seperate to avoid float errors
|
||||
if (imu == 1) then
|
||||
mu = -ONE
|
||||
else if (imu == Nmu) then
|
||||
mu = ONE
|
||||
end if
|
||||
mu = -ONE + real(imu - 1,8) * dmu
|
||||
do gin = 1, groups
|
||||
do gout = 1, groups
|
||||
! Calculate probability
|
||||
f = this % calc_f(gin,gout,mu)
|
||||
! If this is a new max, store it.
|
||||
if (f > this % max_val(gout,gin)) this % max_val(gout,gin) = f
|
||||
end do
|
||||
end do
|
||||
end do
|
||||
|
||||
! Finally, since we may not have caught the exact max, add 10% margin
|
||||
this % max_val = this % max_val * 1.1_8
|
||||
|
||||
end subroutine scattdatalegendre_init
|
||||
|
||||
subroutine scattdatahistogram_init(this, order, energy, mult, coeffs)
|
||||
class(ScattDataHistogram), intent(inout) :: this ! Object to work on
|
||||
integer, intent(in) :: order ! Data Order
|
||||
real(8), intent(in) :: energy(:,:) ! Energy Transfer Matrix
|
||||
real(8), intent(in) :: mult(:,:) ! Scatter Prod'n Matrix
|
||||
real(8), intent(in) :: coeffs(:,:,:) ! Coefficients to use
|
||||
|
||||
integer :: imu, gin, gout, groups
|
||||
real(8) :: norm
|
||||
|
||||
groups = size(energy,dim=1)
|
||||
|
||||
call scattdata_init(this, order, energy, mult)
|
||||
|
||||
allocate(this % mu(order))
|
||||
this % dmu = TWO / real(order,8)
|
||||
this % mu(1) = -ONE
|
||||
do imu = 2, order
|
||||
this % mu(imu) = -ONE + real(imu - 1,8) * this % dmu
|
||||
end do
|
||||
|
||||
! Best to integrate this histogram so we can avoid rejection sampling
|
||||
do gin = 1, groups
|
||||
do gout = 1, groups
|
||||
if (energy(gout,gin) > ZERO) then
|
||||
! Integrate the histogram
|
||||
this % data(1,gout,gin) = this % dmu * coeffs(1,gout,gin)
|
||||
do imu = 2, order
|
||||
this % data(imu,gout,gin) = this % dmu * coeffs(imu,gout,gin) + &
|
||||
this % data(imu-1,gout,gin)
|
||||
end do
|
||||
! Now make sure integral norms to zero
|
||||
norm = this % data(order,gout,gin)
|
||||
if (norm > ZERO) then
|
||||
this % data(:,gout,gin) = this % data(:,gout,gin) / norm
|
||||
end if
|
||||
end if
|
||||
end do
|
||||
end do
|
||||
|
||||
end subroutine scattdatahistogram_init
|
||||
|
||||
subroutine scattdatatabular_init(this, order, energy, mult, coeffs)
|
||||
class(ScattDataTabular), intent(inout) :: this ! Object to work on
|
||||
integer, intent(in) :: order ! Data Order
|
||||
real(8), intent(in) :: energy(:,:) ! Energy Transfer Matrix
|
||||
real(8), intent(in) :: mult(:,:) ! Scatter Prod'n Matrix
|
||||
real(8), intent(in) :: coeffs(:,:,:) ! Coefficients to use
|
||||
|
||||
integer :: imu, gin, gout, groups
|
||||
real(8) :: norm
|
||||
logical :: legendre_flag
|
||||
integer :: this_order
|
||||
|
||||
if (order < 0) then
|
||||
legendre_flag = .true.
|
||||
this_order = -1 * order
|
||||
else
|
||||
legendre_flag = .false.
|
||||
this_order = order
|
||||
end if
|
||||
|
||||
groups = size(energy,dim=1)
|
||||
|
||||
call scattdata_init(this, this_order, energy, mult)
|
||||
|
||||
allocate(this % mu(this_order))
|
||||
this % dmu = TWO / real(this_order - 1)
|
||||
do imu = 1, this_order - 1
|
||||
this % mu(imu) = -ONE + real(imu - 1) * this % dmu
|
||||
end do
|
||||
this % mu(this_order) = ONE
|
||||
|
||||
! Best to integrate this histogram so we can avoid rejection sampling
|
||||
allocate(this % fmu(this_order,groups,groups))
|
||||
do gin = 1, groups
|
||||
do gout = 1, groups
|
||||
if (energy(gout,gin) > ZERO) then
|
||||
if (legendre_flag) then
|
||||
! Coeffs are legendre coeffs. Need to build f(mu) then integrate
|
||||
! and store the integral in this % data
|
||||
! Ensure the coeffs are normalized
|
||||
norm = ONE / coeffs(1,gout,gin)
|
||||
do imu = 1, this_order
|
||||
this % fmu(imu,gout,gin) = evaluate_legendre(norm * coeffs(:,gout,gin), this % mu(imu))
|
||||
! Force positivity
|
||||
if (this % fmu(imu,gout,gin) < ZERO) then
|
||||
this % fmu(imu,gout,gin) = ZERO
|
||||
end if
|
||||
end do
|
||||
else
|
||||
! Coeffs contain f(mu), put in f(mu) to save duplicate.
|
||||
this % fmu(:,gout,gin) = this % data(:,gout,gin)
|
||||
end if
|
||||
|
||||
! Re-normalize fmu for numerical integration issues and in case
|
||||
! the negative fix-up introduced un-normalized data
|
||||
norm = ZERO
|
||||
do imu = 2, this_order
|
||||
norm = norm + HALF * this % dmu * (this % fmu(imu-1,gout,gin) + this % fmu(imu,gout,gin))
|
||||
end do
|
||||
if (norm > ZERO) then
|
||||
this % fmu(:,gout,gin) = this % fmu(:,gout,gin) / norm
|
||||
end if
|
||||
|
||||
! Now create CDF from fmu with trapezoidal rule
|
||||
this % data(1,gout,gin) = ZERO
|
||||
do imu = 2, this_order - 1
|
||||
this % data(imu,gout,gin) = this % data(imu-1,gout,gin) + &
|
||||
HALF * this % dmu * (this % fmu(imu-1,gout,gin) + this % fmu(imu,gout,gin))
|
||||
end do
|
||||
this % data(this_order,gout,gin) = ONE
|
||||
end if
|
||||
end do
|
||||
end do
|
||||
|
||||
end subroutine scattdatatabular_init
|
||||
|
||||
!===============================================================================
|
||||
! SCATTDATA_*_CALC_F Calculates the value of f given mu (and gin,gout pair)
|
||||
!===============================================================================
|
||||
|
||||
pure function scattdatalegendre_calc_f(this, gin, gout, mu) result(f)
|
||||
class(ScattDataLegendre), intent(in) :: this ! The ScattData to evaluate
|
||||
integer, intent(in) :: gin ! Incoming Energy Group
|
||||
integer, intent(in) :: gout ! Outgoing Energy Group
|
||||
real(8), intent(in) :: mu ! Angle of interest
|
||||
real(8) :: f ! Return value of f(mu)
|
||||
|
||||
! Plug mu in to the legendre expansion and go from there
|
||||
f = evaluate_legendre(this % data(:, gout, gin), mu)
|
||||
|
||||
end function scattdatalegendre_calc_f
|
||||
|
||||
pure function scattdatahistogram_calc_f(this, gin, gout, mu) result(f)
|
||||
class(ScattDataHistogram), intent(in) :: this ! The ScattData to evaluate
|
||||
integer, intent(in) :: gin ! Incoming Energy Group
|
||||
integer, intent(in) :: gout ! Outgoing Energy Group
|
||||
real(8), intent(in) :: mu ! Angle of interest
|
||||
real(8) :: f ! Return value of f(mu)
|
||||
|
||||
integer :: imu
|
||||
|
||||
! Find mu bin
|
||||
imu = floor((mu + ONE)/ this % dmu + ONE)
|
||||
! Adjust so interpolation works on the last bin if necessary
|
||||
if (imu == size(this % data, dim=1)) then
|
||||
imu = imu - 1
|
||||
end if
|
||||
|
||||
! Use histogram interpolation to find f(mu)
|
||||
f = this % data(imu, gout, gin)
|
||||
|
||||
end function scattdatahistogram_calc_f
|
||||
|
||||
pure function scattdatatabular_calc_f(this, gin, gout, mu) result(f)
|
||||
class(ScattDataTabular), intent(in) :: this ! The ScattData to evaluate
|
||||
integer, intent(in) :: gin ! Incoming Energy Group
|
||||
integer, intent(in) :: gout ! Outgoing Energy Group
|
||||
real(8), intent(in) :: mu ! Angle of interest
|
||||
real(8) :: f ! Return value of f(mu)
|
||||
|
||||
integer :: imu
|
||||
real(8) :: r
|
||||
|
||||
! Find mu bin
|
||||
imu = floor((mu + ONE)/ this % dmu + ONE)
|
||||
! Adjust so interpolation works on the last bin if necessary
|
||||
if (imu == size(this % data, dim=1)) then
|
||||
imu = imu - 1
|
||||
end if
|
||||
|
||||
! ! Now interpolate to find f(mu)
|
||||
r = (mu - this % mu(imu)) / (this % mu(imu + 1) - this % mu(imu))
|
||||
f = (ONE - r) * this % data(imu, gout, gin) + &
|
||||
r * this % data(imu + 1, gout, gin)
|
||||
|
||||
end function scattdatatabular_calc_f
|
||||
|
||||
!===============================================================================
|
||||
! SCATTDATA*_SCATTER Samples the outgoing energy and change in angle.
|
||||
!===============================================================================
|
||||
|
||||
subroutine scattdatalegendre_sample(this, gin, gout, mu, wgt)
|
||||
class(ScattDataLegendre), intent(in) :: this ! Scattering object to use
|
||||
integer, intent(in) :: gin ! Incoming neutron group
|
||||
integer, intent(out) :: gout ! Sampled outgoin group
|
||||
real(8), intent(out) :: mu ! Sampled change in angle
|
||||
real(8), intent(inout) :: wgt ! Particle weight
|
||||
|
||||
real(8) :: xi ! Our random number
|
||||
real(8) :: prob ! Running probability
|
||||
real(8) :: u, f, M
|
||||
integer :: samples
|
||||
|
||||
xi = prn()
|
||||
prob = ZERO
|
||||
gout = 0
|
||||
|
||||
do while (prob < xi)
|
||||
gout = gout + 1
|
||||
prob = prob + this % energy(gout,gin)
|
||||
end do
|
||||
|
||||
! Now we can sample mu using the legendre representation of the thisering
|
||||
! kernel in data(1:this % order)
|
||||
|
||||
! Do with rejection sampling
|
||||
! Set maximal value
|
||||
M = this % max_val(gout,gin)
|
||||
samples = 0
|
||||
do
|
||||
mu = TWO * prn() - ONE
|
||||
f = this % calc_f(gin,gout,mu)
|
||||
if (f > ZERO) then
|
||||
u = prn() * M
|
||||
if (u <= f) then
|
||||
exit
|
||||
end if
|
||||
end if
|
||||
samples = samples + 1
|
||||
if (samples > MAX_SAMPLE) then
|
||||
call fatal_error("Maximum number of Legendre expansion samples reached!")
|
||||
end if
|
||||
end do
|
||||
|
||||
wgt = wgt * this % mult(gout,gin)
|
||||
|
||||
end subroutine scattdatalegendre_sample
|
||||
|
||||
subroutine scattdatahistogram_sample(this, gin, gout, mu, wgt)
|
||||
class(ScattDataHistogram), intent(in) :: this ! Scattering object to use
|
||||
integer, intent(in) :: gin ! Incoming neutron group
|
||||
integer, intent(out) :: gout ! Sampled outgoin group
|
||||
real(8), intent(out) :: mu ! Sampled change in angle
|
||||
real(8), intent(inout) :: wgt ! Particle weight
|
||||
|
||||
real(8) :: xi ! Our random number
|
||||
real(8) :: prob ! Running probability
|
||||
integer :: imu
|
||||
|
||||
xi = prn()
|
||||
prob = ZERO
|
||||
gout = 0
|
||||
|
||||
do while (prob < xi)
|
||||
gout = gout + 1
|
||||
prob = prob + this % energy(gout,gin)
|
||||
end do
|
||||
|
||||
xi = prn()
|
||||
if (xi < this % data(1,gout,gin)) then
|
||||
imu = 1
|
||||
else
|
||||
imu = binary_search(this % data(:,gout,gin), &
|
||||
size(this % data(:,gout,gin)), xi)
|
||||
end if
|
||||
|
||||
! Randomly select a mu in this bin.
|
||||
mu = prn() * this % dmu + this % mu(imu)
|
||||
|
||||
wgt = wgt * this % mult(gout,gin)
|
||||
|
||||
end subroutine scattdatahistogram_sample
|
||||
|
||||
subroutine scattdatatabular_sample(this, gin, gout, mu, wgt)
|
||||
class(ScattDataTabular), intent(in) :: this ! Scattering object to use
|
||||
integer, intent(in) :: gin ! Incoming neutron group
|
||||
integer, intent(out) :: gout ! Sampled outgoin group
|
||||
real(8), intent(out) :: mu ! Sampled change in angle
|
||||
real(8), intent(inout) :: wgt ! Particle weight
|
||||
|
||||
real(8) :: xi ! Our random number
|
||||
real(8) :: prob ! Running probability
|
||||
real(8) :: mu0, frac, mu1
|
||||
real(8) :: c_k, c_k1, p0, p1
|
||||
integer :: k, NP
|
||||
|
||||
xi = prn()
|
||||
prob = ZERO
|
||||
gout = 0
|
||||
|
||||
do while (prob < xi)
|
||||
gout = gout + 1
|
||||
prob = prob + this % energy(gout,gin)
|
||||
end do
|
||||
|
||||
! determine outgoing cosine bin
|
||||
NP = size(this % data(:,gout,gin))
|
||||
xi = prn()
|
||||
|
||||
c_k = this % data(1,gout,gin)
|
||||
do k = 1, NP - 1
|
||||
c_k1 = this % data(k+1,gout,gin)
|
||||
if (xi < c_k1) exit
|
||||
c_k = c_k1
|
||||
end do
|
||||
|
||||
! check to make sure k is <= NP - 1
|
||||
k = min(k, NP - 1)
|
||||
|
||||
p0 = this % fmu(k,gout,gin)
|
||||
mu0 = this % mu(k)
|
||||
! Linear-linear interpolation to find mu value w/in bin.
|
||||
p1 = this % fmu(k+1,gout,gin)
|
||||
mu1 = this % mu(k+1)
|
||||
|
||||
frac = (p1 - p0)/(mu1 - mu0)
|
||||
|
||||
if (frac == ZERO) then
|
||||
mu = mu0 + (xi - c_k)/p0
|
||||
else
|
||||
mu = mu0 + (sqrt(max(ZERO, p0*p0 + TWO*frac*(xi - c_k))) - p0)/frac
|
||||
end if
|
||||
|
||||
if (mu <= -ONE) then
|
||||
mu = -ONE
|
||||
else if (mu >= ONE) then
|
||||
mu = ONE
|
||||
end if
|
||||
|
||||
wgt = wgt * this % mult(gout,gin)
|
||||
|
||||
end subroutine scattdatatabular_sample
|
||||
|
||||
end module scattdata_header
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
module secondary_correlated
|
||||
|
||||
use angleenergy_header, only: AngleEnergy
|
||||
use constants, only: ZERO, ONE, TWO, HISTOGRAM, LINEAR_LINEAR
|
||||
use distribution_univariate, only: DistributionContainer
|
||||
use secondary_header, only: AngleEnergy
|
||||
use random_lcg, only: prn
|
||||
use search, only: binary_search
|
||||
|
||||
|
|
@ -24,8 +24,8 @@ module secondary_correlated
|
|||
integer :: n_region ! number of interpolation regions
|
||||
integer, allocatable :: breakpoints(:) ! breakpoints of interpolation regions
|
||||
integer, allocatable :: interpolation(:) ! interpolation region codes
|
||||
real(8), allocatable :: energy_in(:) ! incoming energies
|
||||
type(AngleEnergyTable), allocatable :: table(:) ! outgoing E/mu distributions
|
||||
real(8), allocatable :: energy(:) ! incoming energies
|
||||
type(AngleEnergyTable), allocatable :: distribution(:) ! outgoing E/mu distributions
|
||||
contains
|
||||
procedure :: sample => correlated_sample
|
||||
end type CorrelatedAngleEnergy
|
||||
|
|
@ -61,17 +61,17 @@ contains
|
|||
|
||||
! find energy bin and calculate interpolation factor -- if the energy is
|
||||
! outside the range of the tabulated energies, choose the first or last bins
|
||||
n_energy_in = size(this%energy_in)
|
||||
if (E_in < this%energy_in(1)) then
|
||||
n_energy_in = size(this%energy)
|
||||
if (E_in < this%energy(1)) then
|
||||
i = 1
|
||||
r = ZERO
|
||||
elseif (E_in > this%energy_in(n_energy_in)) then
|
||||
elseif (E_in > this%energy(n_energy_in)) then
|
||||
i = n_energy_in - 1
|
||||
r = ONE
|
||||
else
|
||||
i = binary_search(this%energy_in, n_energy_in, E_in)
|
||||
r = (E_in - this%energy_in(i)) / &
|
||||
(this%energy_in(i+1) - this%energy_in(i))
|
||||
i = binary_search(this%energy, n_energy_in, E_in)
|
||||
r = (E_in - this%energy(i)) / &
|
||||
(this%energy(i+1) - this%energy(i))
|
||||
end if
|
||||
|
||||
! Sample between the ith and (i+1)th bin
|
||||
|
|
@ -82,23 +82,23 @@ contains
|
|||
end if
|
||||
|
||||
! interpolation for energy E1 and EK
|
||||
n_energy_out = size(this%table(i)%e_out)
|
||||
E_i_1 = this%table(i)%e_out(1)
|
||||
E_i_K = this%table(i)%e_out(n_energy_out)
|
||||
n_energy_out = size(this%distribution(i)%e_out)
|
||||
E_i_1 = this%distribution(i)%e_out(1)
|
||||
E_i_K = this%distribution(i)%e_out(n_energy_out)
|
||||
|
||||
n_energy_out = size(this%table(i+1)%e_out)
|
||||
E_i1_1 = this%table(i+1)%e_out(1)
|
||||
E_i1_K = this%table(i+1)%e_out(n_energy_out)
|
||||
n_energy_out = size(this%distribution(i+1)%e_out)
|
||||
E_i1_1 = this%distribution(i+1)%e_out(1)
|
||||
E_i1_K = this%distribution(i+1)%e_out(n_energy_out)
|
||||
|
||||
E_1 = E_i_1 + r*(E_i1_1 - E_i_1)
|
||||
E_K = E_i_K + r*(E_i1_K - E_i_K)
|
||||
|
||||
! determine outgoing energy bin
|
||||
n_energy_out = size(this%table(l)%e_out)
|
||||
n_energy_out = size(this%distribution(l)%e_out)
|
||||
r1 = prn()
|
||||
c_k = this%table(l)%c(1)
|
||||
c_k = this%distribution(l)%c(1)
|
||||
do k = 1, n_energy_out - 1
|
||||
c_k1 = this%table(l)%c(k+1)
|
||||
c_k1 = this%distribution(l)%c(k+1)
|
||||
if (r1 < c_k1) exit
|
||||
c_k = c_k1
|
||||
end do
|
||||
|
|
@ -106,9 +106,9 @@ contains
|
|||
! check to make sure k is <= NP - 1
|
||||
k = min(k, n_energy_out - 1)
|
||||
|
||||
E_l_k = this%table(l)%e_out(k)
|
||||
p_l_k = this%table(l)%p(k)
|
||||
if (this%table(l)%interpolation == HISTOGRAM) then
|
||||
E_l_k = this%distribution(l)%e_out(k)
|
||||
p_l_k = this%distribution(l)%p(k)
|
||||
if (this%distribution(l)%interpolation == HISTOGRAM) then
|
||||
! Histogram interpolation
|
||||
if (p_l_k > ZERO) then
|
||||
E_out = E_l_k + (r1 - c_k)/p_l_k
|
||||
|
|
@ -116,10 +116,10 @@ contains
|
|||
E_out = E_l_k
|
||||
end if
|
||||
|
||||
elseif (this%table(l)%interpolation == LINEAR_LINEAR) then
|
||||
elseif (this%distribution(l)%interpolation == LINEAR_LINEAR) then
|
||||
! Linear-linear interpolation
|
||||
E_l_k1 = this%table(l)%e_out(k+1)
|
||||
p_l_k1 = this%table(l)%p(k+1)
|
||||
E_l_k1 = this%distribution(l)%e_out(k+1)
|
||||
p_l_k1 = this%distribution(l)%p(k+1)
|
||||
|
||||
frac = (p_l_k1 - p_l_k)/(E_l_k1 - E_l_k)
|
||||
if (frac == ZERO) then
|
||||
|
|
@ -139,9 +139,9 @@ contains
|
|||
|
||||
! Find correlated angular distribution for closest outgoing energy bin
|
||||
if (r1 - c_k < c_k1 - r1) then
|
||||
mu = this%table(l)%angle(k)%obj%sample()
|
||||
mu = this%distribution(l)%angle(k)%obj%sample()
|
||||
else
|
||||
mu = this%table(l)%angle(k + 1)%obj%sample()
|
||||
mu = this%distribution(l)%angle(k + 1)%obj%sample()
|
||||
end if
|
||||
end subroutine correlated_sample
|
||||
|
||||
|
|
|
|||
|
|
@ -1,78 +0,0 @@
|
|||
module secondary_header
|
||||
|
||||
use endf_header, only: Tab1
|
||||
use interpolation, only: interpolate_tab1
|
||||
use random_lcg, only: prn
|
||||
|
||||
!===============================================================================
|
||||
! ANGLEENERGY (abstract) defines a correlated or uncorrelated angle-energy
|
||||
! distribution that is a function of incoming energy. Each derived type must
|
||||
! implement a sample() subroutine that returns an outgoing energy and scattering
|
||||
! cosine given an incoming energy.
|
||||
!===============================================================================
|
||||
|
||||
type, abstract :: AngleEnergy
|
||||
contains
|
||||
procedure(iSampleAngleEnergy), deferred :: sample
|
||||
end type AngleEnergy
|
||||
|
||||
abstract interface
|
||||
subroutine iSampleAngleEnergy(this, E_in, E_out, mu)
|
||||
import AngleEnergy
|
||||
class(AngleEnergy), intent(in) :: this
|
||||
real(8), intent(in) :: E_in
|
||||
real(8), intent(out) :: E_out
|
||||
real(8), intent(out) :: mu
|
||||
end subroutine iSampleAngleEnergy
|
||||
end interface
|
||||
|
||||
type :: AngleEnergyContainer
|
||||
class(AngleEnergy), allocatable :: obj
|
||||
end type AngleEnergyContainer
|
||||
|
||||
!===============================================================================
|
||||
! SECONDARYDISTRIBUTION stores multiple angle-energy distributions, each of
|
||||
! which has a given probability of occurring for a given incoming energy. In
|
||||
! general, most secondary distributions only have one angle-energy distribution,
|
||||
! but for some cases (e.g., (n,2n) in certain nuclides) multiple distinct
|
||||
! distributions exist.
|
||||
!===============================================================================
|
||||
|
||||
type :: SecondaryDistribution
|
||||
type(Tab1), allocatable :: applicability(:)
|
||||
type(AngleEnergyContainer), allocatable :: distribution(:)
|
||||
contains
|
||||
procedure :: sample => secondary_sample
|
||||
end type SecondaryDistribution
|
||||
|
||||
contains
|
||||
|
||||
subroutine secondary_sample(this, E_in, E_out, mu)
|
||||
class(SecondaryDistribution), intent(in) :: this
|
||||
real(8), intent(in) :: E_in ! incoming energy
|
||||
real(8), intent(out) :: E_out ! sampled outgoing energy
|
||||
real(8), intent(out) :: mu ! sampled scattering cosine
|
||||
|
||||
integer :: n ! number of angle-energy distributions
|
||||
real(8) :: p_valid ! probability that given distribution is valid
|
||||
|
||||
n = size(this%applicability)
|
||||
if (n > 1) then
|
||||
do i = 1, n
|
||||
! Determine probability that i-th energy distribution is sampled
|
||||
p_valid = interpolate_tab1(this%applicability(i), E_in)
|
||||
|
||||
! If i-th distribution is sampled, sample energy from the distribution
|
||||
if (prn() <= p_valid) then
|
||||
call this%distribution(i)%obj%sample(E_in, E_out, mu)
|
||||
exit
|
||||
end if
|
||||
end do
|
||||
else
|
||||
! If only one distribution is present, go ahead and sample it
|
||||
call this%distribution(1)%obj%sample(E_in, E_out, mu)
|
||||
end if
|
||||
|
||||
end subroutine secondary_sample
|
||||
|
||||
end module secondary_header
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
module secondary_kalbach
|
||||
|
||||
use angleenergy_header, only: AngleEnergy
|
||||
use constants, only: ZERO, ONE, TWO, HISTOGRAM, LINEAR_LINEAR
|
||||
use secondary_header, only: AngleEnergy
|
||||
use random_lcg, only: prn
|
||||
use search, only: binary_search
|
||||
|
||||
|
|
@ -25,8 +25,8 @@ module secondary_kalbach
|
|||
integer :: n_region ! number of interpolation regions
|
||||
integer, allocatable :: breakpoints(:) ! breakpoints of interpolation regions
|
||||
integer, allocatable :: interpolation(:) ! interpolation region codes
|
||||
real(8), allocatable :: energy_in(:) ! incoming energies
|
||||
type(KalbachMannTable), allocatable :: table(:) ! outgoing E/mu parameters
|
||||
real(8), allocatable :: energy(:) ! incoming energies
|
||||
type(KalbachMannTable), allocatable :: distribution(:) ! outgoing E/mu parameters
|
||||
contains
|
||||
procedure :: sample => kalbachmann_sample
|
||||
end type KalbachMann
|
||||
|
|
@ -64,17 +64,17 @@ contains
|
|||
|
||||
! find energy bin and calculate interpolation factor -- if the energy is
|
||||
! outside the range of the tabulated energies, choose the first or last bins
|
||||
n_energy_in = size(this%energy_in)
|
||||
if (E_in < this%energy_in(1)) then
|
||||
n_energy_in = size(this%energy)
|
||||
if (E_in < this%energy(1)) then
|
||||
i = 1
|
||||
r = ZERO
|
||||
elseif (E_in > this%energy_in(n_energy_in)) then
|
||||
elseif (E_in > this%energy(n_energy_in)) then
|
||||
i = n_energy_in - 1
|
||||
r = ONE
|
||||
else
|
||||
i = binary_search(this%energy_in, n_energy_in, E_in)
|
||||
r = (E_in - this%energy_in(i)) / &
|
||||
(this%energy_in(i+1) - this%energy_in(i))
|
||||
i = binary_search(this%energy, n_energy_in, E_in)
|
||||
r = (E_in - this%energy(i)) / &
|
||||
(this%energy(i+1) - this%energy(i))
|
||||
end if
|
||||
|
||||
! Sample between the ith and (i+1)th bin
|
||||
|
|
@ -85,23 +85,23 @@ contains
|
|||
end if
|
||||
|
||||
! interpolation for energy E1 and EK
|
||||
n_energy_out = size(this%table(i)%e_out)
|
||||
E_i_1 = this%table(i)%e_out(1)
|
||||
E_i_K = this%table(i)%e_out(n_energy_out)
|
||||
n_energy_out = size(this%distribution(i)%e_out)
|
||||
E_i_1 = this%distribution(i)%e_out(1)
|
||||
E_i_K = this%distribution(i)%e_out(n_energy_out)
|
||||
|
||||
n_energy_out = size(this%table(i+1)%e_out)
|
||||
E_i1_1 = this%table(i+1)%e_out(1)
|
||||
E_i1_K = this%table(i+1)%e_out(n_energy_out)
|
||||
n_energy_out = size(this%distribution(i+1)%e_out)
|
||||
E_i1_1 = this%distribution(i+1)%e_out(1)
|
||||
E_i1_K = this%distribution(i+1)%e_out(n_energy_out)
|
||||
|
||||
E_1 = E_i_1 + r*(E_i1_1 - E_i_1)
|
||||
E_K = E_i_K + r*(E_i1_K - E_i_K)
|
||||
|
||||
! determine outgoing energy bin
|
||||
n_energy_out = size(this%table(l)%e_out)
|
||||
n_energy_out = size(this%distribution(l)%e_out)
|
||||
r1 = prn()
|
||||
c_k = this%table(l)%c(1)
|
||||
c_k = this%distribution(l)%c(1)
|
||||
do k = 1, n_energy_out - 1
|
||||
c_k1 = this%table(l)%c(k+1)
|
||||
c_k1 = this%distribution(l)%c(k+1)
|
||||
if (r1 < c_k1) exit
|
||||
c_k = c_k1
|
||||
end do
|
||||
|
|
@ -109,9 +109,9 @@ contains
|
|||
! check to make sure k is <= NP - 1
|
||||
k = min(k, n_energy_out - 1)
|
||||
|
||||
E_l_k = this%table(l)%e_out(k)
|
||||
p_l_k = this%table(l)%p(k)
|
||||
if (this%table(l)%interpolation == HISTOGRAM) then
|
||||
E_l_k = this%distribution(l)%e_out(k)
|
||||
p_l_k = this%distribution(l)%p(k)
|
||||
if (this%distribution(l)%interpolation == HISTOGRAM) then
|
||||
! Histogram interpolation
|
||||
if (p_l_k > ZERO) then
|
||||
E_out = E_l_k + (r1 - c_k)/p_l_k
|
||||
|
|
@ -120,13 +120,13 @@ contains
|
|||
end if
|
||||
|
||||
! Determine Kalbach-Mann parameters
|
||||
km_r = this%table(l)%r(k)
|
||||
km_a = this%table(l)%a(k)
|
||||
km_r = this%distribution(l)%r(k)
|
||||
km_a = this%distribution(l)%a(k)
|
||||
|
||||
elseif (this%table(l)%interpolation == LINEAR_LINEAR) then
|
||||
elseif (this%distribution(l)%interpolation == LINEAR_LINEAR) then
|
||||
! Linear-linear interpolation
|
||||
E_l_k1 = this%table(l)%e_out(k+1)
|
||||
p_l_k1 = this%table(l)%p(k+1)
|
||||
E_l_k1 = this%distribution(l)%e_out(k+1)
|
||||
p_l_k1 = this%distribution(l)%p(k+1)
|
||||
|
||||
frac = (p_l_k1 - p_l_k)/(E_l_k1 - E_l_k)
|
||||
if (frac == ZERO) then
|
||||
|
|
@ -137,10 +137,10 @@ contains
|
|||
end if
|
||||
|
||||
! Determine Kalbach-Mann parameters
|
||||
km_r = this%table(l)%r(k) + (E_out - E_l_k)/(E_l_k1 - E_l_k) * &
|
||||
(this%table(l)%r(k+1) - this%table(l)%r(k))
|
||||
km_a = this%table(l)%a(k) + (E_out - E_l_k)/(E_l_k1 - E_l_k) * &
|
||||
(this%table(l)%a(k+1) - this%table(l)%a(k))
|
||||
km_r = this%distribution(l)%r(k) + (E_out - E_l_k)/(E_l_k1 - E_l_k) * &
|
||||
(this%distribution(l)%r(k+1) - this%distribution(l)%r(k))
|
||||
km_a = this%distribution(l)%a(k) + (E_out - E_l_k)/(E_l_k1 - E_l_k) * &
|
||||
(this%distribution(l)%a(k+1) - this%distribution(l)%a(k))
|
||||
end if
|
||||
|
||||
! Now interpolate between incident energy bins i and i + 1
|
||||
|
|
|
|||
70
src/secondary_nbody.F90
Normal file
70
src/secondary_nbody.F90
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
module secondary_nbody
|
||||
|
||||
use angleenergy_header, only: AngleEnergy
|
||||
use constants, only: ONE, TWO, PI
|
||||
use math, only: maxwell_spectrum
|
||||
use random_lcg, only: prn
|
||||
|
||||
!===============================================================================
|
||||
! NBODYPHASESPACE gives the energy distribution for particles emitted from
|
||||
! neutron and charged-particle reactions. This corresponds to ACE law 66 and
|
||||
! ENDF File 6, LAW=6.
|
||||
!===============================================================================
|
||||
|
||||
type, extends(AngleEnergy) :: NBodyPhaseSpace
|
||||
integer :: n_bodies
|
||||
real(8) :: mass_ratio
|
||||
real(8) :: A
|
||||
real(8) :: Q
|
||||
contains
|
||||
procedure :: sample => nbody_sample
|
||||
end type NBodyPhaseSpace
|
||||
|
||||
contains
|
||||
|
||||
subroutine nbody_sample(this, E_in, E_out, mu)
|
||||
class(NBodyPhaseSpace), intent(in) :: this
|
||||
real(8), intent(in) :: E_in ! incoming energy
|
||||
real(8), intent(out) :: E_out ! sampled outgoing energy
|
||||
real(8), intent(out) :: mu ! sampled outgoing energy
|
||||
|
||||
real(8) :: Ap ! total mass of particles in neutron masses
|
||||
real(8) :: E_max ! maximum possible COM energy
|
||||
real(8) :: x, y, v
|
||||
real(8) :: r1, r2, r3, r4, r5, r6
|
||||
|
||||
! By definition, the distribution of the angle is isotropic for an N-body
|
||||
! phase space distribution
|
||||
mu = TWO*prn() - ONE
|
||||
|
||||
! Determine E_max parameter
|
||||
Ap = this%mass_ratio
|
||||
E_max = (Ap - ONE)/Ap * (this%A/(this%A + ONE)*E_in + this%Q)
|
||||
|
||||
! x is essentially a Maxwellian distribution
|
||||
x = maxwell_spectrum(ONE)
|
||||
|
||||
select case (this%n_bodies)
|
||||
case (3)
|
||||
y = maxwell_spectrum(ONE)
|
||||
case (4)
|
||||
r1 = prn()
|
||||
r2 = prn()
|
||||
r3 = prn()
|
||||
y = -log(r1*r2*r3)
|
||||
case (5)
|
||||
r1 = prn()
|
||||
r2 = prn()
|
||||
r3 = prn()
|
||||
r4 = prn()
|
||||
r5 = prn()
|
||||
r6 = prn()
|
||||
y = -log(r1*r2*r3*r4) - log(r5) * cos(PI/TWO*r6)**2
|
||||
end select
|
||||
|
||||
! Now determine v and E_out
|
||||
v = x/(x+y)
|
||||
E_out = E_max * v
|
||||
end subroutine nbody_sample
|
||||
|
||||
end module secondary_nbody
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue