Implement mesh-based weight windows (squashed)

Co-authored-by: Andrew Davis <andrew.davis@ukaea.uk>
Co-authored-by: Patrick Shriwise <pshriwise@gmail.com>
Co-authored-by: Paul Romano <paul.k.romano@gmail.com>
This commit is contained in:
YuanHu-PKU-KIT 2022-01-08 14:43:18 -06:00 committed by Paul Romano
parent d12e390f1a
commit e08af3c374
24 changed files with 1690 additions and 12 deletions

View file

@ -361,6 +361,7 @@ list(APPEND libopenmc_SOURCES
src/track_output.cpp
src/urr.cpp
src/volume_calc.cpp
src/weight_windows.cpp
src/wmp.cpp
src/xml_interface.cpp
src/xsdata.cpp)

View file

@ -1007,3 +1007,170 @@ sub-elements/attributes:
sample points within.
*Default*: None
-------------------------
``<weightwindow>`` Element
-------------------------
The ``<weightwindow>`` element specifies all necessary parameters for weight
window mesh function.This element has the following sub-elements/attributes:
:type:
This input indicates where the space-energy cell boundary and lower weight
window bound should read from. 0 means read these from the settings.xml, 1
means read these from MCNP WWINP file. This input is necessary for weight
window mesh function to be used.
*Default*: None
:origin:
The lower-left coordinates of weight window mesh.
*Default*: None
*Example*: 0 0 0
:xmesh:
The locations of the coarse meshes in the X direction.
*Default*: None
*Example*: 10 20
:xints:
The number of fine meshes within corresponding coarse meshes in the X direction.
*Default*: None
*Example*: 10 20
:ymesh:
The locations of the coarse meshes in the Y direction.
*Default*: None
*Example*: 10 20
:yints:
The number of fine meshes within corresponding coarse meshes in the Y direction.
*Default*: None
*Example*: 10 20
:zmesh:
The locations of the coarse meshes in the Z direction.
*Default*: None
*Example*: 10 20
:zints:
The number of fine meshes within corresponding coarse meshes in the Z direction.
*Default*: None
*Example*: 10 20
:energy:
The energy groups for neutron and/or photon in eV.
:neutron:
The energy groups for neutron in eV.
*Default*: None
*Example*: 1e6 2e6 10e6
:photon:
The energy groups for photon in eV.
*Default*: None
*Example*: 1e6 5e6
:lower_ww:
The lower weight window bound for weight window mesh to be used.
*Default*: None
*Example*:
[x0,y0,z0,E0] ...... [xn,y0,z0,E0] Index of X increasing first
...
[x0,y1,z0,E0] ...... [xn,y1,z0,E0] Index of Y increasing second
...
[x0,y0,z1,E0] ...... [xn,yn,z1,E0] Index of Z increasing third
...
[x0,y0,z0,E1] ...... [xn,yn,zn,E1] Index of Energy increasing forth
.. note:: All these sub-elements (from ``<origin>`` to ``<lower_ww>``) are only
valide for ``<type>`` 0. ``<type>`` 1 will read all these necessary
parameters from MCNP wwinp file, which should named as ``wwinp``.
The examples above indicate that there are 10 fine meshes in 0~10 cm
and 10 fine meshes in 10~20 cm at all 3 dimensions, and 3 energy
groups (0~1e6, 1e6~2e6, 2e6~10e6) for neutron and 2 energy groups
(0~1e6, 1e6~5e6) for photon.
:neutron_parameters:
The weight window parameters for neutron.
:upper:
The ratio of upper weight window bound and lower weight window bound.
*Default*: 5
:survival:
The ratio of survival weight and lower weight window bound.
*Default*: 3
:max_split:
The max number of split particles.
*Default*: 5
:multiplier:
The multiplier for all lower weight window bounds.
*Default*: 1
:photon_parameters:
The weight window parameters for photon.
:upper:
The ratio of upper weight window bound and lower weight window bound.
*Default*: 5
:survival:
The ratio of survival weight and lower weight window bound.
*Default*: 3
:max_split:
The max number of split particles.
*Default*: 5
:multiplier:
The multiplier for all lower weight window bounds.
*Default*: 1
:user_defined_biasing:
The usder defined weight biasing in energy.
:biasing_energy:
The energy groups for source.
:origin_probability:
The origin probability for each energy groups.
:biasing:
The biasing probability for each energy groups.

View file

@ -358,6 +358,10 @@ constexpr int CMFD_NOACCEL {-1};
enum class GeometryType { CSG, DAG };
//==============================================================================
// Variance Reduction constants
constexpr double WEIGHT_CUTOFF {1.0e-38}; // default low weight cutoff
} // namespace openmc
#endif // OPENMC_CONSTANTS_H

View file

@ -305,6 +305,9 @@ private:
int n_event_ {0}; // number of events executed in this particle's history
// Weight window information
int n_split_ {0}; // Number of splits this particle has undergone
// DagMC state variables
#ifdef DAGMC
moab::DagMC::RayHistory history_;
@ -426,6 +429,9 @@ public:
double& collision_distance() { return collision_distance_; }
int& n_event() { return n_event_; }
int n_split() const { return n_split_; }
int& n_split() { return n_split_; }
#ifdef DAGMC
moab::DagMC::RayHistory& history() { return history_; }
Direction& last_dir() { return last_dir_; }

View file

@ -89,6 +89,11 @@ void inelastic_scatter(const Nuclide& nuc, const Reaction& rx, Particle& p);
void sample_secondary_photons(Particle& p, int i_nuclide);
//Split or Roulette particles based their weight and the lower weight window
// bound.
//! \param[in] p, particle to be split or rouletted with the weight window.
void split_particle(Particle& p);
} // namespace openmc
#endif // OPENMC_PHYSICS_H

View file

@ -55,6 +55,7 @@ extern "C" bool trigger_on; //!< tally triggers enabled?
extern bool trigger_predict; //!< predict batches for triggers?
extern bool ufs_on; //!< uniform fission site method on?
extern bool urr_ptables_on; //!< use unresolved resonance prob. tables?
extern bool weight_windows_on; //!< are weight windows are enabled?
extern bool write_all_tracks; //!< write track files for every particle?
extern bool write_initial_source; //!< write out initial source file?
@ -99,6 +100,7 @@ extern std::unordered_set<int>
statepoint_batch; //!< Batches when state should be written
extern std::unordered_set<int>
source_write_surf_id; //!< Surface ids where sources will be written
extern int max_splits; //!< maximum number of particle splits for weight windows
extern int64_t max_surface_particles; //!< maximum number of particles to be
//!< banked on surfaces per process
extern TemperatureMethod

View file

@ -0,0 +1,95 @@
#ifndef OPENMC_WEIGHT_WINDOWS_H
#define OPENMC_WEIGHT_WINDOWS_H
#include <cstdint>
#include <unordered_map>
#include <hdf5.h>
#include <pugixml.hpp>
#include "openmc/constants.h"
#include "openmc/memory.h"
#include "openmc/mesh.h"
#include "openmc/particle.h"
#include "openmc/vector.h"
namespace openmc {
//==============================================================================
// Non-member functions
//==============================================================================
//! Free memory associated with weight windows
void free_memory_weight_windows();
//==============================================================================
// Global variables
//==============================================================================
class WeightWindows;
namespace variance_reduction {
extern std::unordered_map<int32_t, int32_t> ww_map;
extern vector<unique_ptr<WeightWindows>> weight_windows;
} // namespace variance_reduction
//==============================================================================
//! Individual weight window information
//==============================================================================
struct WeightWindow {
double lower_weight {-1}; // -1 indicates not valid state
double upper_weight {1};
double survival_weight {0.5};
double weight_cutoff {WEIGHT_CUTOFF};
int max_split {1};
//! Whether the weight window is in a valid state
operator bool() const { return lower_weight >= 0.0; }
};
//==============================================================================
//! Weight window settings
//==============================================================================
class WeightWindows {
public:
// Constructors
WeightWindows();
WeightWindows(pugi::xml_node node);
// Methods
//! Set the weight window ID
void set_id(int32_t id = -1);
// NOTE: This is unused for now but may be used in the future
//! Write weight window settings to an HDF5 file
//! \param[in] group HDF5 group to write to
void to_hdf5(hid_t group) const;
//! Return a weight window at the specified index
//! \param[in] p Particle to get weight window for
WeightWindow get_weight_window(const Particle& p) const;
// Accessors
int32_t id() const { return id_; }
const Mesh& mesh() const { return *model::meshes[mesh_idx_]; }
private:
// Data members
int32_t id_; //!< Unique ID
ParticleType particle_type_; //!< Particle type to apply weight windows to
vector<double> energy_bins_; //!< Energy bins [eV]
vector<double> lower_ww_; //!< Lower weight window bounds
vector<double> upper_ww_; //!< Upper weight window bounds
double survival_ratio_; //!< Survival weight ratio
double weight_cutoff_; //!< Weight cutoff
int max_split_; //!< Maximum value for particle splitting
int32_t mesh_idx_; //!< index in meshes vector
};
} // namespace openmc
#endif // OPENMC_WEIGHT_WINDOWS_H

View file

@ -11,6 +11,7 @@ from openmc.plots import *
from openmc.region import *
from openmc.volume import *
from openmc.source import *
from openmc.weight_windows import *
from openmc.settings import *
from openmc.surface import *
from openmc.universe import *

View file

@ -84,6 +84,32 @@ class MeshBase(IDManagerMixin, ABC):
else:
raise ValueError('Unrecognized mesh type: "' + mesh_type + '"')
@classmethod
def from_xml(cls, elem):
"""Generates a mesh from an XML element
Parameters
----------
elem : xml.etree.ElementTree.Element
XML element
Returns
-------
openmc.MeshBase
an openmc mesh object
"""
mesh_type = get_text(elem, 'type')
if mesh_type == 'regular' or mesh_type is None:
return RegularMesh.from_xml_element(elem)
elif mesh_type == 'rectilinear':
return RectilinearMesh.from_xml_element(elem)
elif mesh_type == 'unstructured':
return UnstructuredMesh.from_xml_element(elem)
else:
raise ValueError(f'Unrecognized mesh type "{mesh_type}" found.')
class RegularMesh(MeshBase):
"""A regular Cartesian mesh in one, two, or three dimensions
@ -301,7 +327,6 @@ class RegularMesh(MeshBase):
if self._upper_right is not None:
subelement = ET.SubElement(element, "upper_right")
subelement.text = ' '.join(map(str, self._upper_right))
if self._width is not None:
subelement = ET.SubElement(element, "width")
subelement.text = ' '.join(map(str, self._width))
@ -570,19 +595,19 @@ class RectilinearMesh(MeshBase):
fmt = '{0: <16}{1}{2}\n'
string = super().__repr__()
string += fmt.format('\tDimensions', '=\t', self.n_dimension)
x_grid_str = str(self._x_grid) if not self._x_grid else len(self._x_grid)
x_grid_str = str(self._x_grid) if self._x_grid is None else len(self._x_grid)
string += fmt.format('\tN X pnts:', '=\t', x_grid_str)
if self._x_grid:
if self._x_grid is not None:
string += fmt.format('\tX Min:', '=\t', self._x_grid[0])
string += fmt.format('\tX Max:', '=\t', self._x_grid[-1])
y_grid_str = str(self._y_grid) if not self._y_grid else len(self._y_grid)
y_grid_str = str(self._y_grid) if self._y_grid is None else len(self._y_grid)
string += fmt.format('\tN Y pnts:', '=\t', y_grid_str)
if self._y_grid:
if self._y_grid is not None:
string += fmt.format('\tY Min:', '=\t', self._y_grid[0])
string += fmt.format('\tY Max:', '=\t', self._y_grid[-1])
z_grid_str = str(self._z_grid) if not self._z_grid else len(self._z_grid)
z_grid_str = str(self._z_grid) if self._z_grid is None else len(self._z_grid)
string += fmt.format('\tN Z pnts:', '=\t', z_grid_str)
if self._z_grid:
if self._z_grid is not None:
string += fmt.format('\tZ Min:', '=\t', self._z_grid[0])
string += fmt.format('\tZ Max:', '=\t', self._z_grid[-1])
return string
@ -599,6 +624,29 @@ class RectilinearMesh(MeshBase):
return mesh
@classmethod
def from_xml_element(cls, elem):
"""Generate a rectilinear mesh from an XML element
Parameters
----------
elem : xml.etree.ElementTree.Element
XML element
Returns
-------
openmc.RectilinearMesh
Rectilinear mesh object
"""
id = int(get_text(elem, 'id'))
mesh = cls(id)
mesh.x_grid = [float(x) for x in get_text(elem, 'x_grid').split()]
mesh.y_grid = [float(y) for y in get_text(elem, 'y_grid').split()]
mesh.z_grid = [float(z) for z in get_text(elem, 'z_grid').split()]
return mesh
def to_xml_element(self):
"""Return XML representation of the mesh
@ -911,4 +959,4 @@ class UnstructuredMesh(MeshBase):
library = get_text(elem, 'library')
length_multiplier = float(get_text(elem, 'length_multiplier', 1.0))
return cls(filename, library, mesh_id, '', length_multiplier)
return cls(filename, library, mesh_id, '', length_multiplier)

View file

