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

openmc-dev / openmc / 17445237309

03 Sep 2025 08:25PM UTC coverage: 84.864% (-0.3%) from 85.209%
17445237309

Pull #3460

github

web-flow
Merge 5e7a2eae9 into 591856472
Pull Request #3460: Source biasing capabilities

343 of 638 new or added lines in 11 files covered. (53.76%)

4 existing lines in 3 files now uncovered.

53134 of 62611 relevant lines covered (84.86%)

38459716.64 hits per line

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

60.84
/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
// PDF evaluation not supported for all distribution types
NEW
21
double Distribution::evaluate(double x) const
×
22
{
NEW
23
  throw std::runtime_error(
×
NEW
24
    "PDF evaluation not implemented for this distribution type.");
×
25
}
26

27
//==============================================================================
28
// DiscreteIndex implementation
29
//==============================================================================
30

31
DiscreteIndex::DiscreteIndex(pugi::xml_node node)
25,810✔
32
{
33
  auto params = get_node_array<double>(node, "parameters");
25,810✔
34
  std::size_t n = params.size() / 2;
25,810✔
35

36
  assign({params.data() + n, n});
25,810✔
37
}
25,810✔
38

39
DiscreteIndex::DiscreteIndex(span<const double> p)
58,936✔
40
{
41
  assign(p);
58,936✔
42
}
58,936✔
43

44
void DiscreteIndex::assign(span<const double> p)
92,944✔
45
{
46
  prob_.assign(p.begin(), p.end());
92,944✔
47

48
  this->init_alias();
92,944✔
49
  this->init_wgt();
92,944✔
50
}
92,944✔
51

52
void DiscreteIndex::init_alias()
92,944✔
53
{
54
  normalize();
92,944✔
55

56
  // record user input normalized distribution prob_actual for RR/source bias
57
  prob_actual_ = prob_;
92,944✔
58

59
  // The initialization and sampling method is based on Vose
60
  // (DOI: 10.1109/32.92917)
61
  // Vectors for large and small probabilities based on 1/n
62
  vector<size_t> large;
92,944✔
63
  vector<size_t> small;
92,944✔
64

65
  size_t n = prob_.size();
92,944✔
66

67
  // Set and allocate memory
68
  alias_.assign(n, 0);
92,944✔
69

70
  // Fill large and small vectors based on 1/n
71
  for (size_t i = 0; i < n; i++) {
1,597,736✔
72
    prob_[i] *= n;
1,504,792✔
73
    if (prob_[i] > 1.0) {
1,504,792✔
74
      large.push_back(i);
223,775✔
75
    } else {
76
      small.push_back(i);
1,281,017✔
77
    }
78
  }
79

80
  while (!large.empty() && !small.empty()) {
1,406,817✔
81
    int j = small.back();
1,313,873✔
82
    int k = large.back();
1,313,873✔
83

84
    // Remove last element of small
85
    small.pop_back();
1,313,873✔
86

87
    // Update probability and alias based on Vose's algorithm
88
    prob_[k] += prob_[j] - 1.0;
1,313,873✔
89
    alias_[j] = k;
1,313,873✔
90

91
    // Move large index to small vector, if it is no longer large
92
    if (prob_[k] < 1.0) {
1,313,873✔
93
      small.push_back(k);
216,053✔
94
      large.pop_back();
216,053✔
95
    }
96
  }
97
}
92,944✔
98

99
void DiscreteIndex::init_wgt()
92,944✔
100
{
101
  wgt_.assign(prob_.size(), 1.0);
92,944✔
102
}
92,944✔
103

104
size_t DiscreteIndex::sample(uint64_t* seed) const
68,162,690✔
105
{
106
  // Alias sampling of discrete distribution
107
  size_t n = prob_.size();
68,162,690✔
108
  if (n > 1) {
68,162,690✔
109
    size_t u = prn(seed) * n;
14,688,202✔
110
    if (prn(seed) < prob_[u]) {
14,688,202✔
111
      return u;
9,205,762✔
112
    } else {
113
      return alias_[u];
5,482,440✔
114
    }
115
  } else {
116
    return 0;
53,474,488✔
117
  }
118
}
119

120
void DiscreteIndex::normalize()
92,944✔
121
{
122
  // Renormalize density function so that it sums to unity. Note that we save
123
  // the integral of the distribution so that if it is used as part of another
124
  // distribution (e.g., Mixture), we know its relative strength.
125
  integral_ = std::accumulate(prob_.begin(), prob_.end(), 0.0);
92,944✔
126
  for (auto& p_i : prob_) {
1,597,736✔
127
    p_i /= integral_;
1,504,792✔
128
  }
129
}
92,944✔
130

NEW
131
void DiscreteIndex::apply_bias(span<const double> b)
×
132
{
133
  // Replace the probability vector with that from the bias distribution.
NEW
134
  prob_.assign(b.begin(), b.end());
×
NEW
135
  if (prob_.size() != prob_actual_.size()) {
×
NEW
136
    openmc::fatal_error(
×
NEW
137
      "Size mismatch: Attempted to bias Discrete distribution with " +
×
NEW
138
      std::to_string(prob_actual_.size()) +
×
139
      " probability entries using a "
NEW
140
      "Discrete distribution with " +
×
NEW
141
      std::to_string(prob_.size()) +
×
142
      " entries. Please ensure distributions have the same size.");
143
  }
144

145
  // Normalize biased probability vector and populate weight table.
NEW
146
  normalize();
×
NEW
147
  for (std::size_t i = 0; i < wgt_.size(); ++i) {
×
NEW
148
    if (prob_[i] == 0.0) {
×
149
      // Allow nonzero entries in original distribution to be given zero
150
      // sampling probability in the biased distribution.
NEW
151
      wgt_[i] = INFTY;
×
152
    } else {
NEW
153
      wgt_[i] = prob_actual_[i] / prob_[i];
×
154
    }
155
  }
156

157
  // Reconstruct alias table for sampling from the biased distribution.
158
  // Values from unbiased prob_actual_ may be recovered using weight table.
NEW
159
  this->init_alias();
×
160
}
161

162
//==============================================================================
163
// Discrete implementation
164
//==============================================================================
165

166
Discrete::Discrete(pugi::xml_node node) : di_(node)
25,810✔
167
{
168
  auto params = get_node_array<double>(node, "parameters");
25,810✔
169

170
  std::size_t n = params.size() / 2;
25,810✔
171

172
  x_.assign(params.begin(), params.begin() + n);
25,810✔
173
}
25,810✔
174

175
Discrete::Discrete(const double* x, const double* p, size_t n) : di_({p, n})
58,936✔
176
{
177

178
  x_.assign(x, x + n);
58,936✔
179
}
58,936✔
180

181
std::pair<double, double> Discrete::sample(uint64_t* seed) const
64,639,222✔
182
{
183
  size_t sample_index = di_.sample(seed);
64,639,222✔
184
  return {x_[sample_index], di_.weight()[sample_index]};
64,639,222✔
185
}
186

NEW
187
double Discrete::evaluate(double x) const
×
188
{
189
  // This function is not called when sampling from a Discrete distribution,
190
  // even if it is biased. This is because Discrete distributions may only
191
  // be biased by another Discrete distribution. It is only called when a
192
  // Discrete distribution is used to bias another kind of distribution.
NEW
193
  for (size_t i = 0; i < x_.size(); ++i) {
×
NEW
194
    if (std::fabs(x_[i] - x) <= FP_PRECISION) {
×
NEW
195
      return di_.prob_actual()[i];
×
196
    }
197
  }
NEW
198
  return 0.0;
×
199
}
200

NEW
201
void Discrete::set_bias_discrete(pugi::xml_node bias_node)
×
202
{
203
  // Takes the probability vector from a bias distribution and applies it to
204
  // the existing DiscreteIndex.
NEW
205
  auto params = get_node_array<double>(bias_node, "parameters");
×
NEW
206
  std::size_t n = params.size() / 2;
×
207

NEW
208
  di_.apply_bias({params.data() + n, n});
×
209
}
210

211
//==============================================================================
212
// Uniform implementation
213
//==============================================================================
214

215
Uniform::Uniform(pugi::xml_node node)
256✔
216
{
217
  auto params = get_node_array<double>(node, "parameters");
256✔
218
  if (params.size() != 2) {
256✔
219
    fatal_error("Uniform distribution must have two "
×
220
                "parameters specified.");
221
  }
222

223
  a_ = params.at(0);
256✔
224
  b_ = params.at(1);
256✔
225
}
256✔
226

227
std::pair<double, double> Uniform::sample(uint64_t* seed) const
285,875✔
228
{
229
  if (bias()) {
285,875✔
NEW
230
    auto [val, wgt] = bias()->sample(seed);
×
NEW
231
    return {val, this->evaluate(val) / bias()->evaluate(val)};
×
232
  } else {
233
    return {a_ + prn(seed) * (b_ - a_), 1.0};
285,875✔
234
  }
235
}
236

NEW
237
double Uniform::evaluate(double x) const
×
238
{
NEW
239
  if (x <= a()) {
×
NEW
240
    return 0.0;
×
NEW
241
  } else if (x >= b()) {
×
NEW
242
    return 0.0;
×
243
  } else {
NEW
244
    return 1 / (b() - a());
×
245
  }
246
}
247

248
//==============================================================================
249
// PowerLaw implementation
250
//==============================================================================
251

252
PowerLaw::PowerLaw(pugi::xml_node node)
32✔
253
{
254
  auto params = get_node_array<double>(node, "parameters");
32✔
255
  if (params.size() != 3) {
32✔
256
    fatal_error("PowerLaw distribution must have three "
×
257
                "parameters specified.");
258
  }
259

260
  const double a = params.at(0);
32✔
261
  const double b = params.at(1);
32✔
262
  const double n = params.at(2);
32✔
263

264
  offset_ = std::pow(a, n + 1);
32✔
265
  span_ = std::pow(b, n + 1) - offset_;
32✔
266
  ninv_ = 1 / (n + 1);
32✔
267
}
32✔
268

NEW
269
double PowerLaw::evaluate(double x) const
×
270
{
NEW
271
  if (x <= a()) {
×
NEW
272
    return 0.0;
×
NEW
273
  } else if (x >= b()) {
×
NEW
274
    return 0.0;
×
275
  } else {
NEW
276
    int pwr = n() + 1;
×
NEW
277
    double norm = pwr / span_;
×
NEW
278
    return norm * std::pow(std::fabs(x), n());
×
279
  }
280
}
281

282
std::pair<double, double> PowerLaw::sample(uint64_t* seed) const
2,222✔
283
{
284
  if (bias()) {
2,222✔
NEW
285
    auto [val, wgt] = bias()->sample(seed);
×
NEW
286
    return {val, this->evaluate(val) / bias()->evaluate(val)};
×
287
  } else {
288
    return {std::pow(offset_ + prn(seed) * span_, ninv_), 1.0};
2,222✔
289
  }
290
}
291

292
//==============================================================================
293
// Maxwell implementation
294
//==============================================================================
295

296
Maxwell::Maxwell(pugi::xml_node node)
64✔
297
{
298
  theta_ = std::stod(get_node_value(node, "parameters"));
64✔
299
}
64✔
300

301
std::pair<double, double> Maxwell::sample(uint64_t* seed) const
3,883✔
302
{
303
  if (bias()) {
3,883✔
NEW
304
    auto [val, wgt] = bias()->sample(seed);
×
NEW
305
    return {val, this->evaluate(val) / bias()->evaluate(val)};
×
306
  } else {
307
    return {maxwell_spectrum(theta_, seed), 1.0};
3,883✔
308
  }
309
}
310

NEW
311
double Maxwell::evaluate(double x) const
×
312
{
NEW
313
  double c = (2.0 / SQRT_PI) * std::pow(theta_, -1.5);
×
NEW
314
  return c * std::sqrt(x) * std::exp(-x / theta_);
×
315
}
316

317
//==============================================================================
318
// Watt implementation
319
//==============================================================================
320

321
Watt::Watt(pugi::xml_node node)
96✔
322
{
323
  auto params = get_node_array<double>(node, "parameters");
96✔
324
  if (params.size() != 2)
96✔
325
    openmc::fatal_error("Watt energy distribution must have two "
×
326
                        "parameters specified.");
327

328
  a_ = params.at(0);
96✔
329
  b_ = params.at(1);
96✔
330
}
96✔
331

332
std::pair<double, double> Watt::sample(uint64_t* seed) const
11,304,174✔
333
{
334
  if (bias()) {
11,304,174✔
NEW
335
    auto [val, wgt] = bias()->sample(seed);
×
NEW
336
    return {val, this->evaluate(val) / bias()->evaluate(val)};
×
337
  } else {
338
    return {watt_spectrum(a_, b_, seed), 1.0};
11,304,174✔
339
  }
340
}
341

NEW
342
double Watt::evaluate(double x) const
×
343
{
NEW
344
  double c = std::exp(-a_ * (b_ / 4.0)) / (std::pow(a_, 2.0) * std::sqrt(b_));
×
NEW
345
  return c * std::exp(-x / a_) * std::sinh(std::sqrt(b_ * x));
×
346
}
347

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

