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

openmc-dev / openmc / 19037776327

03 Nov 2025 02:20PM UTC coverage: 81.819% (-3.3%) from 85.155%
19037776327

Pull #3252

github

web-flow
Merge 27157b613 into fd964bc9b
Pull Request #3252: Adding vtkhdf option to write vtk data

16654 of 23262 branches covered (71.59%)

Branch coverage included in aggregate %.

58 of 66 new or added lines in 1 file covered. (87.88%)

3192 existing lines in 103 files now uncovered.

54150 of 63275 relevant lines covered (85.58%)

43036802.74 hits per line

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

92.11
/src/distribution_energy.cpp
1
#include "openmc/distribution_energy.h"
2

3
#include <algorithm> // for max, min, copy, move
4
#include <cstddef>   // for size_t
5
#include <iterator>  // for back_inserter
6

7
#include "xtensor/xview.hpp"
8

9
#include "openmc/endf.h"
10
#include "openmc/hdf5_interface.h"
11
#include "openmc/math_functions.h"
12
#include "openmc/random_dist.h"
13
#include "openmc/random_lcg.h"
14
#include "openmc/search.h"
15

16
namespace openmc {
17

18
//==============================================================================
19
// DiscretePhoton implementation
20
//==============================================================================
21

22
DiscretePhoton::DiscretePhoton(hid_t group)
2,417,733✔
23
{
24
  read_attribute(group, "primary_flag", primary_flag_);
2,417,733✔
25
  read_attribute(group, "energy", energy_);
2,417,733✔
26
  read_attribute(group, "atomic_weight_ratio", A_);
2,417,733✔
27
}
2,417,733✔
28

29
double DiscretePhoton::sample(double E, uint64_t* seed) const
201,586✔
30
{
31
  if (primary_flag_ == 2) {
201,586✔
32
    return energy_ + A_ / (A_ + 1) * E;
29,392✔
33
  } else {
34
    return energy_;
172,194✔
35
  }
36
}
37

38
//==============================================================================
39
// LevelInelastic implementation
40
//==============================================================================
41

42
LevelInelastic::LevelInelastic(hid_t group)
532,710✔
43
{
44
  read_attribute(group, "threshold", threshold_);
532,710✔
45
  read_attribute(group, "mass_ratio", mass_ratio_);
532,710✔
46
}
532,710✔
47

48
double LevelInelastic::sample(double E, uint64_t* seed) const
12,838,245✔
49
{
50
  return mass_ratio_ * (E - threshold_);
12,838,245✔
51
}
52

53
//==============================================================================
54
// ContinuousTabular implementation
55
//==============================================================================
56

57
ContinuousTabular::ContinuousTabular(hid_t group)
305,224✔
58
{
59
  // Open incoming energy dataset
60
  hid_t dset = open_dataset(group, "energy");
305,224✔
61

62
  // Get interpolation parameters
63
  xt::xarray<int> temp;
305,224✔
64
  read_attribute(dset, "interpolation", temp);
305,224✔
65

66
  auto temp_b = xt::view(temp, 0); // view of breakpoints
305,224✔
67
  auto temp_i = xt::view(temp, 1); // view of interpolation parameters
305,224✔
68

69
  std::copy(temp_b.begin(), temp_b.end(), std::back_inserter(breakpoints_));
305,224✔
70
  for (const auto i : temp_i)
610,694✔
71
    interpolation_.push_back(int2interp(i));
305,470✔
72
  n_region_ = breakpoints_.size();
305,224✔
73

74
  // Get incoming energies
75
  read_dataset(dset, energy_);
305,224✔
76
  std::size_t n_energy = energy_.size();
305,224✔
77
  close_dataset(dset);
305,224✔
78

79
  // Get outgoing energy distribution data
80
  dset = open_dataset(group, "distribution");
305,224✔
81
  vector<int> offsets;
305,224✔
82
  vector<int> interp;
305,224✔
83
  vector<int> n_discrete;
305,224✔
84
  read_attribute(dset, "offsets", offsets);
305,224✔
85
  read_attribute(dset, "interpolation", interp);
305,224✔
86
  read_attribute(dset, "n_discrete_lines", n_discrete);
305,224✔
87

88
  xt::xarray<double> eout;
305,224✔
89
  read_dataset(dset, eout);
305,224✔
90
  close_dataset(dset);
305,224✔
91

92
  for (int i = 0; i < n_energy; ++i) {
4,650,427✔
93
    // Determine number of outgoing energies
94
    int j = offsets[i];
4,345,203✔
95
    int n;
96
    if (i < n_energy - 1) {
4,345,203✔
97
      n = offsets[i + 1] - j;
4,039,979✔
98
    } else {
99
      n = eout.shape()[1] - j;
305,224✔
100
    }
101

102
    // Assign interpolation scheme and number of discrete lines
103
    CTTable d;
4,345,203✔
104
    d.interpolation = int2interp(interp[i]);
4,345,203✔
105
    d.n_discrete = n_discrete[i];
4,345,203✔
106

107
    // Copy data
108
    d.e_out = xt::view(eout, 0, xt::range(j, j + n));
4,345,203✔
109
    d.p = xt::view(eout, 1, xt::range(j, j + n));
4,345,203✔
110

111
    // To get answers that match ACE data, for now we still use the tabulated
112
    // CDF values that were passed through to the HDF5 library. At a later
113
    // time, we can remove the CDF values from the HDF5 library and
114
    // reconstruct them using the PDF
115
    if (true) {
116
      d.c = xt::view(eout, 2, xt::range(j, j + n));
4,345,203✔
117
    } else {
118
      // Calculate cumulative distribution function -- discrete portion
119
      for (int k = 0; k < d.n_discrete; ++k) {
120
        if (k == 0) {
121
          d.c[k] = d.p[k];
122
        } else {
123
          d.c[k] = d.c[k - 1] + d.p[k];
124
        }
125
      }
126

127
      // Continuous portion
128
      for (int k = d.n_discrete; k < n; ++k) {
129
        if (k == d.n_discrete) {
130
          d.c[k] = d.c[k - 1] + d.p[k];
131
        } else {
132
          if (d.interpolation == Interpolation::histogram) {
133
            d.c[k] = d.c[k - 1] + d.p[k - 1] * (d.e_out[k] - d.e_out[k - 1]);
134
          } else if (d.interpolation == Interpolation::lin_lin) {
135
            d.c[k] = d.c[k - 1] + 0.5 * (d.p[k - 1] + d.p[k]) *
136
                                    (d.e_out[k] - d.e_out[k - 1]);
137
          }
138
        }
139
      }
140

141
      // Normalize density and distribution functions
142
      d.p /= d.c[n - 1];
143
      d.c /= d.c[n - 1];
144
    }
145

146
    distribution_.push_back(std::move(d));
4,345,203✔
147
  } // incoming energies
4,345,203✔
148
}
305,224✔
149

150
double ContinuousTabular::sample(double E, uint64_t* seed) const
28,613,377✔
151
{
152
  // Read number of interpolation regions and incoming energies
153
  bool histogram_interp;
154
  if (n_region_ == 1) {
28,613,377!
155
    histogram_interp = (interpolation_[0] == Interpolation::histogram);
28,613,377✔
156
  } else {
157
    histogram_interp = false;
×
158
  }
159

160
  // Find energy bin and calculate interpolation factor -- if the energy is
161
  // outside the range of the tabulated energies, choose the first or last bins
162
  auto n_energy_in = energy_.size();
28,613,377✔
163
  int i;
164
  double r;
165
  if (E < energy_[0]) {
28,613,377!
UNCOV
166
    i = 0;
×
UNCOV
167
    r = 0.0;
×
168
  } else if (E > energy_[n_energy_in - 1]) {
28,613,377!
169
    i = n_energy_in - 2;
×
170
    r = 1.0;
×
171
  } else {
172
    i = lower_bound_index(energy_.begin(), energy_.end(), E);
28,613,377✔
173
    r = (E - energy_[i]) / (energy_[i + 1] - energy_[i]);
28,613,377✔
174
  }
175

176
  // Sample between the ith and [i+1]th bin
177
  int l;
178
  if (histogram_interp) {
28,613,377✔
179
    l = i;
2,211✔
180
  } else {
181
    l = r > prn(seed) ? i + 1 : i;
28,611,166✔
182
  }
183

184
  // Determine outgoing energy bin
185
  int n_energy_out = distribution_[l].e_out.size();
28,613,377✔
186
  int n_discrete = distribution_[l].n_discrete;
28,613,377✔
187
  double r1 = prn(seed);
28,613,377✔
188
  double c_k = distribution_[l].c[0];
28,613,377✔
189
  int k = 0;
28,613,377✔
190
  int end = n_energy_out - 2;
28,613,377✔
191

192
  // Discrete portion
193
  for (int j = 0; j < n_discrete; ++j) {
29,194,298✔
194
    k = j;
727,397✔
195
    c_k = distribution_[l].c[k];
727,397✔
196
    if (r1 < c_k) {
727,397✔
197
      end = j;
146,476✔
198
      break;
146,476✔
199
    }
200
  }
201

202
  // Continuous portion
203
  double c_k1;
204
  for (int j = n_discrete; j < end; ++j) {
2,147,483,647✔
205
    k = j;
2,147,483,647✔
206
    c_k1 = distribution_[l].c[k + 1];
2,147,483,647✔
207
    if (r1 < c_k1)
2,147,483,647✔
208
      break;
28,466,571✔
209
    k = j + 1;
2,147,483,647✔
210
    c_k = c_k1;
2,147,483,647✔
211
  }
212

213
  double E_l_k = distribution_[l].e_out[k];
28,613,377✔
214
  double p_l_k = distribution_[l].p[k];
28,613,377✔
215
  double E_out = E_l_k;
28,613,377✔
216
  if (distribution_[l].interpolation == Interpolation::histogram) {
28,613,377✔
217
    // Histogram interpolation
218
    if (p_l_k > 0.0 && k >= n_discrete) {
726,273!
219
      E_out = E_l_k + (r1 - c_k) / p_l_k;
579,797✔
220
    }
221

222
  } else if (distribution_[l].interpolation == Interpolation::lin_lin) {
27,887,104!
223
    // Linear-linear interpolation
224
    double E_l_k1 = distribution_[l].e_out[k + 1];
27,887,104✔
225
    double p_l_k1 = distribution_[l].p[k + 1];
27,887,104✔
226

227
    if (E_l_k != E_l_k1) {
27,887,104!
228
      double frac = (p_l_k1 - p_l_k) / (E_l_k1 - E_l_k);
27,887,104✔
229
      if (frac == 0.0) {
27,887,104!
230
        E_out = E_l_k + (r1 - c_k) / p_l_k;
×
231
      } else {
232
        E_out =
27,887,104✔
233
          E_l_k +
234
          (std::sqrt(std::max(0.0, p_l_k * p_l_k + 2.0 * frac * (r1 - c_k))) -
27,887,104✔
235
            p_l_k) /
27,887,104✔
236
            frac;
237
      }
238
    }
239
  } else {
240
    throw std::runtime_error {"Unexpected interpolation for continuous energy "
×
241
                              "distribution."};
×
242
  }
243

244
  // Now interpolate between incident energy bins i and i + 1
245
  if (!histogram_interp && n_energy_out > 1 && k >= n_discrete) {
28,613,377✔
246
    // Interpolation for energy E1 and EK
247
    n_energy_out = distribution_[i].e_out.size();
28,464,690✔
248
    n_discrete = distribution_[i].n_discrete;
28,464,690✔
249
    const double E_i_1 = distribution_[i].e_out[n_discrete];
28,464,690✔
250
    const double E_i_K = distribution_[i].e_out[n_energy_out - 1];
28,464,690✔
251

252
    n_energy_out = distribution_[i + 1].e_out.size();
28,464,690✔
253
    n_discrete = distribution_[i + 1].n_discrete;
28,464,690✔
254
    const double E_i1_1 = distribution_[i + 1].e_out[n_discrete];
28,464,690✔
255
    const double E_i1_K = distribution_[i + 1].e_out[n_energy_out - 1];
28,464,690✔
256

257
    const double E_1 = E_i_1 + r * (E_i1_1 - E_i_1);
28,464,690✔
258
    const double E_K = E_i_K + r * (E_i1_K - E_i_K);
28,464,690✔
259

260
    if (l == i) {
28,464,690✔
261
      return E_1 + (E_out - E_i_1) * (E_K - E_1) / (E_i_K - E_i_1);
19,234,519✔
262
    } else {
263
      return E_1 + (E_out - E_i1_1) * (E_K - E_1) / (E_i1_K - E_i1_1);
9,230,171✔
264
    }
265
  } else {
266
    return E_out;
148,687✔
267
  }
268
}
269

270
//==============================================================================
271
// MaxwellEnergy implementation
272
//==============================================================================
273

274
MaxwellEnergy::MaxwellEnergy(hid_t group)
2,164✔
275
{
276
  read_attribute(group, "u", u_);
2,164✔
277
  hid_t dset = open_dataset(group, "theta");
2,164✔
278
  theta_ = Tabulated1D {dset};
2,164✔
279
  close_dataset(dset);
2,164✔
280
}
2,164✔
281

282
double MaxwellEnergy::sample(double E, uint64_t* seed) const
143,481✔
283
{
284
  // Get temperature corresponding to incoming energy
285
  double theta = theta_(E);
143,481✔
286

287
  while (true) {
288
    // Sample maxwell fission spectrum
289
    double E_out = maxwell_spectrum(theta, seed);
143,481✔
290

291
    // Accept energy based on restriction energy
292
    if (E_out <= E - u_)
143,481!
293
      return E_out;
143,481✔
UNCOV
294
  }
×
295
}
296

297
//==============================================================================
298
// Evaporation implementation
299
//==============================================================================
300

301
Evaporation::Evaporation(hid_t group)
5,296✔
302
{
303
  read_attribute(group, "u", u_);
5,296✔
304
  hid_t dset = open_dataset(group, "theta");
5,296✔
305
  theta_ = Tabulated1D {dset};
5,296✔
306
  close_dataset(dset);
5,296✔
307
}
5,296✔
308

309
double Evaporation::sample(double E, uint64_t* seed) const
27,280✔
310
{
311
  // Get temperature corresponding to incoming energy
312
  double theta = theta_(E);
27,280✔
313

314
  double y = (E - u_) / theta;
27,280✔
315
  double v = 1.0 - std::exp(-y);
27,280✔
316

317
  // Sample outgoing energy based on evaporation spectrum probability
318
  // density function
319
  double x;
320
  while (true) {
321
    x = -std::log((1.0 - v * prn(seed)) * (1.0 - v * prn(seed)));
32,120✔
322
    if (x <= y)
32,120✔
323
      break;
27,280✔
324
  }
325

326
  return x * theta;
27,280✔
327
}
328

329
//==============================================================================
330
// WattEnergy implementation
331
//==============================================================================
332

333
WattEnergy::WattEnergy(hid_t group)
197✔
334
{
335
  // Read restriction energy
336
  read_attribute(group, "u", u_);
197✔
337

338
  // Read tabulated functions
339
  hid_t dset = open_dataset(group, "a");
197✔
340
  a_ = Tabulated1D {dset};
197✔
341
  close_dataset(dset);
197✔
342
  dset = open_dataset(group, "b");
197✔
343
  b_ = Tabulated1D {dset};
197✔
344
  close_dataset(dset);
197✔
345
}
197✔
346

347
double WattEnergy::sample(double E, uint64_t* seed) const
1,831,599✔
348
{
349
  // Determine Watt parameters at incident energy
350
  double a = a_(E);
1,831,599✔
351
  double b = b_(E);
1,831,599✔
352

353
  while (true) {
354
    // Sample energy-dependent Watt fission spectrum
355
    double E_out = watt_spectrum(a, b, seed);
1,831,599✔
356

357
    // Accept energy based on restriction energy
358
    if (E_out <= E - u_)
1,831,599!
359
      return E_out;
1,831,599✔
UNCOV
360
  }
×
361
}
362

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