@ -6,7 +6,7 @@ from xml.etree import ElementTree as ET
from math import ceil
import openmc.checkvalue as cv
from . import VolumeCalculation, Source, RegularMesh
from . import VolumeCalculation, Source, RegularMesh, WeightWindows
from ._xml import clean_indentation, get_text, reorder_attributes
@ -96,6 +96,10 @@ class Settings:
.. versionadded:: 0.12
max_order : None or int
Maximum scattering order to apply globally when in multi-group mode.
max_splits : int
Maximum number of times a particle can split during a history
.. versionadded:: 0.13
no_reduce : bool
Indicate that all user-defined and global tallies should not be reduced
across processes in a parallel calculation.
@ -197,9 +201,16 @@ class Settings:
described in :ref:`verbosity`.
volume_calculations : VolumeCalculation or iterable of VolumeCalculation
Stochastic volume calculation specifications
weight_windows : iterable of openmc.WeightWindows
Weight windows to use for variance reduction
.. versionadded:: 0.13
weight_windows_on : bool
Whether weight windows are enabled
.. versionadded:: 0.13
write_initial_source : bool
Indicate whether to write the initial source distribution to file
"""
def __init__(self):
@ -272,6 +283,9 @@ class Settings:
self._event_based = None
self._max_particles_in_flight = None
self._write_initial_source = None
self._weight_windows = cv.CheckedList(Source, 'weight windows')
self._weight_windows_on = None
self._max_splits = None
@property
def run_mode(self):
@ -445,6 +459,18 @@ class Settings:
def write_initial_source(self):
return self._write_initial_source
@property
def weight_windows(self):
return self._weight_windows
@property
def weight_windows_on(self):
return self._weight_windows_on
@property
def max_splits(self):
return self._max_splits
@run_mode.setter
def run_mode(self, run_mode):
cv.check_value('run mode', run_mode, {x.value for x in RunMode})
@ -835,6 +861,23 @@ class Settings:
cv.check_type('write initial source', value, bool)
self._write_initial_source = value
@weight_windows.setter
def weight_windows(self, value):
if not isinstance(value, MutableSequence):
value = [value]
self._weight_windows = cv.CheckedList(WeightWindows, 'weight windows', value)
@weight_windows_on.setter
def weight_windows_on(self, value):
cv.check_type('weight windows on', value, bool)
self._weight_windows_on = value
@max_splits.setter
def max_splits(self, value):
cv.check_type('maximum particle splits', value, Integral)
cv.check_greater_than('max particles in flight', value, 0)
self._max_splits = value
def _create_run_mode_subelement(self, root):
elem = ET.SubElement(root, "run_mode")
elem.text = self._run_mode.value
@ -1128,6 +1171,25 @@ class Settings:
elem = ET.SubElement(root, "write_initial_source")
elem.text = str(self._write_initial_source).lower()
def _create_weight_windows_subelement(self, root):
for ww in self._weight_windows:
# Add weight window information
root.append(ww.to_xml_element())
# See if a <mesh> element already exists -- if not, add it
path = f"./mesh[@id='{ww.mesh.id}']"
if root.find(path) is None:
root.append(ww.mesh.to_xml_element())
if self._weight_windows_on is not None:
elem = ET.SubElement(root, "weight_windows_on")
elem.text = str(self._weight_windows_on).lower()
def _create_max_splits_subelement(self, root):
if self._max_splits is not None:
elem = ET.SubElement(root, "max_splits")
elem.text = str(self._max_splits)
def _eigenvalue_from_xml_element(self, root):
elem = root.find('eigenvalue')
if elem is not None:
@ -1410,6 +1472,20 @@ class Settings:
if text is not None:
self.write_initial_source = text in ('true', '1')
def _weight_windows_from_xml_element(self, root):
for elem in root.findall('weight_windows'):
ww = WeightWindows.from_xml_element(elem, root)
self.weight_windows.append(ww)
text = get_text(root, 'weight_windows_on')
if text is not None:
self.weight_windows_on = text in ('true', '1')
def _max_splits_from_xml_element(self, root):
text = get_text(root, 'max_splits')
if text is not None:
self.max_splits = int(text)
def export_to_xml(self, path='settings.xml'):
"""Export simulation settings to an XML file.
@ -1464,6 +1540,8 @@ class Settings:
self._create_material_cell_offsets_subelement(root_element)
self._create_log_grid_bins_subelement(root_element)
self._create_write_initial_source_subelement(root_element)
self._create_weight_windows_subelement(root_element)
self._create_max_splits_subelement(root_element)
# Clean the indentation in the file to be user-readable
clean_indentation(root_element)
@ -1538,6 +1616,8 @@ class Settings:
settings._material_cell_offsets_from_xml_element(root)
settings._log_grid_bins_from_xml_element(root)
settings._write_initial_source_from_xml_element(root)
settings._weight_windows_from_xml_element(root)
settings._max_splits_from_xml_element(root)
# TODO: Get volume calculations

View file

@ -12,7 +12,7 @@ _VERSION_SUMMARY = 6
class Summary:
"""Summary of geometry, materials, and tallies used in a simulation.
"""Summary of model used in a simulation.
Attributes
----------

317
openmc/weight_windows.py Normal file
View file

