FW-CADIS Weight Window Generation with Random Ray (#3273)

Co-authored-by: Olek <45364492+yardasol@users.noreply.github.com>
Co-authored-by: Paul Romano <paul.k.romano@gmail.com>
This commit is contained in:
John Tramm 2025-01-27 22:54:32 -06:00 committed by GitHub
parent 2bea7f338b
commit a8768b7845
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 1351 additions and 69 deletions

View file

@ -1398,7 +1398,7 @@ mesh-based weight windows.
*Default*: true
:method:
Method used to update weight window values (currently only 'magic' is supported)
Method used to update weight window values (one of 'magic' or 'fw_cadis')
*Default*: magic

View file

@ -20,4 +20,5 @@ Theory and Methodology
energy_deposition
parallelization
cmfd
variance_reduction
random_ray

View file

@ -1060,6 +1060,8 @@ random ray and Monte Carlo, however.
develop the scattering source by way of inactive batches before beginning
active batches.
.. _adjoint:
------------------------
Adjoint Flux Solver Mode
------------------------

View file

@ -0,0 +1,134 @@
.. _methods_variance_reduction:
==================
Variance Reduction
==================
.. _methods_variance_reduction_intro:
------------
Introduction
------------
Transport problems can sometimes involve a significant degree of attenuation
between the source and a detector (tally) region, which can result in a flux
differential of ten orders of magnitude (or more) throughout the simulation
domain. As Monte Carlo uncertainties tend to be inversely proportional to the
physical flux density, it can be extremely difficult to accurately resolve
tallies in locations that are optically far from the source. This issue is
particularly common in fixed source simulations, where some tally locations may
not experience a single scoring event, even after billions of analog histories.
Variance reduction techniques aim to either flatten the global uncertainty
distribution, such that all regions of phase space have a fairly similar
uncertainty, or to reduce the uncertainty in specific locations (such as a
detector). There are two strategies available in OpenMC for variance reduction:
the Monte Carlo MAGIC method and the FW-CADIS method. Both strategies work by
developing a weight window mesh that can be utilized by subsequent Monte Carlo
solves to split particles heading towards areas of lower flux densities while
terminating particles in higher flux regions---all while maintaining a fair
game.
------------
MAGIC Method
------------
The Method of Automatic Generation of Importances by Calculation, or `MAGIC
method <https://doi.org/10.1016/j.fusengdes.2011.01.059>`_, is an iterative
technique that uses spatial flux information :math:`\phi(r)` obtained from a
normal Monte Carlo solve to produce weight windows :math:`w(r)` that can be
utilized by a subsequent iteration of Monte Carlo. While the first generation of
weight windows produced may only help to reduce variance slightly, use of these
weights to generate another set of weight windows results in a progressively
improving iterative scheme.
Equation :eq:`magic` defines how the lower bound of weight windows
:math:`w_{\ell}(r)` are generated with MAGIC using forward flux information.
Here, we can see that the flux at location :math:`r` is normalized by the
maximum flux in any group at that location. We can also see that the weights are
divided by a factor of two, which accounts for the typical :math:`5\times`
factor separating the lower and upper weight window bounds in OpenMC.
.. math::
:label: magic
w_{\ell}(r) = \frac{\phi(r)}{2\,\text{max}(\phi(r))}
A major advantage of this technique is that it does not require any special
transport machinery; it simply uses multiple Monte Carlo simulations to
iteratively improve a set of weight windows (which are typically defined on a
mesh covering the simulation domain). The downside to this method is that as the
flux differential increases between areas near and far from the source, it
requires more outer Monte Carlo iterations, each of which can be expensive in
itself. Additionally, computation of weight windows based on regular (forward)
neutron flux tally information does not produce the most numerically effective
set of weight windows. Nonetheless, MAGIC remains a simple and effective
technique for generating weight windows.
--------
FW-CADIS
--------
As discussed in the previous section, computation of weight windows based on
regular (forward) neutron flux tally information does not produce the most
numerically efficient set of weight windows. It is highly preferable to generate
weight windows based on spatial adjoint flux :math:`\phi^{\dag}(r)`
information. The adjoint flux is essentially the "reverse" simulation problem,
where we sample a random point and assume this is where a particle was absorbed,
and then trace it backwards (upscattering in energy), until we sample the point
where it was born from.
The Forward-Weighted Consistent Adjoint Driven Importance Sampling method, or
`FW-CADIS method <https://doi.org/10.13182/NSE12-33>`_, produces weight windows
for global variance reduction given adjoint flux information throughout the
entire domain. The weight window lower bound is defined in Equation
:eq:`fw_cadis`, and also involves a normalization step not shown here.
.. math::
:label: fw_cadis
w_{\ell}(r) = \frac{1}{2\phi^{\dag}(r)}
While the algorithm itself is quite simple, it requires estimates of the global
adjoint flux distribution, which is difficult to generate directly with Monte
Carlo transport. Thus, FW-CADIS typically uses an alternative solver (often
deterministic) that can be more readily adapted for generating adjoint flux
information, and which is often much cheaper than Monte Carlo given that a rough
solution is often sufficient for weight window generation.
The FW-CADIS implementation in OpenMC utilizes its own internal random ray
multigroup transport solver to generate the adjoint source distribution. No
coupling to any external transport is solver is necessary. The random ray solver
operates on the same geometry as the Monte Carlo solver, so no redefinition of
the simulation geometry is required. More details on how the adjoint flux is
computed are given in the :ref:`adjoint methods section <adjoint>`.
More information on the workflow is available in the :ref:`user guide
<variance_reduction>`, but generally production of weight windows with FW-CADIS
involves several stages (some of which are highly automated). These tasks
include generation of approximate multigroup cross section data for use by the
random ray solver, running of the random ray solver in normal (forward flux)
mode to generate a source for the adjoint solver, running of the random ray
solver in adjoint mode to generate adjoint flux tallies, and finally the
production of weight windows via the FW-CADIS method. As is discussed in the
user guide, most of these steps are automated together, making the additional
burden on the user fairly small.
The major advantage of this technique is that it typically produces much more
numerically efficient weight windows as compared to those generated with MAGIC,
sometimes with an order-of-magnitude improvement in the figure of merit
(Equation :eq:`variance_fom`), which accounts for both the variance and the
execution time. Another major advantage is that the cost of the random ray
solver is typically negligible compared to the cost of the subsequent Monte
Carlo solve itself, making it a very cheap method to deploy. The downside to
this method is that it introduces a second transport method into the mix (random
ray), such that there are more free input parameters for the user to know about
and adjust, potentially making the method more complex to use. However, as many
of the parameters have natural choices, much of this parameterization can be
handled automatically behind the scenes without the need for the user to be
aware of this.
.. math::
:label: variance_fom
\text{FOM} = \frac{1}{\text{Time} \times \sigma^2}

View file

@ -25,6 +25,7 @@ essential aspects of using OpenMC to perform simulations.
processing
parallel
volume
variance_reduction
random_ray
troubleshoot

View file

@ -435,10 +435,11 @@ Inputting Multigroup Cross Sections (MGXS)
Multigroup cross sections for use with OpenMC's random ray solver are input the
same way as with OpenMC's traditional multigroup Monte Carlo mode. There is more
information on generating multigroup cross sections via OpenMC in the
:ref:`multigroup materials <create_mgxs>` user guide. You may also wish to
use an existing multigroup library. An example of using OpenMC's Python
interface to generate a correctly formatted ``mgxs.h5`` input file is given
in the `OpenMC Jupyter notebook collection
:ref:`multigroup materials <create_mgxs>` user guide. You may also wish to use
an existing ``mgxs.h5`` MGXS library file, or define your own given a known set
of cross section data values (e.g., as taken from a benchmark specification). An
example of using OpenMC's Python interface to generate a correctly formatted
``mgxs.h5`` input file is given in the `OpenMC Jupyter notebook collection
<https://nbviewer.org/github/openmc-dev/openmc-notebooks/blob/main/mg-mode-part-i.ipynb>`_.
.. note::
@ -447,6 +448,184 @@ in the `OpenMC Jupyter notebook collection
separate materials can be defined each with a separate multigroup dataset
corresponding to a given temperature.
.. _mgxs_gen:
-------------------------------------------
Generating Multigroup Cross Sections (MGXS)
-------------------------------------------
OpenMC is capable of generating multigroup cross sections by way of flux
collapsing data based on flux solutions obtained from a continuous energy Monte
Carlo solve. While it is a circular excercise in some respects to use continuous
energy Monte Carlo to generate cross sections to be used by a reduced-fidelity
multigroup transport solver, there are many use cases where this is nonetheless
highly desirable. For instance, generation of a multigroup library may enable
the same set of approximate multigroup cross section data to be used across a
variety of problem types (or through a multidimensional parameter sweep of
design variables) with only modest errors and at greatly reduced cost as
compared to using only continuous energy Monte Carlo.
We give here a quick summary of how to produce a multigroup cross section data
file (``mgxs.h5``) from a starting point of a typical continuous energy Monte
Carlo input file. Notably, continuous energy input files define materials as a
mixture of nuclides with different densities, whereas multigroup materials are
simply defined by which name they correspond to in a ``mgxs.h5`` library file.
To generate the cross section data, we begin with a continuous energy Monte
Carlo input deck and add in the required tallies that will be needed to generate
our library. In this example, we will specify material-wise cross sections and a
two group energy decomposition::
# Define geometry
...
...
geometry = openmc.Geometry()
...
...
# Initialize MGXS library with a finished OpenMC geometry object
mgxs_lib = openmc.mgxs.Library(geometry)
# Pick energy group structure
groups = mgxs.EnergyGroups(mgxs.GROUP_STRUCTURES['CASMO-2'])
mgxs_lib.energy_groups = groups
# Disable transport correction
mgxs_lib.correction = None
# Specify needed cross sections for random ray
mgxs_lib.mgxs_types = ['total', 'absorption', 'nu-fission', 'fission',
'nu-scatter matrix', 'multiplicity matrix', 'chi']
# Specify a "cell" domain type for the cross section tally filters
mgxs_lib.domain_type = "material"
# Specify the cell domains over which to compute multi-group cross sections
mgxs_lib.domains = geom.get_all_materials().values()
# Do not compute cross sections on a nuclide-by-nuclide basis
mgxs_lib.by_nuclide = False
# Check the library - if no errors are raised, then the library is satisfactory.
mgxs_lib.check_library_for_openmc_mgxs()
# Construct all tallies needed for the multi-group cross section library
mgxs_lib.build_library()
# Create a "tallies.xml" file for the MGXS Library
tallies = openmc.Tallies()
mgxs_lib.add_to_tallies_file(tallies, merge=True)
# Export
tallies.export_to_xml()
...
When selecting an energy decomposition, you can manually define group boundaries
or pick out a group structure already known to OpenMC (a list of which can be
found at :class:`openmc.mgxs.GROUP_STRUCTURES`). Once the above input deck has
been run, the resulting statepoint file will contain the needed flux and
reaction rate tally data so that a MGXS library file can be generated. Below is
the postprocessing script needed to generate the ``mgxs.h5`` library file given
a statepoint file (e.g., ``statepoint.100.h5``) file and summary file (e.g.,
``summary.h5``) that resulted from running our previous example::
import openmc
import openmc.mgxs as mgxs
summary = openmc.Summary('summary.h5')
geom = summary.geometry
mats = summary.materials
statepoint_filename = 'statepoint.100.h5'
sp = openmc.StatePoint(statepoint_filename)
groups = mgxs.EnergyGroups(mgxs.GROUP_STRUCTURES['CASMO-2'])
mgxs_lib = openmc.mgxs.Library(geom)
mgxs_lib.energy_groups = groups
mgxs_lib.correction = None
mgxs_lib.mgxs_types = ['total', 'absorption', 'nu-fission', 'fission',
'nu-scatter matrix', 'multiplicity matrix', 'chi']
# Specify a "cell" domain type for the cross section tally filters
mgxs_lib.domain_type = "material"
# Specify the cell domains over which to compute multi-group cross sections
mgxs_lib.domains = geom.get_all_materials().values()
# Do not compute cross sections on a nuclide-by-nuclide basis
mgxs_lib.by_nuclide = False
# Check the library - if no errors are raised, then the library is satisfactory.
mgxs_lib.check_library_for_openmc_mgxs()
# Construct all tallies needed for the multi-group cross section library
mgxs_lib.build_library()
mgxs_lib.load_from_statepoint(sp)
names = []
for mat in mgxs_lib.domains: names.append(mat.name)
# Create a MGXS File which can then be written to disk
mgxs_file = mgxs_lib.create_mg_library(xs_type='macro', xsdata_names=names)
# Write the file to disk using the default filename of "mgxs.h5"
mgxs_file.export_to_hdf5("mgxs.h5")
Notably, the postprocessing script needs to match the same
:class:`openmc.mgxs.Library` settings that were used to generate the tallies,
but otherwise is able to discern the rest of the simulation details from the
statepoint and summary files. Once the postprocessing script is successfully
run, the ``mgxs.h5`` file can be loaded by subsequent runs of OpenMC.
If you want to convert continuous energy material objects in an OpenMC input
deck to multigroup ones from a ``mgxs.h5`` library, you can follow the below
example. Here we begin with the original continuous energy materials we used to
generate our MGXS library::
fuel = openmc.Material(name='UO2 (2.4%)')
fuel.set_density('g/cm3', 10.29769)
fuel.add_nuclide('U234', 4.4843e-6)
fuel.add_nuclide('U235', 5.5815e-4)
fuel.add_nuclide('U238', 2.2408e-2)
fuel.add_nuclide('O16', 4.5829e-2)
water = openmc.Material(name='Hot borated water')
water.set_density('g/cm3', 0.740582)
water.add_nuclide('H1', 4.9457e-2)
water.add_nuclide('O16', 2.4672e-2)
water.add_nuclide('B10', 8.0042e-6)
water.add_nuclide('B11', 3.2218e-5)
water.add_s_alpha_beta('c_H_in_H2O')
materials = openmc.Materials([fuel, water])
Once the ``mgxs.h5`` library file has been generated, we can then manually make
the necessary edits to the material definitions so that they load from the
multigroup library instead of defining their isotopic contents, as::
# Instantiate some Macroscopic Data
fuel_data = openmc.Macroscopic('UO2 (2.4%)')
water_data = openmc.Macroscopic('Hot borated water')
# Instantiate some Materials and register the appropriate Macroscopic objects
fuel= openmc.Material(name='UO2 (2.4%)')
fuel.set_density('macro', 1.0)
fuel.add_macroscopic(fuel_data)
water= openmc.Material(name='Hot borated water')
water.set_density('macro', 1.0)
water.add_macroscopic(water_data)
# Instantiate a Materials collection and export to XML
materials = openmc.Materials([fuel, water])
materials.cross_sections = "mgxs.h5"
In the above example, our ``fuel`` and ``water`` materials will now load MGXS
data from the ``mgxs.h5`` file instead of loading continuous energy isotopic
cross section data.
--------------
Linear Sources
--------------
@ -597,9 +776,7 @@ estimator, the following code would be used:
Adjoint Flux Mode
-----------------
The adjoint flux random ray solver mode can be enabled as:
entire
::
The adjoint flux random ray solver mode can be enabled as::
settings.random_ray['adjoint'] = True

View file

@ -0,0 +1,162 @@
.. _variance_reduction:
==================
Variance Reduction
==================
Global variance reduction in OpenMC is accomplished by weight windowing
techniques. OpenMC is capable of generating weight windows using either the
MAGIC or FW-CADIS methods. Both techniques will produce a ``weight_windows.h5``
file that can be loaded and used later on. In this section, we break down the
steps required to both generate and then apply weight windows.
.. _ww_generator:
------------------------------------
Generating Weight Windows with MAGIC
------------------------------------
As discussed in the :ref:`methods section <methods_variance_reduction>`, MAGIC
is an iterative method that uses flux tally information from a Monte Carlo
simulation to produce weight windows for a user-defined mesh. While generating
the weight windows, OpenMC is capable of applying the weight windows generated
from a previous batch while processing the next batch, allowing for progressive
improvement in the weight window quality across iterations.
The typical way of generating weight windows is to define a mesh and then add an
:class:`openmc.WeightWindowGenerator` object to an :attr:`openmc.Settings`
instance, as follows::
# Define weight window spatial mesh
ww_mesh = openmc.RegularMesh()
ww_mesh.dimension = (10, 10, 10)
ww_mesh.lower_left = (0.0, 0.0, 0.0)
ww_mesh.upper_right = (100.0, 100.0, 100.0)
# Create weight window object and adjust parameters
wwg = openmc.WeightWindowGenerator(
method='magic',
mesh=ww_mesh,
max_realizations=settings.batches
)
# Add generator to Settings instance
settings.weight_window_generators = wwg
Notably, the :attr:`max_realizations` attribute is adjusted to the number of
batches, such that all iterations are used to refine the weight window
parameters.
With the :class:`~openmc.WeightWindowGenerator` instance added to the
:attr:`~openmc.Settings`, the rest of the problem can be defined as normal. When
running, note that the second iteration and beyond may be several orders of
magnitude slower than the first. As the weight windows are applied in each
iteration, particles may be agressively split, resulting in a large number of
secondary (split) particles being generated per initial source particle. This is
not necessarily a bad thing, as the split particles are much more efficient at
exploring low flux regions of phase space as compared to initial particles.
Thus, even though the reported "particles/second" metric of OpenMC may be much
lower when generating (or just applying) weight windows as compared to analog
MC, it typically leads to an overall improvement in the figure of merit
accounting for the reduction in the variance.
.. warning::
The number of particles per batch may need to be adjusted downward
significantly to result in reasonable runtimes when weight windows are being
generated or used.
At the end of the simulation, a ``weight_windows.h5`` file will be saved to disk
for later use. Loading it in another subsequent simulation will be discussed in
the "Using Weight Windows" section below.
------------------------------------------------------
Generating Weight Windows with FW-CADIS and Random Ray
------------------------------------------------------
Weight window generation with FW-CADIS and random ray in OpenMC uses the same
exact strategy as with MAGIC. An :class:`openmc.WeightWindowGenerator` object is
added to the :attr:`openmc.Settings` object, and a ``weight_windows.h5`` will be
generated at the end of the simulation. The only difference is that the code
must be run in random ray mode. A full description of how to enable and setup
random ray mode can be found in the :ref:`Random Ray User Guide <random_ray>`.
.. note::
It is a long term goal for OpenMC to be able to generate FW-CADIS weight
windows with only a few tweaks to an existing continuous energy Monte Carlo
input deck. However, at the present time, the workflow requires several
steps to generate multigroup cross section data and to configure the random
ray solver. A high level overview of the current workflow for generation of
weight windows with FW-CADIS using random ray is given below.
1. Produce approximate multigroup cross section data (stored in a ``mgxs.h5``
library). There is more information on generating multigroup cross sections
via OpenMC in the :ref:`multigroup materials <create_mgxs>` user guide, and a
specific example of generating cross section data for use with random ray in
the :ref:`random ray MGXS guide <mgxs_gen>`.
2. Make a copy of your continuous energy Python input file. You'll edit the new
file to work in multigroup mode with random ray for producing weight windows.
3. Adjust the material definitions in your new multigroup Python file to utilize
the multigroup cross sections instead of nuclide-wise continuous energy data.
There is a specific example of making this conversion in the :ref:`random ray
MGXS guide <mgxs_gen>`.
4. Configure OpenMC to run in random ray mode (by adding several standard random
ray input flags and settings to the :attr:`openmc.Settings.random_ray`
dictionary). More information can be found in the :ref:`Random Ray User
Guide <random_ray>`.
5. Add in a :class:`~openmc.WeightWindowGenerator` in a similar manner as for
MAGIC generation with Monte Carlo and set the :attr:`method` attribute set to
``"fw_cadis"``::
# Define weight window spatial mesh
ww_mesh = openmc.RegularMesh()
ww_mesh.dimension = (10, 10, 10)
ww_mesh.lower_left = (0.0, 0.0, 0.0)
ww_mesh.upper_right = (100.0, 100.0, 100.0)
# Create weight window object and adjust parameters
wwg = openmc.WeightWindowGenerator(
method='fw_cadis',
mesh=ww_mesh,
max_realizations=settings.batches
)
# Add generator to openmc.settings object
settings.weight_window_generators = wwg
.. warning::
If using FW-CADIS weight window generation, ensure that the selected weight
window mesh does not subdivide any cells in the problem. In the future, this
restriction is intended to be relaxed, but for now subdivision of cells by a
mesh tally will result in undefined behavior.
6. When running your multigroup random ray input deck, OpenMC will automatically
run a forward solve followed by an adjoint solve, with a
``weight_windows.h5`` file generated at the end. The ``weight_windows.h5``
file will contain FW-CADIS generated weight windows. This file can be used in
identical manner as one generated with MAGIC, as described below.
--------------------
Using Weight Windows
--------------------
To use a ``weight_windows.h5`` weight window file with OpenMC's Monte Carlo
solver, the Python input just needs to load the h5 file::
settings.weight_window_checkpoints = {'collision': True, 'surface': True}
settings.survival_biasing = False
settings.weight_windows = openmc.hdf5_to_wws('weight_windows.h5')
settings.weight_windows_on = True
The :class:`~openmc.WeightWindowGenerator` instance is not needed to load an
existing ``weight_windows.h5`` file. Inclusion of a
:class:`~openmc.WeightWindowGenerator` instance will cause OpenMC to generate
*new* weight windows and thus overwrite the existing ``weight_windows.h5`` file.
Weight window mesh information is embedded into the weight window file, so the
mesh does not need to be redefined. Monte Carlo solves that load a weight window
file as above will utilize weight windows to reduce the variance of the
simulation.

View file

@ -17,9 +17,7 @@
namespace openmc {
enum class WeightWindowUpdateMethod {
MAGIC,
};
enum class WeightWindowUpdateMethod { MAGIC, FW_CADIS };
//==============================================================================
// Constants
@ -127,8 +125,9 @@ public:
//! \param[in] threshold Relative error threshold. Results over this
//! threshold will be ignored \param[in] ratio Ratio of upper to lower
//! weight window bounds
void update_magic(const Tally* tally, const std::string& value = "mean",
double threshold = 1.0, double ratio = 5.0);
void update_weights(const Tally* tally, const std::string& value = "mean",
double threshold = 1.0, double ratio = 5.0,
WeightWindowUpdateMethod method = WeightWindowUpdateMethod::MAGIC);
// NOTE: This is unused for now but may be used in the future
//! Write weight window settings to an HDF5 file
@ -221,12 +220,11 @@ public:
void create_tally();
// Data members
int32_t tally_idx_; //!< Index of the tally used to update the weight windows
int32_t ww_idx_; //!< Index of the weight windows object being generated
std::string method_; //!< Method used to update weight window. Only "magic"
//!< is valid for now.
int32_t max_realizations_; //!< Maximum number of tally realizations
int32_t update_interval_; //!< Determines how often updates occur
int32_t tally_idx_; //!< Index of the tally used to update the weight windows
int32_t ww_idx_; //!< Index of the weight windows object being generated
WeightWindowUpdateMethod method_; //!< Method used to update weight window.
int32_t max_realizations_; //!< Maximum number of tally realizations
int32_t update_interval_; //!< Determines how often updates occur
bool on_the_fly_; //!< Whether or not to keep tally results between batches or
//!< realizations

View file

@ -660,9 +660,8 @@ class WeightWindowGenerator:
maximum and minimum energy for the data available at runtime.
particle_type : {'neutron', 'photon'}
Particle type the weight windows apply to
method : {'magic'}
The weight window generation methodology applied during an update. Only
'magic' is currently supported.
method : {'magic', 'fw_cadis'}
The weight window generation methodology applied during an update.
max_realizations : int
The upper limit for number of tally realizations when generating weight
windows.
@ -680,9 +679,8 @@ class WeightWindowGenerator:
energies in [eV] for a single bin
particle_type : {'neutron', 'photon'}
Particle type the weight windows apply to
method : {'magic'}
The weight window generation methodology applied during an update. Only
'magic' is currently supported.
method : {'magic', 'fw_cadis'}
The weight window generation methodology applied during an update.
max_realizations : int
The upper limit for number of tally realizations when generating weight
windows.
@ -767,7 +765,7 @@ class WeightWindowGenerator:
@method.setter
def method(self, m: str):
cv.check_type('generation method', m, str)
cv.check_value('generation method', m, {'magic'})
cv.check_value('generation method', m, ('magic', 'fw_cadis'))
self._method = m
if self._update_parameters is not None:
try:
@ -800,7 +798,7 @@ class WeightWindowGenerator:
return self._update_parameters
def _check_update_parameters(self, params: dict):
if self.method == 'magic':
if self.method == 'magic' or self.method == 'fw_cadis':
check_params = self._MAGIC_PARAMS
for key, val in params.items():
@ -843,7 +841,7 @@ class WeightWindowGenerator:
update_parameters : dict
The update parameters as-read from the XML node (keys: str, values: str)
"""
if method == 'magic':
if method == 'magic' or method == 'fw_cadis':
check_params = cls._MAGIC_PARAMS
for param, param_type in check_params.items():

View file

@ -173,6 +173,7 @@ void validate_random_ray_inputs()
case FilterType::MATERIAL:
case FilterType::MESH:
case FilterType::UNIVERSE:
case FilterType::PARTICLE:
break;
default:
fatal_error("Invalid filter specified. Only cell, cell_instance, "

View file

@ -22,6 +22,7 @@
#include "openmc/particle.h"
#include "openmc/particle_data.h"
#include "openmc/physics_common.h"
#include "openmc/random_ray/flat_source_domain.h"
#include "openmc/search.h"
#include "openmc/settings.h"
#include "openmc/tallies/filter_energy.h"
@ -482,8 +483,8 @@ void WeightWindows::set_bounds(
upper_ww_ *= ratio;
}
void WeightWindows::update_magic(
const Tally* tally, const std::string& value, double threshold, double ratio)
void WeightWindows::update_weights(const Tally* tally, const std::string& value,
double threshold, double ratio, WeightWindowUpdateMethod method)
{
///////////////////////////
// Setup and checks
@ -624,20 +625,44 @@ void WeightWindows::update_magic(
auto mesh_vols = this->mesh()->volumes();
int e_bins = new_bounds.shape()[0];
for (int e = 0; e < e_bins; e++) {
// select all
auto group_view = xt::view(new_bounds, e);
// divide by volume of mesh elements
for (int i = 0; i < group_view.size(); i++) {
group_view[i] /= mesh_vols[i];
if (method == WeightWindowUpdateMethod::MAGIC) {
// If we are computing weight windows with forward fluxes derived from a
// Monte Carlo or forward random ray solve, we use the MAGIC algorithm.
for (int e = 0; e < e_bins; e++) {
// select all
auto group_view = xt::view(new_bounds, e);
// divide by volume of mesh elements
for (int i = 0; i < group_view.size(); i++) {
group_view[i] /= mesh_vols[i];
}
double group_max =
*std::max_element(group_view.begin(), group_view.end());
// normalize values in this energy group by the maximum value for this
// group
if (group_max > 0.0)
group_view /= 2.0 * group_max;
}
} else {
// If we are computing weight windows with adjoint fluxes derived from an
// adjoint random ray solve, we use the FW-CADIS algorithm.
for (int e = 0; e < e_bins; e++) {
// select all
auto group_view = xt::view(new_bounds, e);
// divide by volume of mesh elements
for (int i = 0; i < group_view.size(); i++) {
group_view[i] /= mesh_vols[i];
}
}
double group_max = *std::max_element(group_view.begin(), group_view.end());
// normalize values in this energy group by the maximum value for this
// group
if (group_max > 0.0)
group_view /= 2.0 * group_max;
xt::noalias(new_bounds) = 1.0 / new_bounds;
auto max_val = xt::amax(new_bounds)();
xt::noalias(new_bounds) = new_bounds / (2.0 * max_val);
}
// make sure that values where the mean is zero are set s.t. the weight window
@ -760,38 +785,52 @@ WeightWindowsGenerator::WeightWindowsGenerator(pugi::xml_node node)
e_bounds.push_back(data::energy_max[p_type]);
}
// set method and parameters for updates
method_ = get_node_value(node, "method");
if (method_ == "magic") {
// parse non-default update parameters if specified
if (check_for_node(node, "update_parameters")) {
pugi::xml_node params_node = node.child("update_parameters");
if (check_for_node(params_node, "value"))
tally_value_ = get_node_value(params_node, "value");
if (check_for_node(params_node, "threshold"))
threshold_ = std::stod(get_node_value(params_node, "threshold"));
if (check_for_node(params_node, "ratio")) {
ratio_ = std::stod(get_node_value(params_node, "ratio"));
}
// set method
std::string method_string = get_node_value(node, "method");
if (method_string == "magic") {
method_ = WeightWindowUpdateMethod::MAGIC;
if (settings::solver_type == SolverType::RANDOM_RAY &&
FlatSourceDomain::adjoint_) {
fatal_error("Random ray weight window generation with MAGIC cannot be "
"done in adjoint mode.");
}
// check update parameter values
if (tally_value_ != "mean" && tally_value_ != "rel_err") {
fatal_error(fmt::format("Unsupported tally value '{}' specified for "
"weight window generation.",
tally_value_));
} else if (method_string == "fw_cadis") {
method_ = WeightWindowUpdateMethod::FW_CADIS;
if (settings::solver_type != SolverType::RANDOM_RAY) {
fatal_error("FW-CADIS can only be run in random ray solver mode.");
}
if (threshold_ <= 0.0)
fatal_error(fmt::format("Invalid relative error threshold '{}' (<= 0.0) "
"specified for weight window generation",
ratio_));
if (ratio_ <= 1.0)
fatal_error(fmt::format("Invalid weight window ratio '{}' (<= 1.0) "
"specified for weight window generation"));
FlatSourceDomain::adjoint_ = true;
} else {
fatal_error(fmt::format(
"Unknown weight window update method '{}' specified", method_));
"Unknown weight window update method '{}' specified", method_string));
}
// parse non-default update parameters if specified
if (check_for_node(node, "update_parameters")) {
pugi::xml_node params_node = node.child("update_parameters");
if (check_for_node(params_node, "value"))
tally_value_ = get_node_value(params_node, "value");
if (check_for_node(params_node, "threshold"))
threshold_ = std::stod(get_node_value(params_node, "threshold"));
if (check_for_node(params_node, "ratio")) {
ratio_ = std::stod(get_node_value(params_node, "ratio"));
}
}
// check update parameter values
if (tally_value_ != "mean" && tally_value_ != "rel_err") {
fatal_error(fmt::format("Unsupported tally value '{}' specified for "
"weight window generation.",
tally_value_));
}
if (threshold_ <= 0.0)
fatal_error(fmt::format("Invalid relative error threshold '{}' (<= 0.0) "
"specified for weight window generation",
ratio_));
if (ratio_ <= 1.0)
fatal_error(fmt::format("Invalid weight window ratio '{}' (<= 1.0) "
"specified for weight window generation"));
// create a matching weight windows object
auto wws = WeightWindows::create();
ww_idx_ = wws->index();
@ -860,7 +899,7 @@ void WeightWindowsGenerator::update() const
tally->n_realizations_ % update_interval_ != 0)
return;
wws->update_magic(tally, tally_value_, threshold_, ratio_);
wws->update_weights(tally, tally_value_, threshold_, ratio_, method_);
// if we're not doing on the fly generation, reset the tally results once
// we're done with the update
@ -944,7 +983,7 @@ extern "C" int openmc_weight_windows_update_magic(int32_t ww_idx,
// get the WeightWindows object
const auto& wws = variance_reduction::weight_windows.at(ww_idx);
wws->update_magic(tally, value, threshold, ratio);
wws->update_weights(tally, value, threshold, ratio);
return 0;
}

View file

@ -0,0 +1,260 @@
<?xml version='1.0' encoding='utf-8'?>
<model>
<materials>
<cross_sections>mgxs.h5</cross_sections>
<material id="1" name="source">
<density units="macro" value="1.0"/>
<macroscopic name="source"/>
</material>
<material id="2" name="void">
<density units="macro" value="1.0"/>
<macroscopic name="void"/>
</material>
<material id="3" name="absorber">
<density units="macro" value="1.0"/>
<macroscopic name="absorber"/>
</material>
</materials>
<geometry>
<cell id="1" material="1" name="infinite source region" universe="1"/>
<cell id="2" material="2" name="infinite void region" universe="2"/>
<cell id="3" material="3" name="infinite absorber region" universe="3"/>
<cell fill="4" id="4" universe="5"/>
<cell fill="5" id="5" name="full domain" region="1 -2 3 -4 5 -6" universe="6"/>
<lattice id="4">
<pitch>2.5 2.5 2.5</pitch>
<dimension>12 12 12</dimension>
<lower_left>0.0 0.0 0.0</lower_left>
<universes>
3 3 3 3 3 3 3 3 3 3 3 3
3 3 3 3 3 3 3 3 3 3 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
1 1 2 2 2 2 2 2 2 2 3 3
1 1 2 2 2 2 2 2 2 2 3 3
3 3 3 3 3 3 3 3 3 3 3 3
3 3 3 3 3 3 3 3 3 3 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
1 1 2 2 2 2 2 2 2 2 3 3
1 1 2 2 2 2 2 2 2 2 3 3
3 3 3 3 3 3 3 3 3 3 3 3
3 3 3 3 3 3 3 3 3 3 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
3 3 3 3 3 3 3 3 3 3 3 3
3 3 3 3 3 3 3 3 3 3 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
3 3 3 3 3 3 3 3 3 3 3 3
3 3 3 3 3 3 3 3 3 3 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
3 3 3 3 3 3 3 3 3 3 3 3
3 3 3 3 3 3 3 3 3 3 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
3 3 3 3 3 3 3 3 3 3 3 3
3 3 3 3 3 3 3 3 3 3 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
3 3 3 3 3 3 3 3 3 3 3 3
3 3 3 3 3 3 3 3 3 3 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
3 3 3 3 3 3 3 3 3 3 3 3
3 3 3 3 3 3 3 3 3 3 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
3 3 3 3 3 3 3 3 3 3 3 3
3 3 3 3 3 3 3 3 3 3 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
2 2 2 2 2 2 2 2 2 2 3 3
3 3 3 3 3 3 3 3 3 3 3 3
3 3 3 3 3 3 3 3 3 3 3 3
3 3 3 3 3 3 3 3 3 3 3 3
3 3 3 3 3 3 3 3 3 3 3 3
3 3 3 3 3 3 3 3 3 3 3 3
3 3 3 3 3 3 3 3 3 3 3 3
3 3 3 3 3 3 3 3 3 3 3 3
3 3 3 3 3 3 3 3 3 3 3 3
3 3 3 3 3 3 3 3 3 3 3 3
3 3 3 3 3 3 3 3 3 3 3 3
3 3 3 3 3 3 3 3 3 3 3 3
3 3 3 3 3 3 3 3 3 3 3 3
3 3 3 3 3 3 3 3 3 3 3 3
3 3 3 3 3 3 3 3 3 3 3 3
3 3 3 3 3 3 3 3 3 3 3 3
3 3 3 3 3 3 3 3 3 3 3 3
3 3 3 3 3 3 3 3 3 3 3 3
3 3 3 3 3 3 3 3 3 3 3 3
3 3 3 3 3 3 3 3 3 3 3 3
3 3 3 3 3 3 3 3 3 3 3 3
3 3 3 3 3 3 3 3 3 3 3 3
3 3 3 3 3 3 3 3 3 3 3 3
3 3 3 3 3 3 3 3 3 3 3 3
3 3 3 3 3 3 3 3 3 3 3 3 </universes>
</lattice>
<surface boundary="reflective" coeffs="0.0" id="1" type="x-plane"/>
<surface boundary="vacuum" coeffs="30.0" id="2" type="x-plane"/>
<surface boundary="reflective" coeffs="0.0" id="3" type="y-plane"/>
<surface boundary="vacuum" coeffs="30.0" id="4" type="y-plane"/>
<surface boundary="reflective" coeffs="0.0" id="5" type="z-plane"/>
<surface boundary="vacuum" coeffs="30.0" id="6" type="z-plane"/>
</geometry>
<settings>
<run_mode>fixed source</run_mode>
<particles>90</particles>
<batches>10</batches>
<inactive>5</inactive>
<source particle="neutron" strength="3.14" type="independent">
<energy type="discrete">
<parameters>100.0 1.0</parameters>
</energy>
<constraints>
<domain_type>universe</domain_type>
<domain_ids>1</domain_ids>
</constraints>
</source>
<energy_mode>multi-group</energy_mode>
<weight_window_generators>
<weight_windows_generator>
<mesh>1</mesh>
<particle_type>neutron</particle_type>
<max_realizations>10</max_realizations>
<update_interval>1</update_interval>
<on_the_fly>true</on_the_fly>
<method>fw_cadis</method>
</weight_windows_generator>
</weight_window_generators>
<mesh id="1">
<dimension>6 6 6</dimension>
<lower_left>0.0 0.0 0.0</lower_left>
<upper_right>30.0 30.0 30.0</upper_right>
</mesh>
<random_ray>
<distance_active>500.0</distance_active>
<distance_inactive>100.0</distance_inactive>
<source particle="neutron" strength="1.0" type="independent">
<space type="box">
<parameters>0.0 0.0 0.0 30.0 30.0 30.0</parameters>
</space>
</source>
<volume_normalized_flux_tallies>True</volume_normalized_flux_tallies>
<volume_estimator>naive</volume_estimator>
</random_ray>
</settings>
<tallies>
<filter id="3" type="material">
<bins>1</bins>
</filter>
<filter id="2" type="material">
<bins>2</bins>
</filter>
<filter id="1" type="material">
<bins>3</bins>
</filter>
<tally id="3" name="Source Tally">
<filters>3</filters>
<scores>flux</scores>
<estimator>tracklength</estimator>
</tally>
<tally id="2" name="Void Tally">
<filters>2</filters>
<scores>flux</scores>
<estimator>tracklength</estimator>
</tally>
<tally id="1" name="Absorber Tally">
<filters>1</filters>
<scores>flux</scores>
<estimator>tracklength</estimator>
</tally>
</tallies>
</model>

View file

@ -0,0 +1,442 @@
RegularMesh
ID = 1
Name =
Dimensions = 3
Voxels = [6 6 6]
Lower left = [0. 0. 0.]
Upper Right = [np.float64(30.0), np.float64(30.0), np.float64(30.0)]
Width = [5. 5. 5.]
Lower Bounds
1.50e-01
1.54e-01
1.43e-01
1.30e-01
1.30e-01
1.97e-01
1.46e-01
1.25e-01
1.34e-01
1.32e-01
1.27e-01
3.40e-01
1.36e-01
1.13e-01
1.09e-01
1.19e-01
1.25e-01
3.08e-01
1.38e-01
1.18e-01
9.49e-02
1.19e-01
1.35e-01
1.96e-01
1.12e-01
9.60e-02
9.33e-02
9.74e-02
1.37e-01
2.76e-01
4.53e-02
1.31e-01
2.47e-01
7.36e-02
1.72e-01
1.38e-01
1.55e-01
1.54e-01
1.48e-01
1.36e-01
1.29e-01
1.88e-01
1.40e-01
1.51e-01
1.28e-01
1.18e-01
1.29e-01
2.86e-01
1.42e-01
1.26e-01
1.28e-01
1.16e-01
1.22e-01
2.81e-01
1.38e-01
1.22e-01
1.04e-01
9.81e-02
1.08e-01
1.97e-01
1.34e-01
1.32e-01
1.10e-01
8.69e-02
1.03e-01
1.33e-01
2.51e-01
3.39e-01
1.13e-01
6.92e-02
1.54e-01
5.71e-02
1.60e-01
1.52e-01
1.34e-01
1.32e-01
1.28e-01
2.53e-01
1.33e-01
1.35e-01
1.15e-01
1.32e-01
1.37e-01
3.13e-01
1.27e-01
1.36e-01
1.35e-01
1.10e-01
1.08e-01
8.81e-02
1.51e-01
1.32e-01
1.13e-01
9.89e-02
9.25e-02
5.04e-02
1.40e-01
1.56e-01
1.40e-01
9.19e-02
6.54e-02
2.05e-02
3.35e-01
1.88e-01
3.16e-01
1.80e-01
5.78e-02
5.36e-03
1.43e-01
1.35e-01
1.47e-01
1.33e-01
1.47e-01
4.95e-01
1.42e-01
1.45e-01
1.27e-01
1.31e-01
1.26e-01
1.43e-01
1.49e-01
1.23e-01
1.24e-01
1.13e-01
9.69e-02
1.07e-01
1.65e-01
1.50e-01
1.24e-01
1.07e-01
8.90e-02
8.81e-02
1.48e-01
1.58e-01
1.20e-01
7.93e-02
7.19e-02
3.94e-02
2.29e-01
1.95e-01
2.66e-01
1.51e-01
3.52e-02
3.68e-03
1.48e-01
1.18e-01
1.47e-01
1.41e-01
1.37e-01
2.71e-01
1.70e-01
1.70e-01
1.29e-01
1.33e-01
1.28e-01
2.79e-01
1.84e-01
1.42e-01
1.18e-01
1.25e-01
1.26e-01
1.64e-01
1.73e-01
1.37e-01
1.12e-01
9.01e-02
1.13e-01
1.68e-01
1.60e-01
1.52e-01
1.30e-01
8.71e-02
7.72e-02
3.76e-02
1.57e-01
9.31e-02
1.63e-01
6.80e-02
6.22e-02
3.37e-03
2.47e-01
2.17e-01
1.48e-01
2.01e-01
1.54e-01
5.69e-02
2.89e-01
3.59e-01
1.17e-01
8.64e-02
3.34e-01
1.50e-01
5.00e-01
2.16e-01
1.22e-01
1.66e-01
1.44e-01
4.98e-02
4.28e-01
1.17e-01
8.73e-02
1.11e-01
1.09e-01
9.69e-02
3.26e-01
2.28e-01
2.49e-01
3.55e-02
1.69e-02
3.95e-03
1.03e-01
4.89e-02
1.90e-01
4.61e-02
9.75e-03
3.00e-04
Upper Bounds
7.49e-01
7.71e-01
7.13e-01
6.51e-01
6.48e-01
9.85e-01
7.31e-01
6.26e-01
6.69e-01
6.59e-01
6.33e-01
1.70e+00
6.82e-01
5.67e-01
5.45e-01
5.94e-01
6.24e-01
1.54e+00
6.92e-01
5.91e-01
4.75e-01
5.96e-01
6.77e-01
9.82e-01
5.59e-01
4.80e-01
4.67e-01
4.87e-01
6.84e-01
1.38e+00
2.26e-01
6.55e-01
1.24e+00
3.68e-01
8.59e-01
6.88e-01
7.73e-01
7.70e-01
7.42e-01
6.78e-01
6.46e-01
9.40e-01
7.00e-01
7.57e-01
6.39e-01
5.92e-01
6.43e-01
1.43e+00
7.12e-01
6.29e-01
6.40e-01
5.78e-01
6.09e-01
1.40e+00
6.90e-01
6.10e-01
5.21e-01
4.91e-01
5.42e-01
9.87e-01
6.71e-01
6.60e-01
5.52e-01
4.35e-01
5.14e-01
6.65e-01
1.26e+00
1.70e+00
5.63e-01
3.46e-01
7.72e-01
2.86e-01
8.00e-01
7.58e-01
6.70e-01
6.59e-01
6.41e-01
1.26e+00
6.65e-01
6.76e-01
5.76e-01
6.60e-01
6.86e-01
1.57e+00
6.35e-01
6.81e-01
6.75e-01
5.51e-01
5.39e-01
4.40e-01
7.56e-01
6.62e-01
5.67e-01
4.95e-01
4.63e-01
2.52e-01
6.98e-01
7.78e-01
6.99e-01
4.59e-01
3.27e-01
1.02e-01
1.67e+00
9.38e-01
1.58e+00
9.00e-01
2.89e-01
2.68e-02
7.16e-01
6.75e-01
7.35e-01
6.63e-01
7.34e-01
2.47e+00
7.11e-01
7.23e-01
6.34e-01
6.54e-01
6.32e-01
7.14e-01
7.45e-01
6.15e-01
6.19e-01
5.64e-01
4.85e-01
5.37e-01
8.26e-01
7.51e-01
6.21e-01
5.36e-01
4.45e-01
4.40e-01
7.40e-01
7.92e-01
5.99e-01
3.96e-01
3.60e-01
1.97e-01
1.14e+00
9.76e-01
1.33e+00
7.55e-01
1.76e-01
1.84e-02
7.42e-01
5.90e-01
7.37e-01
7.07e-01
6.84e-01
1.36e+00
8.52e-01
8.49e-01
6.46e-01
6.63e-01
6.42e-01
1.40e+00
9.19e-01
7.08e-01
5.89e-01
6.24e-01
6.28e-01
8.21e-01
8.64e-01
6.87e-01
5.58e-01
4.50e-01
5.64e-01
8.40e-01
7.99e-01
7.62e-01
6.51e-01
4.36e-01
3.86e-01
1.88e-01
7.85e-01
4.66e-01
8.13e-01
3.40e-01
3.11e-01
1.68e-02
1.24e+00
1.09e+00
7.40e-01
1.00e+00
7.72e-01
2.85e-01
1.44e+00
1.79e+00
5.85e-01
4.32e-01
1.67e+00
7.50e-01
2.50e+00
1.08e+00
6.10e-01
8.32e-01
7.19e-01
2.49e-01
2.14e+00
5.87e-01
4.36e-01
5.57e-01
5.47e-01
4.84e-01
1.63e+00
1.14e+00
1.25e+00
1.78e-01
8.43e-02
1.97e-02
5.17e-01
2.44e-01
9.49e-01
2.31e-01
4.87e-02
1.50e-03

View file

@ -0,0 +1,33 @@
import os
import openmc
from openmc.examples import random_ray_three_region_cube
from tests.testing_harness import WeightWindowPyAPITestHarness
class MGXSTestHarness(WeightWindowPyAPITestHarness):
def _cleanup(self):
super()._cleanup()
f = 'mgxs.h5'
if os.path.exists(f):
os.remove(f)
def test_random_ray_adjoint_fixed_source():
model = random_ray_three_region_cube()
ww_mesh = openmc.RegularMesh()
n = 6
width = 30.0
ww_mesh.dimension = (n, n, n)
ww_mesh.lower_left = (0.0, 0.0, 0.0)
ww_mesh.upper_right = (width, width, width)
wwg = openmc.WeightWindowGenerator(
method="fw_cadis", mesh=ww_mesh, max_realizations=model.settings.batches)
model.settings.weight_window_generators = wwg
model.settings.random_ray['volume_estimator'] = 'naive'
harness = MGXSTestHarness('statepoint.10.h5', model)
harness.main()

View file

@ -441,6 +441,40 @@ class TolerantPyAPITestHarness(PyAPITestHarness):
assert compare, 'Results do not agree'
class WeightWindowPyAPITestHarness(PyAPITestHarness):
def _get_results(self):
"""Digest info in the weight window file and return as a string."""
ww = openmc.hdf5_to_wws()[0]
# Access the weight window bounds
lower_bound = ww.lower_ww_bounds
upper_bound = ww.upper_ww_bounds
# Flatten both arrays
flattened_lower_bound = lower_bound.flatten()
flattened_upper_bound = upper_bound.flatten()
# Convert each element to a string in scientific notation with 2 decimal places
formatted_lower_bound = [f'{x:.2e}' for x in flattened_lower_bound]
formatted_upper_bound = [f'{x:.2e}' for x in flattened_upper_bound]
# Concatenate the formatted arrays
concatenated_strings = ["Lower Bounds"] + formatted_lower_bound + \
["Upper Bounds"] + formatted_upper_bound
# Join the concatenated strings into a single string with newline characters
final_string = '\n'.join(concatenated_strings)
# Prepend the mesh text description and return final string
return str(ww.mesh) + final_string
def _cleanup(self):
super()._cleanup()
f = 'weight_windows.h5'
if os.path.exists(f):
os.remove(f)
class PlotTestHarness(TestHarness):
"""Specialized TestHarness for running OpenMC plotting tests."""
def __init__(self, plot_names, voxel_convert_checks=[]):