Skip to content

Commit e8c9134

Browse files
yrrepypshriwisepaulromano
authored
Add option for survival biasing source normalization (#3070)
Co-authored-by: Patrick Shriwise <[email protected]> Co-authored-by: Paul Romano <[email protected]>
1 parent 9bfce4e commit e8c9134

File tree

9 files changed

+76
-26
lines changed

9 files changed

+76
-26
lines changed

docs/source/io_formats/settings.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,13 @@ time.
8181

8282
*Default*: 1.0
8383

84+
:survival_normalization:
85+
If this element is set to "true", this will enable the use of survival
86+
biasing source normalization, whereby the weight parameters, weight and
87+
weight_avg, are multiplied per history by the start weight of said history.
88+
89+
*Default*: false
90+
8491
:energy_neutron:
8592
The energy under which neutrons will be killed.
8693

include/openmc/particle_data.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,7 @@ class ParticleData : public GeometryState {
434434
int g_last_;
435435

436436
double wgt_ {1.0};
437+
double wgt_born_ {1.0};
437438
double mu_;
438439
double time_ {0.0};
439440
double time_last_ {0.0};
@@ -532,12 +533,20 @@ class ParticleData : public GeometryState {
532533
int& g_last() { return g_last_; }
533534
const int& g_last() const { return g_last_; }
534535

535-
// Statistic weight of particle. Setting to zero
536-
// indicates that the particle is dead.
536+
// Statistic weight of particle. Setting to zero indicates that the particle
537+
// is dead.
537538
double& wgt() { return wgt_; }
538539
double wgt() const { return wgt_; }
540+
541+
// Statistic weight of particle at birth
542+
double& wgt_born() { return wgt_born_; }
543+
double wgt_born() const { return wgt_born_; }
544+
545+
// Statistic weight of particle at last collision
539546
double& wgt_last() { return wgt_last_; }
540547
const double& wgt_last() const { return wgt_last_; }
548+
549+
// Whether particle is alive
541550
bool alive() const { return wgt_ != 0.0; }
542551

543552
// Polar scattering angle after a collision

include/openmc/settings.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ extern bool surf_source_write; //!< write surface source file?
6161
extern bool surf_mcpl_write; //!< write surface mcpl file?
6262
extern bool surf_source_read; //!< read surface source file?
6363
extern bool survival_biasing; //!< use survival biasing?
64+
extern bool survival_normalization; //!< use survival normalization?
6465
extern bool temperature_multipole; //!< use multipole data?
6566
extern "C" bool trigger_on; //!< tally triggers enabled?
6667
extern bool trigger_predict; //!< predict batches for triggers?

openmc/settings.py

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -50,15 +50,19 @@ class Settings:
5050
Indicate whether fission neutrons should be created or not.
5151
cutoff : dict
5252
Dictionary defining weight cutoff, energy cutoff and time cutoff. The
53-
dictionary may have ten keys, 'weight', 'weight_avg', 'energy_neutron',
54-
'energy_photon', 'energy_electron', 'energy_positron', 'time_neutron',
55-
'time_photon', 'time_electron', and 'time_positron'. Value for 'weight'
56-
should be a float indicating weight cutoff below which particle undergo
57-
Russian roulette. Value for 'weight_avg' should be a float indicating
58-
weight assigned to particles that are not killed after Russian roulette.
59-
Value of energy should be a float indicating energy in eV below which
60-
particle type will be killed. Value of time should be a float in
61-
seconds. Particles will be killed exactly at the specified time.
53+
dictionary may have the following keys, 'weight', 'weight_avg',
54+
'survival_normalization', 'energy_neutron', 'energy_photon',
55+
'energy_electron', 'energy_positron', 'time_neutron', 'time_photon',
56+
'time_electron', and 'time_positron'. Value for 'weight' should be a
57+
float indicating weight cutoff below which particle undergo Russian
58+
roulette. Value for 'weight_avg' should be a float indicating weight
59+
assigned to particles that are not killed after Russian roulette. Value
60+
of energy should be a float indicating energy in eV below which particle
61+
type will be killed. Value of time should be a float in seconds.
62+
Particles will be killed exactly at the specified time. Value for
63+
'survival_normalization' is a bool indicating whether or not the weight
64+
cutoff parameters will be applied relative to the particle's starting
65+
weight or to its current weight.
6266
delayed_photon_scaling : bool
6367
Indicate whether to scale the fission photon yield by (EGP + EGD)/EGP
6468
where EGP is the energy release of prompt photons and EGD is the energy
@@ -162,20 +166,20 @@ class Settings:
162166
'naive', 'simulation_averaged', or 'hybrid'.
163167
The default is 'hybrid'.
164168
:source_shape:
165-
Assumed shape of the source distribution within each source
166-
region. Options are 'flat' (default), 'linear', or 'linear_xy'.
169+
Assumed shape of the source distribution within each source region.
170+
Options are 'flat' (default), 'linear', or 'linear_xy'.
167171
:volume_normalized_flux_tallies:
168-
Whether to normalize flux tallies by volume (bool). The default
169-
is 'False'. When enabled, flux tallies will be reported in units of
170-
cm/cm^3. When disabled, flux tallies will be reported in units
171-
of cm (i.e., total distance traveled by neutrons in the spatial
172-
tally region).
172+
Whether to normalize flux tallies by volume (bool). The default is
173+
'False'. When enabled, flux tallies will be reported in units of
174+
cm/cm^3. When disabled, flux tallies will be reported in units of cm
175+
(i.e., total distance traveled by neutrons in the spatial tally
176+
region).
173177
:adjoint:
174178
Whether to run the random ray solver in adjoint mode (bool). The
175179
default is 'False'.
176180
:sample_method:
177-
Sampling method for the ray starting location and direction of travel.
178-
Options are `prng` (default) or 'halton`.
181+
Sampling method for the ray starting location and direction of
182+
travel. Options are `prng` (default) or 'halton`.
179183
180184
.. versionadded:: 0.15.0
181185
resonance_scattering : dict
@@ -899,6 +903,8 @@ def cutoff(self, cutoff: dict):
899903
cv.check_type('average survival weight', cutoff[key], Real)
900904
cv.check_greater_than('average survival weight',
901905
cutoff[key], 0.0)
906+
elif key == 'survival_normalization':
907+
cv.check_type('survival normalization', cutoff[key], bool)
902908
elif key in ['energy_neutron', 'energy_photon', 'energy_electron',
903909
'energy_positron']:
904910
cv.check_type('energy cutoff', cutoff[key], Real)
@@ -1364,7 +1370,8 @@ def _create_cutoff_subelement(self, root):
13641370
element = ET.SubElement(root, "cutoff")
13651371
for key, value in self._cutoff.items():
13661372
subelement = ET.SubElement(element, key)
1367-
subelement.text = str(value)
1373+
subelement.text = str(value) if key != 'survival_normalization' \
1374+
else str(value).lower()
13681375

13691376
def _create_entropy_mesh_subelement(self, root, mesh_memo=None):
13701377
if self.entropy_mesh is None:
@@ -1797,10 +1804,14 @@ def _cutoff_from_xml_element(self, root):
17971804
self.cutoff = {}
17981805
for key in ('energy_neutron', 'energy_photon', 'energy_electron',
17991806
'energy_positron', 'weight', 'weight_avg', 'time_neutron',
1800-
'time_photon', 'time_electron', 'time_positron'):
1807+
'time_photon', 'time_electron', 'time_positron',
1808+
'survival_normalization'):
18011809
value = get_text(elem, key)
18021810
if value is not None:
1803-
self.cutoff[key] = float(value)
1811+
if key == 'survival_normalization':
1812+
self.cutoff[key] = value in ('true', '1')
1813+
else:
1814+
self.cutoff[key] = float(value)
18041815

18051816
def _entropy_mesh_from_xml_element(self, root, meshes):
18061817
text = get_text(root, 'entropy_mesh')

src/physics.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,13 @@ void sample_neutron_reaction(Particle& p)
153153

154154
// Play russian roulette if survival biasing is turned on
155155
if (settings::survival_biasing) {
156-
if (p.wgt() < settings::weight_cutoff) {
156+
// if survival normalization is on, use normalized weight cutoff and
157+
// normalized weight survive
158+
if (settings::survival_normalization) {
159+
if (p.wgt() < settings::weight_cutoff * p.wgt_born()) {
160+
russian_roulette(p, settings::weight_survive * p.wgt_born());
161+
}
162+
} else if (p.wgt() < settings::weight_cutoff) {
157163
russian_roulette(p, settings::weight_survive);
158164
}
159165
}

src/physics_mg.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,13 @@ void sample_reaction(Particle& p)
6868

6969
// Play Russian roulette if survival biasing is turned on
7070
if (settings::survival_biasing) {
71-
if (p.wgt() < settings::weight_cutoff) {
71+
// if survival normalization is applicable, use normalized weight cutoff and
72+
// normalized weight survive
73+
if (settings::survival_normalization) {
74+
if (p.wgt() < settings::weight_cutoff * p.wgt_born()) {
75+
russian_roulette(p, settings::weight_survive * p.wgt_born());
76+
}
77+
} else if (p.wgt() < settings::weight_cutoff) {
7278
russian_roulette(p, settings::weight_survive);
7379
}
7480
}

src/settings.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ bool surf_source_write {false};
6969
bool surf_mcpl_write {false};
7070
bool surf_source_read {false};
7171
bool survival_biasing {false};
72+
bool survival_normalization {false};
7273
bool temperature_multipole {false};
7374
bool trigger_on {false};
7475
bool trigger_predict {false};
@@ -624,6 +625,10 @@ void read_settings_xml(pugi::xml_node root)
624625
if (check_for_node(node_cutoff, "weight_avg")) {
625626
weight_survive = std::stod(get_node_value(node_cutoff, "weight_avg"));
626627
}
628+
if (check_for_node(node_cutoff, "survival_normalization")) {
629+
survival_normalization =
630+
get_node_value_bool(node_cutoff, "survival_normalization");
631+
}
627632
if (check_for_node(node_cutoff, "energy_neutron")) {
628633
energy_cutoff[0] =
629634
std::stod(get_node_value(node_cutoff, "energy_neutron"));

src/simulation.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,9 @@ void initialize_history(Particle& p, int64_t index_source)
586586
// Reset weight window ratio
587587
p.ww_factor() = 0.0;
588588

589+
// set particle history start weight
590+
p.wgt_born() = p.wgt();
591+
589592
// Reset pulse_height_storage
590593
std::fill(p.pht_storage().begin(), p.pht_storage().end(), 0);
591594

@@ -610,7 +613,7 @@ void initialize_history(Particle& p, int64_t index_source)
610613
write_message("Simulating Particle {}", p.id());
611614
}
612615

613-
// Add paricle's starting weight to count for normalizing tallies later
616+
// Add particle's starting weight to count for normalizing tallies later
614617
#pragma omp atomic
615618
simulation::total_weight += p.wgt();
616619

tests/unit_tests/test_settings.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ def test_export_to_xml(run_in_tmpdir):
2626
s.plot_seed = 100
2727
s.survival_biasing = True
2828
s.cutoff = {'weight': 0.25, 'weight_avg': 0.5, 'energy_neutron': 1.0e-5,
29+
'survival_normalization': True,
2930
'energy_photon': 1000.0, 'energy_electron': 1.0e-5,
3031
'energy_positron': 1.0e-5, 'time_neutron': 1.0e-5,
3132
'time_photon': 1.0e-5, 'time_electron': 1.0e-5,
@@ -99,6 +100,7 @@ def test_export_to_xml(run_in_tmpdir):
99100
assert s.seed == 17
100101
assert s.survival_biasing
101102
assert s.cutoff == {'weight': 0.25, 'weight_avg': 0.5,
103+
'survival_normalization': True,
102104
'energy_neutron': 1.0e-5, 'energy_photon': 1000.0,
103105
'energy_electron': 1.0e-5, 'energy_positron': 1.0e-5,
104106
'time_neutron': 1.0e-5, 'time_photon': 1.0e-5,

0 commit comments

Comments
 (0)