359
  mean_value_ = params.at(0);
×
360
  std_dev_ = params.at(1);
×
361
}
362

NEW
363
std::pair<double, double> Normal::sample(uint64_t* seed) const
×
364
{
NEW
365
  if (bias()) {
×
NEW
366
    auto [val, wgt] = bias()->sample(seed);
×
NEW
367
    return {val, this->evaluate(val) / bias()->evaluate(val)};
×
368
  } else {
NEW
369
    return {normal_variate(mean_value_, std_dev_, seed), 1.0};
×
370
  }
371
}
372

NEW
373
double Normal::evaluate(double x) const
×
374
{
NEW
375
  return (1.0 / (std::sqrt(2.0 / PI) * std_dev_)) *
×
NEW
376
         std::exp(-(std::pow((x - mean_value_), 2.0)) /
×
NEW
377
                  (2.0 * std::pow(std_dev_, 2.0)));
×
378
}
379

380
//==============================================================================
381
// Tabular implementation
382
//==============================================================================
383

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

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

412
Tabular::Tabular(const double* x, const double* p, int n, Interpolation interp,
52,742,452✔
413
  const double* c)
52,742,452✔
414
  : interp_ {interp}
52,742,452✔
415
{
416
  init(x, p, n, c);
52,742,452✔
417
}
52,742,452✔
418

419
void Tabular::init(
52,749,697✔
420
  const double* x, const double* p, std::size_t n, const double* c)
421
{
422
  // Copy x/p arrays into vectors
423
  std::copy(x, x + n, std::back_inserter(x_));
52,749,697✔
424
  std::copy(p, p + n, std::back_inserter(p_));
52,749,697✔
425

426
  // Check interpolation parameter
427
  if (interp_ != Interpolation::histogram &&
52,749,697✔
428
      interp_ != Interpolation::lin_lin) {
44,629,425✔
429
    openmc::fatal_error("Only histogram and linear-linear interpolation "
×
430
                        "for tabular distribution is supported.");
431
  }
432

433
  // Calculate cumulative distribution function
434
  if (c) {
52,749,697✔
435
    std::copy(c, c + n, std::back_inserter(c_));
52,742,452✔
436
  } else {
437
    c_.resize(n);
7,245✔
438
    c_[0] = 0.0;
7,245✔
439
    for (int i = 1; i < n; ++i) {
90,012✔
440
      if (interp_ == Interpolation::histogram) {
82,767✔
441
        c_[i] = c_[i - 1] + p_[i - 1] * (x_[i] - x_[i - 1]);
82,639✔
442
      } else if (interp_ == Interpolation::lin_lin) {
128✔
443
        c_[i] = c_[i - 1] + 0.5 * (p_[i - 1] + p_[i]) * (x_[i] - x_[i - 1]);
128✔
444
      }
445
    }
446
  }
447

448
  // Normalize density and distribution functions. Note that we save the
449
  // integral of the distribution so that if it is used as part of another
450
  // distribution (e.g., Mixture), we know its relative strength.
451
  integral_ = c_[n - 1];
52,749,697✔
452
  for (int i = 0; i < n; ++i) {
756,648,678✔
453
    p_[i] = p_[i] / integral_;
703,898,981✔
454
    c_[i] = c_[i] / integral_;
703,898,981✔
455
  }
456
}
52,749,697✔
457

