• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

openmc-dev / openmc / 13710627373

07 Mar 2025 12:00AM UTC coverage: 85.127%. First build
13710627373

push

github

web-flow
Add option for survival biasing source normalization (#3070)

Co-authored-by: Patrick Shriwise <pshriwise@gmail.com>
Co-authored-by: Paul Romano <paul.k.romano@gmail.com>

12 of 18 new or added lines in 5 files covered. (66.67%)

51239 of 60191 relevant lines covered (85.13%)

32493891.2 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.36
/src/physics_mg.cpp
1
#include "openmc/physics_mg.h"
2

3
#include <stdexcept>
4

5
#include "xtensor/xarray.hpp"
6
#include <fmt/core.h>
7

8
#include "openmc/bank.h"
9
#include "openmc/constants.h"
10
#include "openmc/eigenvalue.h"
11
#include "openmc/error.h"
12
#include "openmc/material.h"
13
#include "openmc/math_functions.h"
14
#include "openmc/message_passing.h"
15
#include "openmc/mgxs_interface.h"
16
#include "openmc/particle.h"
17
#include "openmc/physics_common.h"
18
#include "openmc/random_lcg.h"
19
#include "openmc/settings.h"
20
#include "openmc/simulation.h"
21
#include "openmc/tallies/tally.h"
22
#include "openmc/weight_windows.h"
23

24
namespace openmc {
25

26
void collision_mg(Particle& p)
1,785,735,523✔
27
{
28
  // Add to the collision counter for the particle
29
  p.n_collision()++;
1,785,735,523✔
30

31
  // Sample the reaction type
32
  sample_reaction(p);
1,785,735,523✔
33

34
  if (settings::weight_window_checkpoint_collision)
1,785,735,523✔
35
    apply_weight_windows(p);
1,785,735,523✔
36

37
  // Display information about collision
38
  if ((settings::verbosity >= 10) || p.trace()) {
1,785,735,523✔
39
    write_message(fmt::format("    Energy Group = {}", p.g()), 1);
×
40
  }
41
}
1,785,735,523✔
42

43
void sample_reaction(Particle& p)
1,785,735,523✔
44
{
45
  // Create fission bank sites. Note that while a fission reaction is sampled,
46
  // it never actually "happens", i.e. the weight of the particle does not
47
  // change when sampling fission sites. The following block handles all
48
  // absorption (including fission)
49

50
  if (model::materials[p.material()]->fissionable()) {
1,785,735,523✔
51
    if (settings::run_mode == RunMode::EIGENVALUE ||
1,776,459,168✔
52
        (settings::run_mode == RunMode::FIXED_SOURCE &&
×
53
          settings::create_fission_neutrons)) {
54
      create_fission_sites(p);
1,776,459,168✔
55
    }
56
  }
57

58
  // If survival biasing is being used, the following subroutine adjusts the
59
  // weight of the particle. Otherwise, it checks to see if absorption occurs.
60
  if (p.macro_xs().absorption > 0.) {
1,785,735,523✔
61
    absorption(p);
1,785,735,523✔
62
  }
63
  if (!p.alive())
1,785,735,523✔
64
    return;
116,621,527✔
65

66
  // Sample a scattering event to determine the energy of the exiting neutron
67
  scatter(p);
1,669,113,996✔
68

69
  // Play Russian roulette if survival biasing is turned on
70
  if (settings::survival_biasing) {
1,669,113,996✔
71
    // if survival normalization is applicable, use normalized weight cutoff and
72
    // normalized weight survive
73
    if (settings::survival_normalization) {
4,213,044✔
NEW
74
      if (p.wgt() < settings::weight_cutoff * p.wgt_born()) {
×
NEW
75
        russian_roulette(p, settings::weight_survive * p.wgt_born());
×
76
      }
77
    } else if (p.wgt() < settings::weight_cutoff) {
4,213,044✔
78
      russian_roulette(p, settings::weight_survive);
142,197✔
79
    }
80
  }
81
}
82

83
void scatter(Particle& p)
1,669,113,996✔
84
{
85
  data::mg.macro_xs_[p.material()].sample_scatter(p.g_last(), p.g(), p.mu(),
2,147,483,647✔
86
    p.wgt(), p.current_seed(), p.mg_xs_cache().t, p.mg_xs_cache().a);
1,669,113,996✔
87

88
  // Rotate the angle
89
  p.u() = rotate_angle(p.u(), p.mu(), nullptr, p.current_seed());
1,669,113,996✔
90

91
  // Update energy value for downstream compatability (in tallying)
92
  p.E() = data::mg.energy_bin_avg_[p.g()];
1,669,113,996✔
93

94
  // Set event component
95
  p.event() = TallyEvent::SCATTER;
1,669,113,996✔
96
}
1,669,113,996✔
97

98
void create_fission_sites(Particle& p)
1,776,459,168✔
99
{
100
  // If uniform fission source weighting is turned on, we increase or decrease
101
  // the expected number of fission sites produced
102
  double weight = settings::ufs_on ? ufs_get_weight(p) : 1.0;
1,776,459,168✔
103

104
  // Determine the expected number of neutrons produced
105
  double nu_t = p.wgt() / simulation::keff * weight * p.macro_xs().nu_fission /
1,776,459,168✔
106
                p.macro_xs().total;
1,776,459,168✔
107

108
  // Sample the number of neutrons produced
109
  int nu = static_cast<int>(nu_t);
1,776,459,168✔
110
  if (prn(p.current_seed()) <= (nu_t - int(nu_t))) {
1,776,459,168✔
111
    nu++;
111,052,953✔
112
  }
113

114
  // If no neutrons were produced then don't continue
115
  if (nu == 0)
1,776,459,168✔
116
    return;
1,665,405,489✔
117

118
  // Initialize the counter of delayed neutrons encountered for each delayed
119
  // group.
120
  double nu_d[MAX_DELAYED_GROUPS] = {0.};
111,053,679✔
121

122
  // Clear out particle's nu fission bank
123
  p.nu_bank().clear();
111,053,679✔
124

125
  p.fission() = true;
111,053,679✔
126

127
  // Determine whether to place fission sites into the shared fission bank
128
  // or the secondary particle bank.
129
  bool use_fission_bank = (settings::run_mode == RunMode::EIGENVALUE);
111,053,679✔
130

131
  // Counter for the number of fission sites successfully stored to the shared
132
  // fission bank or the secondary particle bank
133
  int n_sites_stored;
134

135
  for (n_sites_stored = 0; n_sites_stored < nu; n_sites_stored++) {
222,108,832✔
136
    // Initialize fission site object with particle data
137
    SourceSite site;
111,055,153✔
138
    site.r = p.r();
111,055,153✔
139
    site.particle = ParticleType::neutron;
111,055,153✔
140
    site.wgt = 1. / weight;
111,055,153✔
141
    site.parent_id = p.id();
111,055,153✔
142
    site.progeny_id = p.n_progeny()++;
111,055,153✔
143

144
    // Sample the cosine of the angle, assuming fission neutrons are emitted
145
    // isotropically
146
    double mu = 2. * prn(p.current_seed()) - 1.;
111,055,153✔
147

148
    // Sample the azimuthal angle uniformly in [0, 2.pi)
149
    double phi = 2. * PI * prn(p.current_seed());
111,055,153✔
150
    site.u.x = mu;
111,055,153✔
151
    site.u.y = std::sqrt(1. - mu * mu) * std::cos(phi);
111,055,153✔
152
    site.u.z = std::sqrt(1. - mu * mu) * std::sin(phi);
111,055,153✔
153

154
    // Sample secondary energy distribution for the fission reaction
155
    int dg;
156
    int gout;
157
    data::mg.macro_xs_[p.material()].sample_fission_energy(
333,165,459✔
158
      p.g(), dg, gout, p.current_seed(), p.mg_xs_cache().t, p.mg_xs_cache().a);
222,110,306✔
159

160
    // Store the energy and delayed groups on the fission bank
161
    site.E = gout;
111,055,153✔
162

163
    // We add 1 to the delayed_group bc in MG, -1 is prompt, but in the rest
164
    // of the code, 0 is prompt.
165
    site.delayed_group = dg + 1;
111,055,153✔
166

167
    // Store fission site in bank
168
    if (use_fission_bank) {
111,055,153✔
169
      int64_t idx = simulation::fission_bank.thread_safe_append(site);
111,055,153✔
170
      if (idx == -1) {
111,055,153✔
171
        warning(
×
172
          "The shared fission bank is full. Additional fission sites created "
173
          "in this generation will not be banked. Results may be "
174
          "non-deterministic.");
175

176
        // Decrement number of particle progeny as storage was unsuccessful.
177
        // This step is needed so that the sum of all progeny is equal to the
178
        // size of the shared fission bank.
179
        p.n_progeny()--;
×
180

181
        // Break out of loop as no more sites can be added to fission bank
182
        break;
×
183
      }
184
    } else {
185
      p.secondary_bank().push_back(site);
×
186
    }
187

188
    // Set the delayed group on the particle as well
189
    p.delayed_group() = dg + 1;
111,055,153✔
190

191
    // Increment the number of neutrons born delayed
192
    if (p.delayed_group() > 0) {
111,055,153✔
193
      nu_d[dg]++;
1,540✔
194
    }
195

196
    // Write fission particles to nuBank
197
    NuBank& nu_bank_entry = p.nu_bank().emplace_back();
111,055,153✔
198
    nu_bank_entry.wgt = site.wgt;
111,055,153✔
199
    nu_bank_entry.E = site.E;
111,055,153✔
200
    nu_bank_entry.delayed_group = site.delayed_group;
111,055,153✔
201
  }
202

203
  // If shared fission bank was full, and no fissions could be added,
204
  // set the particle fission flag to false.
205
  if (n_sites_stored == 0) {
111,053,679✔
206
    p.fission() = false;
×
207
    return;
×
208
  }
209

210
  // Set nu to the number of fission sites successfully stored. If the fission
211
  // bank was not found to be full then these values are already equivalent.
212
  nu = n_sites_stored;
111,053,679✔
213

214
  // Store the total weight banked for analog fission tallies
215
  p.n_bank() = nu;
111,053,679✔
216
  p.wgt_bank() = nu / weight;
111,053,679✔
217
  for (size_t d = 0; d < MAX_DELAYED_GROUPS; d++) {
999,483,111✔
218
    p.n_delayed_bank(d) = nu_d[d];
888,429,432✔
219
  }
220
}
221

222
void absorption(Particle& p)
1,785,735,523✔
223
{
224
  if (settings::survival_biasing) {
1,785,735,523✔
225
    // Determine weight absorbed in survival biasing
226
    double wgt_absorb = p.wgt() * p.macro_xs().absorption / p.macro_xs().total;
4,213,044✔
227

228
    // Adjust weight of particle by the probability of absorption
229
    p.wgt() -= wgt_absorb;
4,213,044✔
230

231
    // Score implicit absorpion estimate of keff
232
    p.keff_tally_absorption() +=
4,213,044✔
233
      wgt_absorb * p.macro_xs().nu_fission / p.macro_xs().absorption;
4,213,044✔
234
  } else {
235
    if (p.macro_xs().absorption > prn(p.current_seed()) * p.macro_xs().total) {
1,781,522,479✔
236
      p.keff_tally_absorption() +=
233,243,054✔
237
        p.wgt() * p.macro_xs().nu_fission / p.macro_xs().absorption;
116,621,527✔
238
      p.wgt() = 0.0;
116,621,527✔
239
      p.event() = TallyEvent::ABSORB;
116,621,527✔
240
    }
241
  }
242
}
1,785,735,523✔
243

244
} // namespace openmc
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc