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

openmc-dev / openmc / 20824991646

08 Jan 2026 05:03PM UTC coverage: 82.033% (-0.2%) from 82.195%
20824991646

Pull #3460

github

web-flow
Merge 05289af22 into dfc80c706
Pull Request #3460: Source biasing capabilities

17178 of 23851 branches covered (72.02%)

Branch coverage included in aggregate %.

536 of 720 new or added lines in 12 files covered. (74.44%)

117 existing lines in 5 files now uncovered.

55664 of 64945 relevant lines covered (85.71%)

43069601.84 hits per line

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

75.13
/src/distribution.cpp
1
#include "openmc/distribution.h"
2

3
#include <algorithm> // for copy
4
#include <array>
5
#include <cmath>     // for sqrt, floor, max
6
#include <iterator>  // for back_inserter
7
#include <numeric>   // for accumulate
8
#include <stdexcept> // for runtime_error
9
#include <string>    // for string, stod
10

11
#include "openmc/constants.h"
12
#include "openmc/error.h"
13
#include "openmc/math_functions.h"
14
#include "openmc/random_dist.h"
15
#include "openmc/random_lcg.h"
16
#include "openmc/xml_interface.h"
17

18
namespace openmc {
19

20
std::pair<double, double> Distribution::sample(uint64_t* seed) const
707,657,785✔
21
{
22
  if (bias_) {
707,657,785✔
23
    // Sample from the bias distribution and compute importance weight
24
    double val = bias_->sample_unbiased(seed);
715,331✔
25
    double wgt = this->evaluate(val) / bias_->evaluate(val);
715,331✔
26
    return {val, wgt};
715,331✔
27
  } else {
28
    // Unbiased sampling: return sampled value with weight 1.0
29
    double val = sample_unbiased(seed);
706,942,454✔
30
    return {val, 1.0};
706,942,454✔
31
  }
32
}
33

34
// PDF evaluation not supported for all distribution types
NEW
35
double Distribution::evaluate(double x) const
×
36
{
NEW
37
  throw std::runtime_error(
×
NEW
38
    "PDF evaluation not implemented for this distribution type.");
×
39
}
40

41
//==============================================================================
42
// DiscreteIndex implementation
43
//==============================================================================
44

45
DiscreteIndex::DiscreteIndex(pugi::xml_node node)
26,945✔
46
{
47
  auto params = get_node_array<double>(node, "parameters");
26,945✔
48
  std::size_t n = params.size() / 2;
26,945✔
49

50
  assign({params.data() + n, n});
26,945✔
51
}
26,945✔
52

53
DiscreteIndex::DiscreteIndex(span<const double> p)
47,163✔
54
{
55
  assign(p);
47,163✔
56
}
47,163✔
57

58
void DiscreteIndex::assign(span<const double> p)
82,375✔
59
{
60
  prob_.assign(p.begin(), p.end());
82,375✔
61

62
  this->init_alias();
82,375✔
63
  this->init_wgt();
82,375✔
64
}
82,375✔
65

66
void DiscreteIndex::init_alias()
82,386✔
67
{
68
  normalize();
82,386✔
69

70
  // Save normalized probabilities before Vose algorithm modifies prob_
71
  prob_norm_ = prob_;
82,386✔
72

73
  // The initialization and sampling method is based on Vose
74
  // (DOI: 10.1109/32.92917)
75
  // Vectors for large and small probabilities based on 1/n
76
  vector<size_t> large;
82,386✔
77
  vector<size_t> small;
82,386✔
78

79
  size_t n = prob_.size();
82,386✔
80

81
  // Set and allocate memory
82
  alias_.assign(n, 0);
82,386✔
83

84
  // Fill large and small vectors based on 1/n
85
  for (size_t i = 0; i < n; i++) {
1,607,068✔
86
    prob_[i] *= n;
1,524,682✔
87
    if (prob_[i] > 1.0) {
1,524,682✔
88
      large.push_back(i);
228,225✔
89
    } else {
90
      small.push_back(i);
1,296,457✔
91
    }
92
  }
93

94
  while (!large.empty() && !small.empty()) {
1,450,165✔
95
    int j = small.back();
1,367,779✔
96
    int k = large.back();
1,367,779✔
97

98
    // Remove last element of small
99
    small.pop_back();
1,367,779✔
100

101
    // Update probability and alias based on Vose's algorithm
102
    prob_[k] += prob_[j] - 1.0;
1,367,779✔
103
    alias_[j] = k;
1,367,779✔
104

105
    // Move large index to small vector, if it is no longer large
106
    if (prob_[k] < 1.0) {
1,367,779✔
107
      small.push_back(k);
219,964✔
108
      large.pop_back();
219,964✔
109
    }
110
  }
111
}
82,386✔
112

113
void DiscreteIndex::init_wgt()
82,375✔
114
{
115
  wgt_.assign(prob_.size(), 1.0);
82,375✔
116
}
82,375✔
117

118
size_t DiscreteIndex::sample(uint64_t* seed) const
66,528,876✔
119
{
120
  // Alias sampling of discrete distribution
121
  size_t n = prob_.size();
66,528,876✔
122
  if (n > 1) {
66,528,876✔
123
    size_t u = prn(seed) * n;
16,166,296✔
124
    if (prn(seed) < prob_[u]) {
16,166,296✔
125
      return u;
9,650,102✔
126
    } else {
127
      return alias_[u];
6,516,194✔
128
    }
129
  } else {
130
    return 0;
50,362,580✔
131
  }
132
}
133

134
void DiscreteIndex::normalize()
82,397✔
135
{
136
  // Renormalize density function so that it sums to unity. Note that we save
137
  // the integral of the distribution so that if it is used as part of another
138
  // distribution (e.g., Mixture), we know its relative strength.
139
  integral_ = std::accumulate(prob_.begin(), prob_.end(), 0.0);
82,397✔
140
  for (auto& p_i : prob_) {
1,607,112✔
141
    p_i /= integral_;
1,524,715✔
142
  }
143
}
82,397✔
144

145
void DiscreteIndex::apply_bias(span<const double> b)
11✔
146
{
147
  // Replace the probability vector with that from the bias distribution.
148
  prob_.assign(b.begin(), b.end());
11✔
149
  if (prob_.size() != prob_norm_.size()) {
11!
NEW
150
    openmc::fatal_error(
×
NEW
151
      "Size mismatch: Attempted to bias Discrete distribution with " +
×
NEW
152
      std::to_string(prob_norm_.size()) +
×
153
      " probability entries using a "
NEW
154
      "Discrete distribution with " +
×
NEW
155
      std::to_string(prob_.size()) +
×
156
      " entries. Please ensure distributions have the same size.");
157
  }
158

159
  // Normalize biased probability vector and populate weight table.
160
  normalize();
11✔
161
  for (std::size_t i = 0; i < wgt_.size(); ++i) {
44✔
162
    if (prob_[i] == 0.0) {
33!
163
      // Allow nonzero entries in original distribution to be given zero
164
      // sampling probability in the biased distribution.
NEW
165
      wgt_[i] = INFTY;
×
166
    } else {
167
      wgt_[i] = prob_norm_[i] / prob_[i];
33✔
168
    }
169
  }
170

171
  // Reconstruct alias table for sampling from the biased distribution.
172
  // Values from unbiased prob_actual_ may be recovered using weight table.
173
  this->init_alias();
11✔
174
}
11✔
175

176
//==============================================================================
177
// Discrete implementation
178
//==============================================================================
179

180
Discrete::Discrete(pugi::xml_node node) : di_(node)
26,945✔
181
{
182
  auto params = get_node_array<double>(node, "parameters");
26,945✔
183

184
  std::size_t n = params.size() / 2;
26,945✔
185

186
  x_.assign(params.begin(), params.begin() + n);
26,945✔
187
}
26,945✔
188

189
Discrete::Discrete(const double* x, const double* p, size_t n) : di_({p, n})
47,163✔
190
{
191

192
  x_.assign(x, x + n);
47,163✔
193
}
47,163✔
194

195
std::pair<double, double> Discrete::sample(uint64_t* seed) const
61,550,063✔
196
{
197
  size_t sample_index = di_.sample(seed);
61,550,063✔
198
  return {x_[sample_index], di_.weight()[sample_index]};
61,550,063✔
199
}
200

NEW
201
double Discrete::sample_unbiased(uint64_t* seed) const
×
202
{
203
  // Discrete uses internal alias-table biasing for weighted sampling,
204
  // but unbiased sampling should return only the value.
NEW
205
  size_t sample_index = di_.sample(seed);
×
NEW
206
  return x_[sample_index];
×
207
}
208

NEW
209
double Discrete::evaluate(double x) const
×
210
{
211
  // This function is not called when sampling from a Discrete distribution,
212
  // even if it is biased. This is because Discrete distributions may only
213
  // be biased by another Discrete distribution. It is only called when a
214
  // Discrete distribution is used to bias another kind of distribution.
NEW
215
  for (size_t i = 0; i < x_.size(); ++i) {
×
NEW
216
    if (std::fabs(x_[i] - x) <= FP_PRECISION) {
×
217
      // Return the normalized probability (before Vose transformation)
NEW
218
      return di_.prob_norm()[i];
×
219
    }
220
  }
NEW
221
  return 0.0;
×
222
}
223

224
void Discrete::set_bias_discrete(pugi::xml_node node)
11✔
225
{
226
  // Takes the probability vector from a bias distribution and applies it to
227
  // the existing DiscreteIndex.
228
  auto bias_params = get_node_array<double>(node, "bias");
11✔
229

230
  di_.apply_bias(bias_params);
11✔
231
}
11✔
232

233
//==============================================================================
234
// Uniform implementation
235
//==============================================================================
236

237
Uniform::Uniform(pugi::xml_node node)
344✔
238
{
239
  auto params = get_node_array<double>(node, "parameters");
344✔
240
  if (params.size() != 2) {
344!
241
    fatal_error("Uniform distribution must have two "
×
242
                "parameters specified.");
243
  }
244

245
  a_ = params.at(0);
344✔
246
  b_ = params.at(1);
344✔
247
}
344✔
248

249
double Uniform::sample_unbiased(uint64_t* seed) const
780,544✔
250
{
251
  return a_ + prn(seed) * (b_ - a_);
780,544✔
252
}
253

254
double Uniform::evaluate(double x) const
385,331✔
255
{
256
  if (x <= a()) {
385,331!
NEW
257
    return 0.0;
×
258
  } else if (x >= b()) {
385,331!
NEW
259
    return 0.0;
×
260
  } else {
261
    return 1 / (b() - a());
385,331✔
262
  }
263
}
264

265
//==============================================================================
266
// PowerLaw implementation
267
//==============================================================================
268

269
PowerLaw::PowerLaw(pugi::xml_node node)
76✔
270
{
271
  auto params = get_node_array<double>(node, "parameters");
76✔
272
  if (params.size() != 3) {
76!
273
    fatal_error("PowerLaw distribution must have three "
×
274
                "parameters specified.");
275
  }
276

277
  const double a = params.at(0);
76✔
278
  const double b = params.at(1);
76✔
279
  const double n = params.at(2);
76✔
280

281
  offset_ = std::pow(a, n + 1);
76✔
282
  span_ = std::pow(b, n + 1) - offset_;
76✔
283
  ninv_ = 1 / (n + 1);
76✔
284
}
76✔
285

286
double PowerLaw::evaluate(double x) const
275,331✔
287
{
288
  if (x <= a()) {
275,331!
NEW
289
    return 0.0;
×
290
  } else if (x >= b()) {
275,331!
NEW
291
    return 0.0;
×
292
  } else {
293
    int pwr = n() + 1;
275,331✔
294
    double norm = pwr / span_;
275,331✔
295
    return norm * std::pow(std::fabs(x), n());
275,331✔
296
  }
297
}
298

299
double PowerLaw::sample_unbiased(uint64_t* seed) const
277,553✔
300
{
301
  return std::pow(offset_ + prn(seed) * span_, ninv_);
277,553✔
302
}
303

304
//==============================================================================
305
// Maxwell implementation
306
//==============================================================================
307

308
Maxwell::Maxwell(pugi::xml_node node)
97✔
309
{
310
  theta_ = std::stod(get_node_value(node, "parameters"));
97✔
311
}
97✔
312

313
double Maxwell::sample_unbiased(uint64_t* seed) const
223,872✔
314
{
315
  return maxwell_spectrum(theta_, seed);
223,872✔
316
}
317

318
double Maxwell::evaluate(double x) const
220,000✔
319
{
320
  double c = (2.0 / SQRT_PI) * std::pow(theta_, -1.5);
220,000✔
321
  return c * std::sqrt(x) * std::exp(-x / theta_);
220,000✔
322
}
323

324
//==============================================================================
325
// Watt implementation
326
//==============================================================================
327

328
Watt::Watt(pugi::xml_node node)
129✔
329
{
330
  auto params = get_node_array<double>(node, "parameters");
129✔
331
  if (params.size() != 2)
129!
332
    openmc::fatal_error("Watt energy distribution must have two "
×
333
                        "parameters specified.");
334

335
  a_ = params.at(0);
129✔
336
  b_ = params.at(1);
129✔
337
}
129✔
338

339
double Watt::sample_unbiased(uint64_t* seed) const
12,548,201✔
340
{
341
  return watt_spectrum(a_, b_, seed);
12,548,201✔
342
}
343

344
double Watt::evaluate(double x) const
220,000✔
345
{
346
  double c =
347
    2.0 / (std::sqrt(PI * b_) * std::pow(a_, 1.5) * std::exp(a_ * b_ / 4.0));
220,000✔
348
  return c * std::exp(-x / a_) * std::sinh(std::sqrt(b_ * x));
220,000✔
349
}
350

351
//==============================================================================
352
// Normal implementation
353
//==============================================================================
354
Normal::Normal(pugi::xml_node node)
33✔
355
{
356
  auto params = get_node_array<double>(node, "parameters");
33✔
357
  if (params.size() != 2) {
33!
358
    openmc::fatal_error("Normal energy distribution must have two "
×
359
                        "parameters specified.");
360
  }
361

362
  mean_value_ = params.at(0);
33✔
363
  std_dev_ = params.at(1);
33✔
364
}
33✔
365

366
double Normal::sample_unbiased(uint64_t* seed) const
220,000✔
367
{
368
  return normal_variate(mean_value_, std_dev_, seed);
220,000✔
369
}
370

371
double Normal::evaluate(double x) const
220,000✔
372
{
373
  return (1.0 / (std::sqrt(2.0 / PI) * std_dev_)) *
220,000✔
374
         std::exp(-(std::pow((x - mean_value_), 2.0)) /
220,000✔
375
                  (2.0 * std::pow(std_dev_, 2.0)));
220,000✔
376
}
377

378
//==============================================================================
379
// Tabular implementation
380
//==============================================================================
381

382
Tabular::Tabular(pugi::xml_node node)
7,637✔
383
{
384
  if (check_for_node(node, "interpolation")) {
7,637!
385
    std::string temp = get_node_value(node, "interpolation");
7,637✔
386
    if (temp == "histogram") {
7,637✔
387
      interp_ = Interpolation::histogram;
7,551✔
388
    } else if (temp == "linear-linear") {
86!
389
      interp_ = Interpolation::lin_lin;
86✔
390
    } else {
391
      openmc::fatal_error(
×
392
        "Unsupported interpolation type for distribution: " + temp);
×
393
    }
394
  } else {
7,637✔
395
    interp_ = Interpolation::histogram;
×
396
  }
397

398
  // Read and initialize tabular distribution. If number of parameters is odd,
399
  // add an extra zero for the 'p' array.
400
  auto params = get_node_array<double>(node, "parameters");
7,637✔
401
  if (params.size() % 2 != 0) {
7,637!
402
    params.push_back(0.0);
×
403
  }
404
  std::size_t n = params.size() / 2;
7,637✔
405
  const double* x = params.data();
7,637✔
406
  const double* p = x + n;
7,637✔
407
  init(x, p, n);
7,637✔
408
}
7,637✔
409

410
Tabular::Tabular(const double* x, const double* p, int n, Interpolation interp,
46,581,537✔
411
  const double* c)
46,581,537✔
412
  : interp_ {interp}
46,581,537✔
413
{
414
  init(x, p, n, c);
46,581,537✔
415
}
46,581,537✔
416

417
void Tabular::init(
46,589,174✔
418
  const double* x, const double* p, std::size_t n, const double* c)
419
{
420
  // Copy x/p arrays into vectors
421
  std::copy(x, x + n, std::back_inserter(x_));
46,589,174✔
422
  std::copy(p, p + n, std::back_inserter(p_));
46,589,174✔
423

424
  // Check interpolation parameter
425
  if (interp_ != Interpolation::histogram &&
46,589,174✔
426
      interp_ != Interpolation::lin_lin) {
38,834,635!
427
    openmc::fatal_error("Only histogram and linear-linear interpolation "
×
428
                        "for tabular distribution is supported.");
429
  }
430

431
  // Calculate cumulative distribution function
432
  if (c) {
46,589,174✔
433
    std::copy(c, c + n, std::back_inserter(c_));
46,581,537✔
434
  } else {
435
    c_.resize(n);
7,637✔
436
    c_[0] = 0.0;
7,637✔
437
    for (int i = 1; i < n; ++i) {
94,540✔
438
      if (interp_ == Interpolation::histogram) {
86,903✔
439
        c_[i] = c_[i - 1] + p_[i - 1] * (x_[i] - x_[i - 1]);
86,709✔
440
      } else if (interp_ == Interpolation::lin_lin) {
194!
441
        c_[i] = c_[i - 1] + 0.5 * (p_[i - 1] + p_[i]) * (x_[i] - x_[i - 1]);
194✔
442
      }
443
    }
444
  }
445

446
  // Normalize density and distribution functions. Note that we save the
447
  // integral of the distribution so that if it is used as part of another
448
  // distribution (e.g., Mixture), we know its relative strength.
449
  integral_ = c_[n - 1];
46,589,174✔
450
  for (int i = 0; i < n; ++i) {
672,128,441✔
451
    p_[i] = p_[i] / integral_;
625,539,267✔
452
    c_[i] = c_[i] / integral_;
625,539,267✔
453
  }
454
}
46,589,174✔
455

456
double Tabular::sample_unbiased(uint64_t* seed) const
693,607,615✔
457
{
458
  // Sample value of CDF
459
  double c = prn(seed);
693,607,615✔
460

461
  // Find first CDF bin which is above the sampled value
462
  double c_i = c_[0];
693,607,615✔
463
  int i;
464
  std::size_t n = c_.size();
693,607,615✔
465
  for (i = 0; i < n - 1; ++i) {
2,147,483,647!
466
    if (c <= c_[i + 1])
2,147,483,647✔
467
      break;
693,607,615✔
468
    c_i = c_[i + 1];
1,915,534,532✔
469
  }
470

471
  // Determine bounding PDF values
472
  double x_i = x_[i];
693,607,615✔
473
  double p_i = p_[i];
693,607,615✔
474

475
  if (interp_ == Interpolation::histogram) {
693,607,615✔
476
    // Histogram interpolation
477
    if (p_i > 0.0) {
3,421,781!
478
      return (x_i + (c - c_i) / p_i);
3,421,781✔
479
    } else {
480
      return x_i;
×
481
    }
482
  } else {
483
    // Linear-linear interpolation
484
    double x_i1 = x_[i + 1];
690,185,834✔
485
    double p_i1 = p_[i + 1];
690,185,834✔
486

487
    double m = (p_i1 - p_i) / (x_i1 - x_i);
690,185,834✔
488
    if (m == 0.0) {
690,185,834✔
489
      return (x_i + (c - c_i) / p_i);
322,137,315✔
490
    } else {
491
      return (
492
        x_i +
493
        (std::sqrt(std::max(0.0, p_i * p_i + 2 * m * (c - c_i))) - p_i) / m);
368,048,519✔
494
    }
495
  }
496
}
497

498
double Tabular::evaluate(double x) const
110,000✔
499
{
500
  int i;
501

502
  if (interp_ == Interpolation::histogram) {
110,000!
NEW
503
    i = std::upper_bound(x_.begin(), x_.end(), x) - x_.begin() - 1;
×
NEW
504
    if (i < 0 || i >= static_cast<int>(p_.size())) {
×
NEW
505
      return 0.0;
×
506
    } else {
NEW
507
      return p_[i];
×
508
    }
509
  } else {
510
    i = std::lower_bound(x_.begin(), x_.end(), x) - x_.begin() - 1;
110,000✔
511

512
    if (i < 0 || i >= static_cast<int>(p_.size()) - 1) {
110,000!
NEW
513
      return 0.0;
×
514
    } else {
515
      double x0 = x_[i];
110,000✔
516
      double x1 = x_[i + 1];
110,000✔
517
      double p0 = p_[i];
110,000✔
518
      double p1 = p_[i + 1];
110,000✔
519

520
      double t = (x - x0) / (x1 - x0);
110,000✔
521
      return (1 - t) * p0 + t * p1;
110,000✔
522
    }
523
  }
524
}
525

526
//==============================================================================
527
// Equiprobable implementation
528
//==============================================================================
529

NEW
530
double Equiprobable::sample_unbiased(uint64_t* seed) const
×
531
{
532
  std::size_t n = x_.size();
×
533

534
  double r = prn(seed);
×
535
  int i = std::floor((n - 1) * r);
×
536

537
  double xl = x_[i];
×
538
  double xr = x_[i + i];
×
NEW
539
  return (xl + ((n - 1) * r - i) * (xr - xl));
×
540
}
541

NEW
542
double Equiprobable::evaluate(double x) const
×
543
{
NEW
544
  double x_min = *std::min_element(x_.begin(), x_.end());
×
NEW
545
  double x_max = *std::max_element(x_.begin(), x_.end());
×
546

NEW
547
  if (x < x_min || x > x_max) {
×
NEW
548
    return 0.0;
×
549
  } else {
NEW
550
    return 1.0 / (x_max - x_min);
×
551
  }
552
}
553

554
//==============================================================================
555
// Mixture implementation
556
//==============================================================================
557

558
Mixture::Mixture(pugi::xml_node node)
70✔
559
{
560
  vector<double> probabilities;
70✔
561

562
  // First pass: collect distributions and their probabilities
563
  for (pugi::xml_node pair : node.children("pair")) {
258✔
564
    // Check that required data exists
565
    if (!pair.attribute("probability"))
188!
566
      fatal_error("Mixture pair element does not have probability.");
×
567
    if (!pair.child("dist"))
188!
568
      fatal_error("Mixture pair element does not have a distribution.");
×
569

570
    // Get probability and distribution
571
    double p = std::stod(pair.attribute("probability").value());
188✔
572
    auto dist = distribution_from_xml(pair.child("dist"));
188✔
573

574
    // Weight probability by the distribution's integral
575
    double weighted_prob = p * dist->integral();
188✔
576
    probabilities.push_back(weighted_prob);
188✔
577
    distribution_.push_back(std::move(dist));
188✔
578
  }
188✔
579

580
  // Save sum of weighted probabilities
581
  integral_ = std::accumulate(probabilities.begin(), probabilities.end(), 0.0);
70✔
582

583
  // Initialize DiscreteIndex with probability vector, which will normalize
584
  di_.assign(probabilities);
70✔
585
}
70✔
586

NEW
587
void Mixture::set_bias_mixture(pugi::xml_node node)
×
588
{
589
  // Takes the probability vector from a bias distribution and applies it to
590
  // the existing DiscreteIndex.
NEW
591
  auto bias_params = get_node_array<double>(node, "bias");
×
592

NEW
593
  di_.apply_bias(bias_params);
×
NEW
594
}
×
595

596
std::pair<double, double> Mixture::sample(uint64_t* seed) const
223,564✔
597
{
598
  size_t sample_index = di_.sample(seed);
223,564✔
599

600
  // Sample the chosen distribution
601
  std::pair<double, double> sample_pair =
602
    distribution_[sample_index]->sample(seed);
223,564✔
603

604
  return {sample_pair.first, di_.weight()[sample_index] * sample_pair.second};
223,564✔
605
}
606

NEW
607
double Mixture::sample_unbiased(uint64_t* seed) const
×
608
{
609
  // Unbiased sampling: choose a sub-distribution based on normalized probs
NEW
610
  size_t sample_index = di_.sample(seed);
×
NEW
611
  auto sample_pair = distribution_[sample_index]->sample(seed);
×
NEW
612
  return sample_pair.first;
×
613
}
614

615
//==============================================================================
616
// Helper function
617
//==============================================================================
618

619
UPtrDist distribution_from_xml(pugi::xml_node node)
35,320✔
620
{
621
  if (!check_for_node(node, "type"))
35,320!
622
    openmc::fatal_error("Distribution type must be specified.");
×
623

624
  // Determine type of distribution
625
  std::string type = get_node_value(node, "type", true, true);
35,320✔
626

627
  // Allocate extension of Distribution
628
  UPtrDist dist;
35,320✔
629
  if (type == "uniform") {
35,320✔
630
    dist = UPtrDist {new Uniform(node)};
344✔
631
  } else if (type == "powerlaw") {
34,976✔
632
    dist = UPtrDist {new PowerLaw(node)};
76✔
633
  } else if (type == "maxwell") {
34,900✔
634
    dist = UPtrDist {new Maxwell(node)};
97✔
635
  } else if (type == "watt") {
34,803✔
636
    dist = UPtrDist {new Watt(node)};
129✔
637
  } else if (type == "normal") {
34,674✔
638
    dist = UPtrDist {new Normal(node)};
33✔
639
  } else if (type == "discrete") {
34,641✔
640
    dist = UPtrDist {new Discrete(node)};
26,934✔
641
  } else if (type == "tabular") {
7,707✔
642
    dist = UPtrDist {new Tabular(node)};
7,637✔
643
  } else if (type == "mixture") {
70!
644
    dist = UPtrDist {new Mixture(node)};
70✔
645
  } else if (type == "muir") {
×
646
    openmc::fatal_error(
×
647
      "'muir' distributions are now specified using the openmc.stats.muir() "
648
      "function in Python. Please regenerate your XML files.");
649
  } else {
650
    openmc::fatal_error("Invalid distribution type: " + type);
×
651
  }
652

653
  // Check for biasing distribution
654
  if (check_for_node(node, "bias")) {
35,320✔
655
    pugi::xml_node bias_node = node.child("bias");
88✔
656

657
    if (check_for_node(bias_node, "bias")) {
88!
NEW
658
      openmc::fatal_error(
×
NEW
659
        "Distribution of type " + type +
×
660
        " has a bias distribution with its "
661
        "own bias distribution. Please ensure bias distributions do not have "
662
        "their own bias.");
663
    } else if (type == "discrete") {
88✔
664
      static_cast<Discrete*>(dist.get())->set_bias_discrete(node);
11✔
665
    } else if (type == "mixture") {
77!
NEW
666
      static_cast<Mixture*>(dist.get())->set_bias_mixture(node);
×
667
    } else {
668
      UPtrDist bias = distribution_from_xml(bias_node);
77✔
669
      dist->set_bias(std::move(bias));
77✔
670
    }
77✔
671
  }
672

673
  return dist;
70,640✔
674
}
35,320✔
675

676
} // 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

© 2026 Coveralls, Inc