458
std::pair<double, double> Tabular::sample(uint64_t* seed) const
611,091,942✔
459
{
460
  if (bias()) {
611,091,942✔
NEW
461
    auto [val, wgt] = bias()->sample(seed);
×
NEW
462
    return {val, this->evaluate(val) / bias()->evaluate(val)};
×
463
  } else {
464
    // Sample value of CDF
465
    double c = prn(seed);
611,091,942✔
466

467
    // Find first CDF bin which is above the sampled value
468
    double c_i = c_[0];
611,091,942✔
469
    int i;
470
    std::size_t n = c_.size();
611,091,942✔
471
    for (i = 0; i < n - 1; ++i) {
2,147,483,647✔
472
      if (c <= c_[i + 1])
2,147,483,647✔
473
        break;
611,091,942✔
474
      c_i = c_[i + 1];
1,580,945,048✔
475
    }
476

477
    // Determine bounding PDF values
478
    double x_i = x_[i];
611,091,942✔
479
    double p_i = p_[i];
611,091,942✔
480

481
    if (interp_ == Interpolation::histogram) {
611,091,942✔
482
      // Histogram interpolation
483
      if (p_i > 0.0) {
3,407,965✔
484
        return {(x_i + (c - c_i) / p_i), 1.0};
3,407,965✔
485
      } else {
NEW
486
        return {x_i, 1.0};
×
487
      }
488
    } else {
489
      // Linear-linear interpolation
490
      double x_i1 = x_[i + 1];
607,683,977✔
491
      double p_i1 = p_[i + 1];
607,683,977✔
492

493
      double m = (p_i1 - p_i) / (x_i1 - x_i);
607,683,977✔
494
      if (m == 0.0) {
607,683,977✔
495
        return {(x_i + (c - c_i) / p_i), 1.0};
317,848,285✔
496
      } else {
497
        return {
498
          (x_i +
579,671,384✔
499
            (std::sqrt(std::max(0.0, p_i * p_i + 2 * m * (c - c_i))) - p_i) /
289,835,692✔
500
              m),
501
          1.0};
289,835,692✔
502
      }
503
    }
504
  }
505
}
506

NEW
507
double Tabular::evaluate(double x) const
×
508
{
509
  int i;
510

UNCOV
511
  if (interp_ == Interpolation::histogram) {
×
NEW
512
    i = std::upper_bound(x_.begin(), x_.end(), x) - x_.begin() - 1;
×
NEW
513
    if (i < 0 || i >= static_cast<int>(p_.size())) {
×
NEW
514
      return 0.0;
×
515
    } else {
NEW
516
      return p_[i];
×
517
    }
518
  } else {
NEW
519
    i = std::lower_bound(x_.begin(), x_.end(), x) - x_.begin() - 1;
×
520

NEW
521
    if (i < 0 || i >= static_cast<int>(p_.size()) - 1) {
×
NEW
522
      return 0.0;
×
523
    } else {
NEW
524
      double x0 = x_[i];
×
NEW
525
      double x1 = x_[i + 1];
×
NEW
526
      double p0 = p_[i];
×
NEW
527
      double p1 = p_[i + 1];
×
528

NEW
529
      double t = (x - x0) / (x1 - x0);
×
NEW
530
      return (1 - t) * p0 + t * p1;
×
531
    }
532
  }
533
}
534

535
//==============================================================================
536
// Equiprobable implementation
537
//==============================================================================
538

NEW
539
std::pair<double, double> Equiprobable::sample(uint64_t* seed) const
×
540
{
NEW
541
  if (bias()) {
×
NEW
542
    auto [val, wgt] = bias()->sample(seed);
×
NEW
543
    return {val, this->evaluate(val) / bias()->evaluate(val)};
×
544
  } else {
NEW
545
    std::size_t n = x_.size();
×
546

NEW
547
    double r = prn(seed);
×
NEW
548
    int i = std::floor((n - 1) * r);
×
549

NEW
550
    double xl = x_[i];
×
NEW
551
    double xr = x_[i + i];
×
NEW
552
    return {(xl + ((n - 1) * r - i) * (xr - xl)), 1.0};
×
553
  }
554
}
555

NEW
556
double Equiprobable::evaluate(double x) const
×
557
{
NEW
558
  double x_min = *std::min_element(x_.begin(), x_.end());
×
NEW
559
  double x_max = *std::max_element(x_.begin(), x_.end());
×
560

NEW
561
  if (x < x_min || x > x_max) {
×
NEW
562
    return 0.0;
×
563
  } else {
NEW
564
    return 1.0 / (x_max - x_min);
×
565
  }
566
}
567

568
//==============================================================================
569
// Mixture implementation
570
//==============================================================================
571

572
Mixture::Mixture(pugi::xml_node node)
48✔
573
{
574
  double cumsum = 0.0;
48✔
575
  for (pugi::xml_node pair : node.children("pair")) {
192✔
576
    // Check that required data exists
577
    if (!pair.attribute("probability"))
144✔
578
      fatal_error("Mixture pair element does not have probability.");
×
579
    if (!pair.child("dist"))
144✔
580
      fatal_error("Mixture pair element does not have a distribution.");
×
581

582
    // cumulative sum of probabilities
583
    double p = std::stod(pair.attribute("probability").value());
144✔
584

585
    // Save cumulative probability and distribution
586
    auto dist = distribution_from_xml(pair.child("dist"));
144✔
587
    cumsum += p * dist->integral();
144✔
588

589
    distribution_.push_back(std::make_pair(cumsum, std::move(dist)));
144✔
590
  }
144✔
591

592
  // Save integral of distribution
593
  integral_ = cumsum;
48✔
594

595
  // Normalize cumulative probabilities to 1
596
  for (auto& pair : distribution_) {
192✔
597
    pair.first /= cumsum;
144✔
598
  }
599
}
48✔
600

