From a8171cbd4e9a1ee801a52f6f0c1426982b73774f Mon Sep 17 00:00:00 2001 From: Ethan Krammer <68677905+ethankrammer@users.noreply.github.com> Date: Thu, 27 Jun 2024 09:22:10 -0500 Subject: [PATCH] Implementation of Shannon Entropy for Random Ray (#3030) Co-authored-by: Ethan Krammer Co-authored-by: John Tramm --- docs/source/methods/eigenvalue.rst | 17 +++- docs/source/methods/random_ray.rst | 36 +++++++ docs/source/usersguide/random_ray.rst | 11 +++ src/eigenvalue.cpp | 2 +- src/random_ray/flat_source_domain.cpp | 38 +++++++- src/settings.cpp | 47 ++++++---- src/simulation.cpp | 3 +- .../random_ray_entropy/__init__.py | 0 .../random_ray_entropy/geometry.xml | 88 ++++++++++++++++++ .../random_ray_entropy/materials.xml | 8 ++ .../random_ray_entropy/mgxs.h5 | Bin 0 -> 10664 bytes .../random_ray_entropy/results_true.dat | 13 +++ .../random_ray_entropy/settings.xml | 17 ++++ .../random_ray_entropy/test.py | 33 +++++++ 14 files changed, 283 insertions(+), 30 deletions(-) create mode 100644 tests/regression_tests/random_ray_entropy/__init__.py create mode 100644 tests/regression_tests/random_ray_entropy/geometry.xml create mode 100644 tests/regression_tests/random_ray_entropy/materials.xml create mode 100644 tests/regression_tests/random_ray_entropy/mgxs.h5 create mode 100644 tests/regression_tests/random_ray_entropy/results_true.dat create mode 100644 tests/regression_tests/random_ray_entropy/settings.xml create mode 100644 tests/regression_tests/random_ray_entropy/test.py diff --git a/docs/source/methods/eigenvalue.rst b/docs/source/methods/eigenvalue.rst index b63723c02..8abcc0957 100644 --- a/docs/source/methods/eigenvalue.rst +++ b/docs/source/methods/eigenvalue.rst @@ -55,15 +55,17 @@ in :ref:`fission-bank-algorithms`. Source Convergence Issues ------------------------- +.. _methods-shannon-entropy: + Diagnosing Convergence with Shannon Entropy ------------------------------------------- As discussed earlier, it is necessary to converge both :math:`k_{eff}` and the source distribution before any tallies can begin. Moreover, the convergence rate -of the source distribution is in general slower than that of -:math:`k_{eff}`. One should thus examine not only the convergence of -:math:`k_{eff}` but also the convergence of the source distribution in order to -make decisions on when to start active batches. +of the source distribution is in general slower than that of :math:`k_{eff}`. +One should thus examine not only the convergence of :math:`k_{eff}` but also the +convergence of the source distribution in order to make decisions on when to +start active batches. However, the representation of the source distribution makes it a bit more difficult to analyze its convergence. Since :math:`k_{eff}` is a scalar @@ -108,6 +110,13 @@ at plots of :math:`k_{eff}` and the Shannon entropy. A number of methods have been proposed (see e.g. [Romano]_, [Ueki]_), but each of these is not without problems. +Shannon entropy is calculated differently for the random ray solver, as +described :ref:`in the random ray theory section +`. Additionally, as the Shannon entropy only +serves as a diagnostic tool for convergence of the fission source distribution, +there is currently no diagnostic to determine if the scattering source +distribution in random ray is converged. + --------------------------- Uniform Fission Site Method --------------------------- diff --git a/docs/source/methods/random_ray.rst b/docs/source/methods/random_ray.rst index 427ba1148..d46d3ad41 100644 --- a/docs/source/methods/random_ray.rst +++ b/docs/source/methods/random_ray.rst @@ -411,6 +411,8 @@ which when partially simplified becomes: Note that there are now four (seemingly identical) volume terms in this equation. +.. _methods-volume-dilemma: + ~~~~~~~~~~~~~~ Volume Dilemma ~~~~~~~~~~~~~~ @@ -745,6 +747,7 @@ How are Tallies Handled? Most tallies, filters, and scores that you would expect to work with a multigroup solver like random ray should work. For example, you can define 3D mesh tallies with energy filters and flux, fission, and nu-fission scores, etc. + There are some restrictions though. For starters, it is assumed that all filter mesh boundaries will conform to physical surface boundaries (or lattice boundaries) in the simulation geometry. It is acceptable for multiple cells @@ -754,6 +757,39 @@ behavior if a single simulation cell is able to score to multiple filter mesh cells. In the future, the capability to fully support mesh tallies may be added to OpenMC, but for now this restriction needs to be respected. +.. _methods-shannon-entropy-random-ray: + +----------------------------- +Shannon Entropy in Random Ray +----------------------------- + +As :math:`k_{eff}` is updated at each generation, the fission source at each FSR +is used to compute the Shannon entropy. This follows the :ref:`same procedure +for computing Shannon entropy in continuous-energy or multigroup Monte Carlo +simulations `, except that fission sources at FSRs are +considered, rather than fission sites of user-defined regular meshes. Thus, the +volume-weighted fission rate is considered instead, and the fraction of fission +sources is adjusted such that: + +.. math:: + :label: fraction-source-random-ray + + S_i = \frac{\text{Fission source in FSR $i \times$ Volume of FSR + $i$}}{\text{Total fission source}} = \frac{Q_{i} V_{i}}{\sum_{i=1}^{i=N} + Q_{i} V_{i}} + +The Shannon entropy is then computed normally as + +.. math:: + :label: shannon-entropy-random-ray + + H = - \sum_{i=1}^N S_i \log_2 S_i + +where :math:`N` is the number of FSRs. FSRs with no fission source (or, +occassionally, negative fission source, :ref:`due to the volume estimator +problem `) are skipped to avoid taking an undefined +logarithm in :eq:`shannon-entropy-random-ray`. + .. _usersguide_fixed_source_methods: ------------ diff --git a/docs/source/usersguide/random_ray.rst b/docs/source/usersguide/random_ray.rst index 61f074650..2a038b122 100644 --- a/docs/source/usersguide/random_ray.rst +++ b/docs/source/usersguide/random_ray.rst @@ -62,6 +62,17 @@ solver:: settings.batches = 1200 settings.inactive = 600 +--------------- +Shannon Entropy +--------------- + +Similar to Monte Carlo, :ref:`Shannon entropy +` can be used to gauge whether the fission +source has fully developed. The Shannon entropy is calculated automatically +after each batch and is printed to the statepoint file. Unlike Monte Carlo, an +entropy mesh does not need to be defined, as the Shannon entropy is calculated +over FSRs using a volume-weighted approach. + ------------------------------- Inactive Ray Length (Dead Zone) ------------------------------- diff --git a/src/eigenvalue.cpp b/src/eigenvalue.cpp index d5410094b..8669d76f9 100644 --- a/src/eigenvalue.cpp +++ b/src/eigenvalue.cpp @@ -556,7 +556,7 @@ void shannon_entropy() double H = 0.0; for (auto p_i : p) { if (p_i > 0.0) { - H -= p_i * std::log(p_i) / std::log(2.0); + H -= p_i * std::log2(p_i); } } diff --git a/src/random_ray/flat_source_domain.cpp b/src/random_ray/flat_source_domain.cpp index 7f5d76943..792e97da0 100644 --- a/src/random_ray/flat_source_domain.cpp +++ b/src/random_ray/flat_source_domain.cpp @@ -1,6 +1,7 @@ #include "openmc/random_ray/flat_source_domain.h" #include "openmc/cell.h" +#include "openmc/eigenvalue.h" #include "openmc/geometry.h" #include "openmc/material.h" #include "openmc/message_passing.h" @@ -278,6 +279,9 @@ double FlatSourceDomain::compute_k_eff(double k_eff_old) const const int t = 0; const int a = 0; + // Vector for gathering fission source terms for Shannon entropy calculation + vector p(n_source_regions_, 0.0f); + #pragma omp parallel for reduction(+ : fission_rate_old, fission_rate_new) for (int sr = 0; sr < n_source_regions_; sr++) { @@ -300,12 +304,38 @@ double FlatSourceDomain::compute_k_eff(double k_eff_old) const sr_fission_source_new += nu_sigma_f * scalar_flux_new_[idx]; } - fission_rate_old += sr_fission_source_old * volume; - fission_rate_new += sr_fission_source_new * volume; + // Compute total fission rates in FSR + sr_fission_source_old *= volume; + sr_fission_source_new *= volume; + + // Accumulate totals + fission_rate_old += sr_fission_source_old; + fission_rate_new += sr_fission_source_new; + + // Store total fission rate in the FSR for Shannon calculation + p[sr] = sr_fission_source_new; } double k_eff_new = k_eff_old * (fission_rate_new / fission_rate_old); + double H = 0.0; + // defining an inverse sum for better performance + double inverse_sum = 1 / fission_rate_new; + +#pragma omp parallel for reduction(+ : H) + for (int sr = 0; sr < n_source_regions_; sr++) { + // Only if FSR has non-negative and non-zero fission source + if (p[sr] > 0.0f) { + // Normalize to total weight of bank sites. p_i for better performance + float p_i = p[sr] * inverse_sum; + // Sum values to obtain Shannon entropy. + H -= p_i * std::log2(p_i); + } + } + + // Adds entropy value to shared entropy vector in openmc namespace. + simulation::entropy.push_back(H); + return k_eff_new; } @@ -525,8 +555,8 @@ void FlatSourceDomain::random_ray_tally() #pragma omp atomic tally.results_(task.filter_idx, task.score_idx, TallyResult::VALUE) += score; - } // end tally task loop - } // end energy group loop + } + } // For flux tallies, the total volume of the spatial region is needed // for normalizing the flux. We store this volume in a separate tensor. diff --git a/src/settings.cpp b/src/settings.cpp index 6c3226b04..4ffa50cdf 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -627,30 +627,37 @@ void read_settings_xml(pugi::xml_node root) } } - // Shannon Entropy mesh - if (check_for_node(root, "entropy_mesh")) { - int temp = std::stoi(get_node_value(root, "entropy_mesh")); - if (model::mesh_map.find(temp) == model::mesh_map.end()) { - fatal_error(fmt::format( - "Mesh {} specified for Shannon entropy does not exist.", temp)); + // Shannon entropy + if (solver_type == SolverType::RANDOM_RAY) { + if (check_for_node(root, "entropy_mesh")) { + fatal_error("Random ray uses FSRs to compute the Shannon entropy. " + "No user-defined entropy mesh is supported."); } - - auto* m = - dynamic_cast(model::meshes[model::mesh_map.at(temp)].get()); - if (!m) - fatal_error("Only regular meshes can be used as an entropy mesh"); - simulation::entropy_mesh = m; - - // Turn on Shannon entropy calculation entropy_on = true; + } else if (solver_type == SolverType::MONTE_CARLO) { + if (check_for_node(root, "entropy_mesh")) { + int temp = std::stoi(get_node_value(root, "entropy_mesh")); + if (model::mesh_map.find(temp) == model::mesh_map.end()) { + fatal_error(fmt::format( + "Mesh {} specified for Shannon entropy does not exist.", temp)); + } - } else if (check_for_node(root, "entropy")) { - fatal_error( - "Specifying a Shannon entropy mesh via the element " - "is deprecated. Please create a mesh using and then reference " - "it by specifying its ID in an element."); + auto* m = dynamic_cast( + model::meshes[model::mesh_map.at(temp)].get()); + if (!m) + fatal_error("Only regular meshes can be used as an entropy mesh"); + simulation::entropy_mesh = m; + + // Turn on Shannon entropy calculation + entropy_on = true; + + } else if (check_for_node(root, "entropy")) { + fatal_error( + "Specifying a Shannon entropy mesh via the element " + "is deprecated. Please create a mesh using and then reference " + "it by specifying its ID in an element."); + } } - // Uniform fission source weighting mesh if (check_for_node(root, "ufs_mesh")) { auto temp = std::stoi(get_node_value(root, "ufs_mesh")); diff --git a/src/simulation.cpp b/src/simulation.cpp index 2d5d75aa4..be03e4855 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -531,7 +531,8 @@ void finalize_generation() if (settings::run_mode == RunMode::EIGENVALUE) { // Calculate shannon entropy - if (settings::entropy_on) + if (settings::entropy_on && + settings::solver_type == SolverType::MONTE_CARLO) shannon_entropy(); // Collect results and statistics diff --git a/tests/regression_tests/random_ray_entropy/__init__.py b/tests/regression_tests/random_ray_entropy/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/regression_tests/random_ray_entropy/geometry.xml b/tests/regression_tests/random_ray_entropy/geometry.xml new file mode 100644 index 000000000..4c87bbbfb --- /dev/null +++ b/tests/regression_tests/random_ray_entropy/geometry.xml @@ -0,0 +1,88 @@ + + + + + + 12.5 12.5 12.5 + 8 8 8 + 0.0 0.0 0.0 + +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 + +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 + +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 + +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 + +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 + +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 + +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 + +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 + + + + + + + + diff --git a/tests/regression_tests/random_ray_entropy/materials.xml b/tests/regression_tests/random_ray_entropy/materials.xml new file mode 100644 index 000000000..5a6f93414 --- /dev/null +++ b/tests/regression_tests/random_ray_entropy/materials.xml @@ -0,0 +1,8 @@ + + + mgxs.h5 + + + + + diff --git a/tests/regression_tests/random_ray_entropy/mgxs.h5 b/tests/regression_tests/random_ray_entropy/mgxs.h5 new file mode 100644 index 0000000000000000000000000000000000000000..6ce80bf713a8d5ff29700ba59e982e18f99b5403 GIT binary patch literal 10664 zcmeHN%}*0S6o1<)ELx=~2P1yeL;JB5M`%OA4UJMuu{yU;m}}jFYub= zpCp>Wc%w?`qzL4JLLk-KJs<`=jH9M_TERFYFm72kyJxt}HjSLnQCudeLWQygKO6SV(mu^{#vDtpJMgnd zH{3=&5sl;2%Eu!cjybM9GwIIQ3|eA|^>K9`h0dQi?=_$Ct!2L!-+%vhQ~3XQ!s+(o zk0k{bjN?Gc@yAL;^%%{l`++9`bj_V4+@MK6%Q@D1c^N!HbJV{c83gv_Q{XH7@ zLUFe!NP%DEcLnpS4N4!}|Be9huotfg$Yp`!8G*|jVO6cwN1#3`^AJf<+$ z;db}k2Yj;uLxT^6kOlNSNdC@?g5Pacu$ja1uHg#02Ep%5%2c{A9m}hQ&q)QJ zb5gOo1FX=yIu|vppSS1FZtb80ix}W%XQ$2Y2?A0_%E1fc90x*l}8~J3RX;`i{jvLfPglKxS;(OKWW1?go4)haasERZ?)r} zg}UG$wGRwK45412!0~}z?V16K?LHMyO*|C8v~|7xQd)UjRl3eE`ovRdPeNFIJeKMT Fk6-_XIk5l$ literal 0 HcmV?d00001 diff --git a/tests/regression_tests/random_ray_entropy/results_true.dat b/tests/regression_tests/random_ray_entropy/results_true.dat new file mode 100644 index 000000000..254254538 --- /dev/null +++ b/tests/regression_tests/random_ray_entropy/results_true.dat @@ -0,0 +1,13 @@ +k-combined: +1.000000E+00 0.000000E+00 +entropy: +8.863421E+00 +8.933584E+00 +8.960553E+00 +8.967921E+00 +8.976016E+00 +8.981856E+00 +8.983670E+00 +8.986584E+00 +8.987732E+00 +8.988186E+00 diff --git a/tests/regression_tests/random_ray_entropy/settings.xml b/tests/regression_tests/random_ray_entropy/settings.xml new file mode 100644 index 000000000..81deaa775 --- /dev/null +++ b/tests/regression_tests/random_ray_entropy/settings.xml @@ -0,0 +1,17 @@ + + + eigenvalue + 100 + 10 + 5 + multi-group + + + + 0.0 0.0 0.0 100.0 100.0 100.0 + + + 40.0 + 400.0 + + diff --git a/tests/regression_tests/random_ray_entropy/test.py b/tests/regression_tests/random_ray_entropy/test.py new file mode 100644 index 000000000..a3cba65ad --- /dev/null +++ b/tests/regression_tests/random_ray_entropy/test.py @@ -0,0 +1,33 @@ +import glob +import os + +from openmc import StatePoint + +from tests.testing_harness import TestHarness + + +class EntropyTestHarness(TestHarness): + def _get_results(self): + """Digest info in the statepoint and return as a string.""" + # Read the statepoint file. + statepoint = glob.glob(os.path.join(os.getcwd(), self._sp_name))[0] + with StatePoint(statepoint) as sp: + # Write out k-combined. + outstr = 'k-combined:\n' + outstr += '{:12.6E} {:12.6E}\n'.format(sp.keff.n, sp.keff.s) + + # Write out entropy data. + outstr += 'entropy:\n' + results = ['{:12.6E}'.format(x) for x in sp.entropy] + outstr += '\n'.join(results) + '\n' + + return outstr + +''' +# This test is adapted from "Monte Carlo power iteration: Entropy and spatial correlations," +M. Nowak et al. The cross sections are defined explicitly so that the value for entropy +is exactly 9 and the eigenvalue is exactly 1. +''' +def test_entropy(): + harness = EntropyTestHarness('statepoint.10.h5') + harness.main()