@ -0,0 +1,317 @@
from collections.abc import Iterable
from numbers import Real, Integral
from xml.etree import ElementTree as ET
import numpy as np
from openmc.filter import _PARTICLES
from openmc.mesh import MeshBase
import openmc.checkvalue as cv
from ._xml import get_text
from .mixin import IDManagerMixin
class WeightWindows(IDManagerMixin):
""" A class to handle the creation of a set of specific weight window
paramaters - a variance reduction class may have several of these
Parameters
----------
mesh : openmc.MeshBase
Mesh for the weight windows
lower_ww_bounds : Iterable of Real
A list of values for which each value is the lower bound of a weight
window
upper_ww_bounds : Iterable of Real
A list of values for which each value is the upper bound of a weight
window
upper_bound_ratio : float
Ratio of the lower to upper weight window bounds
energy_bins : Iterable of Real
A list of values for which each successive pair constitutes a range of
energies in [eV] for a single bin
particle_type : {'neutron', 'photon'}
Particle type the weight windows apply to
survival_ratio : float
Ratio of the lower weight window bound to the survival weight for
rouletting
max_split : int
Maximum allowable number of particles when splitting
weight_cutoff : float
Threshold below which particles will be terminated
id : int
Unique identifier for the weight window settings. If not
specified an identifier will automatically be assigned.
Attributes
----------
id : int
Unique identifier for the weight window settings.
mesh : openmc.MeshBase
Mesh for the weight windows
particle_type : str
Particle type the weight windows apply to
energy_bins : Iterable of Real
A list of values for which each successive pair constitutes a range of
energies in [eV] for a single bin
lower_ww_bounds : Iterable of Real
A list of values for which each value is the lower bound of a weight
window
upper_ww_bounds : Iterable of Real
A list of values for which each value is the upper bound of a weight
window
survival_ratio : float
Ratio of the lower weight window bound to the survival weight for
rouletting
max_split : int
Maximum allowable number of particles when splitting
weight_cutoff : float
Threshold below which particles will be terminated
"""
next_id = 1
used_ids = set()
def __init__(self, mesh, lower_ww_bounds, upper_ww_bounds=None,
upper_bound_ratio=None, energy_bins=None, particle_type='neutron',
survival_ratio=3, max_split=10, weight_cutoff=1.e-38, id=None):
self.mesh = mesh
self.id = id
self.particle_type = particle_type
self.energy_bins = energy_bins
self.lower_ww_bounds = lower_ww_bounds
cv.check_length('Lower window bounds', self.lower_ww_bounds, len(self.energy_bins))
if upper_ww_bounds is not None and upper_bound_ratio:
raise ValueError("Exactly one of uppwer_ww_bounds and "
"upper_bound_ratio must be present.")
if upper_ww_bounds is None and upper_bound_ratio is None:
raise ValueError("Exactly one of uppwer_ww_bounds and "
"upper_bound_ratio must be present.")
if upper_bound_ratio:
self.upper_ww_bounds = [
lb * upper_bound_ratio for lb in self.lower_ww_bounds
]
if upper_ww_bounds is not None:
self.upper_ww_bounds = upper_ww_bounds
if len(self.lower_ww_bounds) != len(self.upper_ww_bounds):
raise ValueError('Size of the lower and upper weight window bounds'
'do not match')
self.survival_ratio = survival_ratio
self.max_split = max_split
self.weight_cutoff = weight_cutoff
def __repr__(self):
string = type(self).__name__ + '\n'
string += '{: <16}=\t{}\n'.format('\tID', self._id)
string += '{: <16}=\t{}\n'.format('\tMesh:', self.mesh)
string += '{: <16}=\t{}\n'.format('\tParticle Type', self._particle_type)
string += '{: <16}=\t{}\n'.format('\tEnergy Bins', self._energy_bins)
string += '{: <16}=\t{}\n'.format('\tLower WW Bounds', self._lower_ww_bounds)
string += '{: <16}=\t{}\n'.format('\tUpper WW Bounds', self._upper_ww_bounds)
string += '{: <16}=\t{}\n'.format('\tSurvival Ratio', self._survival_ratio)
string += '{: <16}=\t{}\n'.format('\tMax Split', self._max_split)
string += '{: <16}=\t{}\n'.format('\tWeight Cutoff', self._weight_cutoff)
return string
@property
def mesh(self):
return self._mesh
@mesh.setter
def mesh(self, mesh):
cv.check_type('Weight window mesh', mesh, MeshBase)
self._mesh = mesh
@property
def particle_type(self):
return self._particle_type
@particle_type.setter
def particle_type(self, pt):
cv.check_value('Particle type', pt, _PARTICLES)
self._particle_type = pt
@property
def energy_bins(self):
return self._energy_bins
@energy_bins.setter
def energy_bins(self, bins):
cv.check_type('Energy bins', bins, Iterable, Real)
self._energy_bins = np.array(bins)
@property
def lower_ww_bounds(self):
return self._lower_ww_bounds
@lower_ww_bounds.setter
def lower_ww_bounds(self, bounds):
cv.check_type('Lower WW bounds', bounds, Iterable, Real)
self._lower_ww_bounds = np.array(bounds)
@property
def upper_ww_bounds(self):
return self._upper_ww_bounds
@upper_ww_bounds.setter
def upper_ww_bounds(self, bounds):
cv.check_type('Upper WW bounds', bounds, Iterable, Real)
self._upper_ww_bounds = np.array(bounds)
@property
def survival_ratio(self):
return self._survival_ratio
@survival_ratio.setter
def survival_ratio(self, val):
cv.check_type('Survival ratio', val, Real)
cv.check_greater_than('Survival ratio', val, 1.0, True)
self._survival_ratio = val
@property
def max_split(self):
return self._max_split
@max_split.setter
def max_split(self, val):
cv.check_type('Max split', val, Integral)
self._max_split = val
@property
def weight_cutoff(self):
return self._weight_cutoff
@weight_cutoff.setter
def weight_cutoff(self, cutoff):
cv.check_type('Weight cutoff', cutoff, Real)
cv.check_greater_than('Weight cutoff', cutoff, 0.0, True)
self._weight_cutoff = cutoff
def to_xml_element(self):
"""Return an XML representation of the weight window settings
Returns
-------
element : xml.etree.ElementTree.Element
XML element containing the weight window information
"""
element = ET.Element('weight_windows')
element.set('id', str(self._id))
subelement = ET.SubElement(element, 'mesh')
subelement.text = str(self.mesh.id)
subelement = ET.SubElement(element, 'particle_type')
subelement.text = self.particle_type
subelement = ET.SubElement(element, 'energy_bins')
subelement.text = ' '.join(str(e) for e in self.energy_bins)
subelement = ET.SubElement(element, 'lower_ww_bounds')
subelement.text = ' '.join(str(b) for b in self.lower_ww_bounds)
subelement = ET.SubElement(element, 'upper_ww_bounds')
subelement.text = ' '.join(str(b) for b in self.upper_ww_bounds)
subelement = ET.SubElement(element, 'survival_ratio')
subelement.text = str(self.survival_ratio)
subelement = ET.SubElement(element, 'max_split')
subelement.text = str(self.max_split)
subelement = ET.SubElement(element, 'weight_cutoff')
subelement.text = str(self.weight_cutoff)
return element
@classmethod
def from_xml_element(cls, elem, root):
"""Generate weight window settings from an XML element
Parameters
----------
elem : xml.etree.ElementTree.Element
XML element
root : xml.etree.ElementTree.Element
Root element for the file where meshes can be found
Returns
-------
openmc.WeightWindows
Weight windows object
"""
# Get mesh for weight windows
mesh_id = int(get_text(elem, 'mesh'))
path = f"./mesh[@id='{mesh_id}']"
mesh_elem = root.find(path)
if mesh_elem is not None:
mesh = MeshBase.from_xml(elem)
# Read all other parameters
lower_ww_bounds = [float(l) for l in get_text(elem, 'lower_ww_bounds').split()]
upper_ww_bounds = [float(u) for u in get_text(elem, 'upper_ww_bounds').split()]
ebins = [float(b) for b in get_text(elem, 'energy_bins').split()]
particle_type = get_text(elem, 'particle_type')
survival_ratio = float(get_text(elem, 'survival_ratio'))
max_split = int(get_text(elem, 'max_split'))
weight_cutoff = float(get_text(elem, 'weight_cutoff'))
id = int(get_text(elem, 'id'))
return cls(
mesh=mesh,
lower_ww_bounds=lower_ww_bounds,
upper_ww_bounds=upper_ww_bounds,
energy_bins=ebins,
particle_type=particle_type,
survival_ratio=survival_ratio,
max_split=max_split,
weight_cutoff=weight_cutoff,
id=id
)
@classmethod
def from_hdf5(cls, group, meshes):
"""Create weight windows from HDF5 group
Parameters
----------
group : h5py.Group
Group in HDF5 file
meshes : dict
Dictionary mapping IDs to mesh objects
Returns
-------
openmc.WeightWindows
A weight window object
"""
id = int(group.name.split('/')[-1].lstrip('weight_windows'))
mesh_id = group['mesh'][()]
ptype = group['particle_type'][()].decode()
ebins = group['energy_bins'][()]
lower_ww_bounds = group['lower_ww_bounds'][()]
upper_ww_bounds = group['upper_ww_bounds'][()]
survival_ratio = group['survival_ratio'][()]
max_split = group['max_split'][()]
weight_cutoff = group['weight_cutoff'][()]
return cls(
mesh=meshes[mesh_id],
lower_ww_bounds=lower_ww_bounds,
upper_ww_bounds=upper_ww_bounds,
energy_bins=ebins,
particle_type=ptype,
survival_ratio=survival_ratio,
max_split=max_split,
weight_cutoff=weight_cutoff,
id=id
)