601
std::pair<double, double> Mixture::sample(uint64_t* seed) const
3,564✔
602
{
603
  // Sample value of CDF
604
  const double p = prn(seed);
3,564✔
605

606
  // find matching distribution
607
  const auto it = std::lower_bound(distribution_.cbegin(), distribution_.cend(),
3,564✔
608
    p, [](const DistPair& pair, double p) { return pair.first < p; });
7,128✔
609

610
  // This should not happen. Catch it
611
  assert(it != distribution_.cend());
2,916✔
612

613
  // Sample the chosen distribution
614
  return it->second->sample(seed);
7,128✔
615
}
616

617
//==============================================================================
618
// Helper function
619
//==============================================================================
620

621
UPtrDist distribution_from_xml(pugi::xml_node node)
33,540✔
622
{
623
  if (!check_for_node(node, "type"))
33,540✔
624
    openmc::fatal_error("Distribution type must be specified.");
×
625

626
  // Determine type of distribution
627
  std::string type = get_node_value(node, "type", true, true);
33,540✔
628

629
  // Allocate extension of Distribution
630
  UPtrDist dist;
33,540✔
631
  if (type == "uniform") {
33,540✔
632
    dist = UPtrDist {new Uniform(node)};
256✔
633
  } else if (type == "powerlaw") {
33,284✔
634
    dist = UPtrDist {new PowerLaw(node)};
32✔
635
  } else if (type == "maxwell") {
33,252✔
636
    dist = UPtrDist {new Maxwell(node)};
64✔
637
  } else if (type == "watt") {
33,188✔
638
    dist = UPtrDist {new Watt(node)};
96✔
639
  } else if (type == "normal") {
33,092✔
640
    dist = UPtrDist {new Normal(node)};
×
641
  } else if (type == "discrete") {
33,092✔
642
    dist = UPtrDist {new Discrete(node)};
25,799✔
643
  } else if (type == "tabular") {
7,293✔
644
    dist = UPtrDist {new Tabular(node)};
7,245✔
645
  } else if (type == "mixture") {
48✔
646
    dist = UPtrDist {new Mixture(node)};
48✔
647
  } else if (type == "muir") {
×
648
    openmc::fatal_error(
×
649
      "'muir' distributions are now specified using the openmc.stats.muir() "
650
      "function in Python. Please regenerate your XML files.");
651
  } else {
652
    openmc::fatal_error("Invalid distribution type: " + type);
×
653
  }
654

655
  // Check for biasing distribution
656
  if (check_for_node(node, "bias")) {
33,540✔
NEW
657
    pugi::xml_node bias_node = node.child("bias");
×
NEW
658
    std::string bias_type = get_node_value(bias_node, "type", true, true);
×
659

NEW
660
    if (check_for_node(bias_node, "bias")) {
×
NEW
661
      openmc::fatal_error(
×
NEW
662
        "Distribution of type " + type +
×
663
        " has a bias distribution with its "
664
        "own bias distribution. Please ensure bias distributions do not have "
665
        "their own bias.");
NEW
666
    } else if (type == "discrete" && bias_type != "discrete") {
×
NEW
667
      openmc::fatal_error(
×
668
        "Discrete distributions may only be biased by another Discrete.");
NEW
669
    } else if (type == "discrete" && bias_type == "discrete") {
×
670
      static_cast<Discrete*>(dist.get())->set_bias_discrete(bias_node);
NEW
671
    } else if (bias_type == "discrete") {
×
NEW
672
      openmc::fatal_error(
×
673
        "Only Discrete distributions may be biased by a Discrete "
674
        "distribution. Please ensure bias distribution shares the support "
675
        "of the unbiased distribution.");
676
    } else {
NEW
677
      UPtrDist bias = distribution_from_xml(bias_node);
×
NEW
678
      dist->set_bias(std::move(bias));
×
679
    }
680
  }
681

682
  return dist;
67,080✔
683
}
33,540✔
684

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