View file

@ -25,6 +25,7 @@
#include "openmc/thermal.h"
#include "openmc/timer.h"
#include "openmc/volume_calc.h"
#include "openmc/weight_windows.h"
#include "xtensor/xview.hpp"
@ -47,6 +48,7 @@ void free_memory()
free_memory_tally();
free_memory_bank();
free_memory_plot();
free_memory_weight_windows();
if (mpi::master) {
free_memory_cmfd();
}
@ -82,6 +84,7 @@ int openmc_finalize()
settings::legendre_to_tabular_points = -1;
settings::material_cell_offsets = true;
settings::max_particles_in_flight = 100000;
settings::max_splits = 1000;
settings::n_inactive = 0;
settings::n_particles = -1;
settings::output_summary = true;
@ -113,6 +116,7 @@ int openmc_finalize()
settings::verbosity = 7;
settings::weight_cutoff = 0.25;
settings::weight_survive = 1.0;
settings::weight_windows_on = false;
settings::write_all_tracks = false;
settings::write_initial_source = false;

View file

@ -33,9 +33,9 @@
#include "openmc/xml_interface.h"
#ifdef LIBMESH
#include "libmesh/mesh_modification.h"
#include "libmesh/mesh_tools.h"
#include "libmesh/numeric_vector.h"
#include "libmesh/mesh_modification.h"
#endif
namespace openmc {

View file

@ -23,6 +23,7 @@
#include "openmc/string_utils.h"
#include "openmc/tallies/tally.h"
#include "openmc/thermal.h"
#include "openmc/weight_windows.h"
#include <fmt/core.h>
@ -44,9 +45,15 @@ void collision(Particle& p)
switch (p.type()) {
case ParticleType::neutron:
sample_neutron_reaction(p);
if (settings::weight_windows_on) {
split_particle(p);
}
break;
case ParticleType::photon:
sample_photon_reaction(p);
if (settings::weight_windows_on) {
split_particle(p);
}
break;
case ParticleType::electron:
sample_electron_reaction(p);
@ -1197,4 +1204,69 @@ void sample_secondary_photons(Particle& p, int i_nuclide)
}
}
// split a particle based on the statistical weight of the current location
void split_particle(Particle& p)
{
// skip dead or no energy
if (p.E() <= 0 || !p.alive())
return;
bool in_domain = false;
// TODO: this is a linear search - should do something more clever
WeightWindow weight_window;
for (const auto& ww : variance_reduction::weight_windows) {
weight_window = ww->get_weight_window(p);
if (weight_window)
break;
}
// particle is not in any of the ww domains, do nothing
if (!weight_window)
return;
// get the paramters
double weight = p.wgt();
p.wgt_last() = weight;
// first check to see if particle should be killed for weight cutoff
if (p.wgt() < weight_window.weight_cutoff) {
p.alive() = false;
p.wgt() = 0.0;
return;
}
// if particle's weight is above the weight window split until they are within
// the window
if (weight > weight_window.upper_weight) {
// do not further split the particle if above the limit
if (p.n_split() >= settings::max_splits)
return;
double n_split = std::ceil(weight / weight_window.upper_weight);
double max_split = weight_window.max_split;
n_split = std::min(n_split, max_split);
p.n_split() += n_split;
// Create secondaries and divide weight among all particles
int i_split = std::round(n_split);
for (int l = 0; l < i_split - 1; l++) {
p.create_secondary(weight / n_split, p.u(), p.E(), p.type());
}
// TODO: maybe weight should be weight - sum of child weight
p.wgt() = weight / n_split;
} else if (weight <= weight_window.lower_weight) {
// if the particle weight is below the window, play Russian roulette
double weight_survive =
std::min(weight * weight_window.max_split, weight_window.survival_weight);
if (weight_survive * prn(p.current_seed()) <= weight) {
p.wgt() = weight_survive;
} else {
p.alive() = false;
p.wgt() = 0.0;
}
// else particle is in the window, continue as normal
}
}
} // namespace openmc

View file

@ -27,6 +27,7 @@
#include "openmc/string_utils.h"
#include "openmc/tallies/trigger.h"
#include "openmc/volume_calc.h"
#include "openmc/weight_windows.h"
#include "openmc/xml_interface.h"
namespace openmc {
@ -67,6 +68,7 @@ bool trigger_on {false};
bool trigger_predict {false};
bool ufs_on {false};
bool urr_ptables_on {true};
bool weight_windows_on {false};
bool write_all_tracks {false};
bool write_initial_source {false};
@ -92,6 +94,7 @@ int max_order {0};
int n_log_bins {8000};
int n_batches;
int n_max_batches;
int max_splits {1000};
ResScatMethod res_scat_method {ResScatMethod::rvs};
double res_scat_energy_min {0.01};
double res_scat_energy_max {1000.0};
@ -848,6 +851,23 @@ void read_settings_xml()
if (check_for_node(root, "material_cell_offsets")) {
material_cell_offsets = get_node_value_bool(root, "material_cell_offsets");
}
// Weight window information
for (pugi::xml_node node_ww : root.children("weight_windows")) {
variance_reduction::weight_windows.emplace_back(
std::make_unique<WeightWindows>(node_ww));
// Enable weight windows by default if one or more are present
settings::weight_windows_on = true;
}
if (check_for_node(root, "weight_windows_on")) {
weight_windows_on = get_node_value_bool(root, "weight_windows_on");
}
if (check_for_node(root, "max_splits")) {
settings::max_splits = std::stoi(get_node_value(root, "max_splits"));
}
}
void free_memory_settings()

220
src/weight_windows.cpp Normal file
View file

@ -0,0 +1,220 @@
#include "openmc/weight_windows.h"
#include "openmc/error.h"
#include "openmc/file_utils.h"
#include "openmc/hdf5_interface.h"
#include "openmc/particle.h"
#include "openmc/particle_data.h"
#include "openmc/search.h"
#include "openmc/xml_interface.h"
#include <fmt/core.h>
#include <gsl/gsl-lite.hpp>
namespace openmc {
//==============================================================================
// Global variables
//==============================================================================
namespace variance_reduction {
std::unordered_map<int32_t, int32_t> ww_map;
openmc::vector<unique_ptr<WeightWindows>> weight_windows;
} // namespace variance_reduction
//==============================================================================
// Non-member functions
//==============================================================================
void free_memory_weight_windows()
{
variance_reduction::ww_map.clear();
variance_reduction::weight_windows.clear();
}
//==============================================================================
// WeightWindowSettings implementation
//==============================================================================
WeightWindows::WeightWindows(pugi::xml_node node)
{
if (!check_for_node(node, "id")) {
fatal_error("Must specify 'id' for weight windows.");
}
int32_t id = std::stoi(get_node_value(node, "id"));
this->set_id(id);
// get the particle type
if (!check_for_node(node, "particle_type")) {
fatal_error("Must specify 'particle_type' for weight windows.");
}
auto particle_type_str = std::string(get_node_value(node, "particle_type"));
particle_type_ = openmc::str_to_particle_type(particle_type_str);
// get the survival value - optional
if (check_for_node(node, "survival_ratio")) {
survival_ratio_ = std::stod(get_node_value(node, "survival_ratio"));
if (survival_ratio_ <= 1)
fatal_error("Survival to lower weight window ratio must bigger than 1"
"and less than the upper to lower weight window ratio.");
}
// get the max split - optional
if (check_for_node(node, "max_split")) {
max_split_ = std::stod(get_node_value(node, "max_split"));
if (max_split_ <= 1)
fatal_error("max split must be larger than 1");
}
// weight cutoff - optional - but default is 1e-38
if (check_for_node(node, "weight_cutoff")) {
weight_cutoff_ = std::stod(get_node_value(node, "weight_cutoff"));
if (weight_cutoff_ <= 0)
fatal_error("weight_cutoff must be larger than 0");
if (weight_cutoff_ > 1)
fatal_error("weight_cutoff must be less than 1");
}
// get the mesh id
int32_t mesh_id;
if (check_for_node(node, "mesh")) {
mesh_id = std::stoi(get_node_value(node, "mesh"));
} else {
fatal_error("No mesh specifier in the domain.");
}
// set the indices to save time later
mesh_idx_ = model::mesh_map.at(mesh_id);
// energy bounds
if (check_for_node(node, "energy_bins")) {
energy_bins_ = get_node_array<double>(node, "energy_bins");
} else {
fatal_error("<energy_bins> is missing from the weight_windows.xml file.");
}
// read the lower weight bounds
if (check_for_node(node, "lower_ww_bounds")) {
lower_ww_ = get_node_array<double>(node, "lower_ww_bounds");
} else {
fatal_error("<lower_ww_bounds> section is missing from the "
"variance_reduction.xml file.");
}
// read the upper weight bounds
if (check_for_node(node, "upper_ww_bounds")) {
upper_ww_ = get_node_array<double>(node, "upper_ww_bounds");
} else {
fatal_error("<upper_ww_bounds> node is missing from the "
"variance_reduction.xml file.");
}
// make sure that the upper and lower bounds have the same size
if (upper_ww_.size() != lower_ww_.size()) {
fatal_error("The upper and lower weight window lengths do not match.");
}
// num spatial*energy bins must match num weight bins
int num_spatial_bins = this->mesh().n_bins();
int num_energy_bins = energy_bins_.size() - 1;
int num_weight_bins = lower_ww_.size();
if (num_weight_bins != num_spatial_bins * num_energy_bins) {
auto err_msg =
fmt::format("In weight window domain {} the number of spatial "
"energy/spatial bins ({}) does not match the number "
"of weight bins ({})",
id_, num_energy_bins, num_weight_bins);
fatal_error(err_msg);
}
}
void WeightWindows::set_id(int32_t id)
{
Expects(id >= 0 || id == C_NONE);
// Clear entry in mesh map in case one was already assigned
if (id_ != C_NONE) {
variance_reduction::ww_map.erase(id_);
id_ = C_NONE;
}
// Ensure no other mesh has the same ID
if (variance_reduction::ww_map.find(id) != variance_reduction::ww_map.end()) {
throw std::runtime_error {
fmt::format("Two weight windows have the same ID: {}", id)};
}
// If no ID is specified, auto-assign the next ID in the sequence
if (id == C_NONE) {
id = 0;
for (const auto& m : variance_reduction::weight_windows) {
id = std::max(id, m->id_);
}
++id;
}
// Update ID and entry in the mesh map
id_ = id;
variance_reduction::ww_map[id] =
variance_reduction::weight_windows.size() - 1;
}
WeightWindow WeightWindows::get_weight_window(const Particle& p) const
{
// check for particle type
if (particle_type_ != p.type()) {
return {};
}
// Get mesh index for particle's position
const auto& mesh = this->mesh();
int ww_index = mesh.get_bin(p.r());
// particle is outside the weight window mesh
if (ww_index < 0)
return {};
// particle energy
double E = p.E();
// check to make sure energy is in range, expects sorted energy values
if (E < energy_bins_.front() || E > energy_bins_.back())
return {};
// get the mesh bin in energy group
int energy_bin =
lower_bound_index(energy_bins_.begin(), energy_bins_.end(), E);
// indices now points to the correct weight for the given energy
ww_index += energy_bin * mesh.n_bins();
// Create individual weight window
WeightWindow ww;
ww.lower_weight = lower_ww_[ww_index];
ww.upper_weight = upper_ww_[ww_index];
ww.survival_weight = ww.lower_weight * survival_ratio_;
ww.max_split = max_split_;
ww.weight_cutoff = weight_cutoff_;
return ww;
}
void WeightWindows::to_hdf5(hid_t group) const
{
hid_t ww_group = create_group(group, fmt::format("weight_windows {}", id_));
write_dataset(
ww_group, "particle_type", openmc::particle_type_to_str(particle_type_));
write_dataset(ww_group, "energy_bins", energy_bins_);
write_dataset(ww_group, "lower_ww_bounds", lower_ww_);
write_dataset(ww_group, "upper_ww_bounds", upper_ww_);
write_dataset(ww_group, "survival_ratio", survival_ratio_);
write_dataset(ww_group, "max_split", max_split_);
write_dataset(ww_group, "weight_cutoff", weight_cutoff_);
write_dataset(ww_group, "mesh", this->mesh().id_);
close_group(ww_group);
}
} // namespace openmc

View file

@ -0,0 +1 @@

View file

@ -0,0 +1,12 @@
<?xml version="1.0"?>
<geometry>
<surface id="5" type="sphere" coeffs="0. 0. 0. 120."/>
<surface id="6" type="sphere" coeffs="0. 0. 0. 125."/>
<surface id="10" type="sphere" coeffs="0. 0. 0. 126." boundary="vacuum"/>
<cell id="5" material="4" region="-5" />
<cell id="6" material="void" region="5 -6" />
<cell id="10" material="void" region="6 -10" />
</geometry>

View file

@ -0,0 +1,27 @@
<?xml version="1.0"?>
<materials>
<material id="4" name="M4">
<density units="g/cc" value="2.3" />
<nuclide ao="0.168018676" name="H1" />
<nuclide ao="1.93244e-05" name="H2" />
<nuclide ao="0.561814465" name="O16" />
<nuclide ao="0.00021401" name="O17" />
<!--nuclide ao="0.001154525" name="O18" /-->
<nuclide ao="0.021365" name="Na23" />
<nuclide ao="0.021343" name="Al27" />
<nuclide ao="0.187439342" name="Si28" />
<nuclide ao="0.009517714" name="Si29" />
<nuclide ao="0.006273944" name="Si30" />
<nuclide ao="0.018026179" name="Ca40" />
<nuclide ao="0.00012031" name="Ca42" />
<nuclide ao="2.51033e-05" name="Ca43" />
<nuclide ao="0.000387892" name="Ca44" />
<nuclide ao="7.438e-07" name="Ca46" />
<nuclide ao="3.47727e-05" name="Ca48" />
<nuclide ao="0.000248179" name="Fe54" />
<nuclide ao="0.003895875" name="Fe56" />
<nuclide ao="8.99727e-05" name="Fe57" />
<nuclide ao="1.19737e-05" name="Fe58" />
</material>
</materials>

View file

@ -0,0 +1,513 @@
tally 1:
1.853183E-02
1.717351E-04
3.029396E-03
4.649064E-06
7.544003E-05
3.164069E-09
9.706048E-05
4.714215E-09
8.632819E-05
4.068658E-09
9.248134E-05
4.277560E-09
1.057891E-04
5.622465E-09
8.678273E-05
5.459032E-09
1.030006E-04
5.401900E-09
6.530162E-05
2.132845E-09
8.429033E-05
3.976020E-09
5.307443E-05
1.535662E-09
6.990236E-05
2.572885E-09
1.046155E-04
5.472211E-09
7.487444E-05
2.832742E-09
1.215270E-04
7.386937E-09
1.526205E-04
1.275134E-08
1.129350E-04
6.438217E-09
1.072884E-04
5.786945E-09
5.986538E-05
1.804378E-09
7.724972E-05
2.986308E-09
9.752938E-05
4.820728E-09
1.090999E-04
5.972473E-09
7.551735E-05
3.575893E-09
8.860064E-05
3.964865E-09
1.018275E-04
5.186898E-09
4.915270E-05
1.275256E-09
1.173593E-04
7.382579E-09
9.243253E-05
4.293582E-09
6.170115E-05
2.140169E-09
8.808961E-05
3.894854E-09
6.941292E-05
2.993412E-09
9.880710E-05
4.936860E-09
8.013541E-05
3.475624E-09
1.234975E-04
7.699401E-09
1.109685E-04
6.162383E-09
4.015613E-05
8.177789E-10
3.194893E-05
5.138631E-10
2.612327E-05
4.678400E-10
1.514792E-05
1.490146E-10
1.388898E-05
1.110648E-10
6.051317E-05
2.103995E-09
3.670728E-05
1.127649E-09
8.000503E-05
3.215581E-09
1.028704E-04
5.559097E-09
1.497360E-04
1.214582E-08
7.122351E-05
2.898636E-09
6.040324E-05
3.648552E-09
3.249762E-05
5.514740E-10
9.107075E-05
4.182561E-09
1.009222E-04
5.390605E-09
3.321080E-05
6.503888E-10
3.723183E-05
8.021551E-10
7.536094E-06
5.679271E-11
6.966939E-06
4.853823E-11
3.370952E-05
5.697978E-10
4.413982E-06
1.948324E-11
1.773332E-05
3.144706E-10
3.287734E-05
5.548423E-10
1.616419E-05
2.612810E-10
8.737224E-05
4.078498E-09
9.726809E-05
5.996334E-09
7.896294E-05
3.185197E-09
1.250834E-05
8.630440E-11
9.543138E-05
5.240082E-09
4.106567E-05
8.535411E-10
1.050483E-04
6.431150E-09
8.632258E-06
3.728368E-11
3.777415E-05
7.765760E-10
4.351726E-05
1.024806E-09
5.019194E-05
1.321650E-09
5.498249E-05
1.691511E-09
5.087032E-05
1.548439E-09
4.339702E-05
9.748317E-10
4.487648E-05
1.017509E-09
3.912145E-05
7.781069E-10
3.360131E-05
5.926580E-10
4.181970E-05
9.315176E-10
4.996376E-05
1.271321E-09
6.953297E-05
3.149553E-09
2.341899E-05
2.863270E-10
2.463170E-05
3.817933E-10
4.287745E-05
1.381986E-09
4.863137E-05
1.265768E-09
3.546002E-05
6.388007E-10
2.768687E-05
3.996862E-10
4.620425E-05
1.157612E-09
8.765238E-05
3.949969E-09
9.122226E-06
5.156352E-11
2.923384E-05
4.277534E-10
6.110238E-06
3.733501E-11
0.000000E+00
0.000000E+00
1.145086E-05
8.044829E-11
1.215673E-04
7.922859E-09
8.180970E-05
3.346559E-09
2.228237E-05
2.960078E-10
1.403128E-05
9.848102E-11
3.726259E-05
9.376976E-10
1.324914E-04
8.860330E-09
1.151127E-04
7.050758E-09
1.458422E-04
1.064061E-08
4.638976E-05
1.132911E-09
5.531801E-05
2.260621E-09
8.192886E-05
3.508237E-09
8.582256E-05
3.799113E-09
8.613792E-05
3.771588E-09
5.454735E-05
1.530858E-09
1.063688E-04
6.220167E-09
7.629155E-05
3.048569E-09
1.190013E-04
7.093135E-09
1.578917E-04
1.272315E-08
4.756033E-05
1.614432E-09
3.115637E-05
6.286413E-10
1.021201E-04
5.685811E-09
1.963249E-04
2.098415E-08
3.695671E-05
7.886960E-10
1.118071E-04
6.287210E-09
8.682908E-05
5.090302E-09
1.267453E-04
8.145301E-09
7.582105E-05
2.877459E-09
1.734581E-04
1.695871E-08
1.115115E-04
6.240141E-09
2.008189E-04
2.031997E-08
1.262066E-04
9.106525E-09
1.229430E-04
8.223850E-09
7.226708E-05
2.611301E-09
1.172134E-04
1.086013E-08
1.625937E-04
1.629146E-08
1.533641E-04
1.372814E-08
4.739040E-05
1.128076E-09
1.658554E-04
1.423181E-08
6.559022E-05
2.372207E-09
1.732080E-04
1.517405E-08
1.317351E-04
1.119150E-08
1.457401E-04
1.073529E-08
1.690169E-04
1.466057E-08
2.126906E-04
2.486373E-08
1.434676E-04
1.039533E-08
1.290828E-04
8.377438E-09
1.651708E-04
1.433255E-08
2.955022E-04
4.651016E-08
3.461359E-04
6.077542E-08
9.778295E-05
5.112317E-09
1.720872E-04
1.512272E-08
6.851550E-05
3.140824E-09
1.632821E-04
2.581085E-08
5.732748E-05
1.680111E-09
1.356990E-04
1.313491E-08
7.179892E-05
4.020446E-09
5.265125E-04
2.525299E-07
1.394079E-05
1.943457E-10
1.139166E-04
9.430244E-09
2.050982E-05
2.114840E-10
3.571743E-05
7.843684E-10
7.039875E-06
4.955983E-11
0.000000E+00
0.000000E+00
3.035075E-05
9.211678E-10
4.571644E-04
2.045276E-07
4.586789E-04
2.001054E-07
9.622015E-05
6.642202E-09
4.513091E-05
1.049061E-09
6.177860E-05
2.991654E-09
1.534235E-05
1.441999E-10
4.071311E-05
1.657557E-09
2.162722E-05
4.677365E-10
5.371783E-04
2.709166E-07
5.156053E-05
2.658489E-09
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00
0.000000E+00

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,46 @@
<?xml version="1.0"?>
<tallies>
<filter id="6" type="cell">
<bins>6</bins>
</filter>
<filter id="10" type="particle">
<bins>neutron</bins>
</filter>
<filter id="3" type="energy">
<bins>1.0000E-05 1.0000E-01 4.1399E-01 5.3158E-01 6.8256E-01 8.7643E-01 1.1254E+00 1.4450E+00 1.8554E+00 2.3824E+00
3.0590E+00 3.9279E+00 5.0435E+00 6.4760E+00 8.3153E+00 1.0677E+01 1.3710E+01 1.7604E+01 2.2603E+01 2.9023E+01
3.7267E+01 4.7851E+01 6.1442E+01 7.8893E+01 1.0130E+02 1.3007E+02 1.6702E+02 2.1445E+02 2.7536E+02 3.5358E+02
4.5400E+02 5.8295E+02 7.4852E+02 9.6112E+02 1.2341E+03 1.5846E+03 2.0347E+03 2.2487E+03 2.4852E+03 2.6126E+03
2.7465E+03 3.0354E+03 3.3546E+03 3.7074E+03 4.3074E+03 5.5308E+03 7.1017E+03 9.1188E+03 1.0595E+04 1.1709E+04
1.5034E+04 1.9305E+04 2.1875E+04 2.3579E+04 2.4176E+04 2.4788E+04 2.6058E+04 2.7000E+04 2.8501E+04 3.1828E+04
3.4307E+04 4.0868E+04 4.6309E+04 5.2475E+04 5.6562E+04 6.7380E+04 7.2025E+04 7.9499E+04 8.2503E+04 8.6517E+04
9.8037E+04 1.1109E+05 1.1679E+05 1.2277E+05 1.2907E+05 1.3569E+05 1.4264E+05 1.4996E+05 1.5764E+05 1.6573E+05
1.7422E+05 1.8316E+05 1.9255E+05 2.0242E+05 2.1280E+05 2.2371E+05 2.3518E+05 2.4724E+05 2.7324E+05 2.8725E+05
2.9452E+05 2.9721E+05 2.9849E+05 3.0197E+05 3.3373E+05 3.6883E+05 3.8774E+05 4.0762E+05 4.5049E+05 4.9787E+05
5.2340E+05 5.5023E+05 5.7844E+05 6.0810E+05 6.3928E+05 6.7206E+05 7.0651E+05 7.4274E+05 7.8082E+05 8.2085E+05
8.6294E+05 9.0718E+05 9.6167E+05 1.0026E+06 1.1080E+06 1.1648E+06 1.2246E+06 1.2874E+06 1.3534E+06 1.4227E+06
1.4957E+06 1.5724E+06 1.6530E+06 1.7377E+06 1.8268E+06 1.9205E+06 2.0190E+06 2.1225E+06 2.2313E+06 2.3069E+06
2.3457E+06 2.3653E+06 2.3851E+06 2.4660E+06 2.5924E+06 2.7253E+06 2.8651E+06 3.0119E+06 3.1664E+06 3.3287E+06
3.6788E+06 4.0657E+06 4.4933E+06 4.7237E+06 4.9659E+06 5.2205E+06 5.4881E+06 5.7695E+06 6.0653E+06 6.3763E+06
6.5924E+06 6.7032E+06 7.0469E+06 7.4082E+06 7.7880E+06 8.1873E+06 8.6071E+06 9.0484E+06 9.5123E+06 1.0000E+07
1.0513E+07 1.1052E+07 1.1618E+07 1.2214E+07 1.2523E+07 1.2840E+07 1.3499E+07 1.3840E+07 1.4191E+07 1.4550E+07
1.4918E+07 1.5683E+07 1.6487E+07 1.6905E+07 1.7333E+07 1.9640E+07 2.0000E+07 2.1000E+07 2.2000E+07 2.3000E+07
2.4000E+07 2.5000E+07 2.6000E+07 2.7000E+07 2.8000E+07 2.9000E+07 3.0000E+07 3.1000E+07 3.2000E+07 3.3000E+07
3.4000E+07 3.5000E+07 3.6000E+07 3.7000E+07 3.8000E+07 3.9000E+07 4.0000E+07 4.1000E+07 4.2000E+07 4.3000E+07
4.4000E+07 4.5000E+07 4.6000E+07 4.7000E+07 4.8000E+07 4.9000E+07 5.0000E+07 5.2000E+07 5.4000E+07 5.6000E+07
5.8000E+07 6.0000E+07 6.2000E+07 6.4000E+07 6.6000E+07 6.8000E+07 7.0000E+07 7.2000E+07 7.4000E+07 7.6000E+07
7.8000E+07 8.0000E+07 8.2000E+07 8.4000E+07 8.6000E+07 8.8000E+07 9.0000E+07 9.2000E+07 9.4000E+07 9.6000E+07
9.8000E+07 1.0000E+08 1.0200E+08 1.0400E+08 1.0600E+08 1.0800E+08 1.1000E+08 1.1200E+08 1.1400E+08 1.1600E+08
1.1800E+08 1.2000E+08 1.2200E+08 1.2400E+08 1.2600E+08 1.2800E+08 1.3000E+08 1.3200E+08 1.3400E+08 1.3600E+08
1.3800E+08 1.4000E+08 1.4200E+08 1.4400E+08 1.4600E+08 1.4800E+08 1.5000E+08</bins>
</filter>
<tally id="6">
<filters>6 10 3</filters>
<scores>flux </scores>
</tally>
</tallies>

View file

@ -0,0 +1,6 @@
from tests.testing_harness import TestHarness
def test_weight_window():
harness = TestHarness('statepoint.2.h5')
harness.main()