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

openmc-dev / openmc / 22110756672

17 Feb 2026 06:31PM UTC coverage: 81.831% (+0.1%) from 81.721%
22110756672

Pull #3813

github

web-flow
Merge 47dc7194f into 977ade79a
Pull Request #3813: Check for positive radii

17268 of 24298 branches covered (71.07%)

Branch coverage included in aggregate %.

16 of 16 new or added lines in 1 file covered. (100.0%)

1065 existing lines in 28 files now uncovered.

57520 of 67095 relevant lines covered (85.73%)

45595325.95 hits per line

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

91.91
/src/secondary_kalbach.cpp
1
#include "openmc/secondary_kalbach.h"
2

3
#include <algorithm> // for copy, move
4
#include <cmath>     // for log, sqrt, sinh
5
#include <cstddef>   // for size_t
6
#include <iterator>  // for back_inserter
7

8
#include "openmc/tensor.h"
9

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
#include "openmc/vector.h"
16

17
namespace openmc {
18

19
//==============================================================================
20
//! KalbachMann implementation
21
//==============================================================================
22

23
KalbachMann::KalbachMann(hid_t group)
62,848✔
24
{
25
  // Open incoming energy dataset
26
  hid_t dset = open_dataset(group, "energy");
62,848✔
27

28
  // Get interpolation parameters
29
  tensor::Tensor<int> temp;
62,848✔
30
  read_attribute(dset, "interpolation", temp);
62,848✔
31

32
  tensor::View<int> temp_b = temp.slice(0); // breakpoints
62,848✔
33
  tensor::View<int> temp_i = temp.slice(1); // interpolation parameters
62,848✔
34

35
  std::copy(temp_b.begin(), temp_b.end(), std::back_inserter(breakpoints_));
62,848✔
36
  for (const auto i : temp_i)
125,696✔
37
    interpolation_.push_back(int2interp(i));
62,848✔
38
  n_region_ = breakpoints_.size();
62,848✔
39

40
  // Get incoming energies
41
  read_dataset(dset, energy_);
62,848✔
42
  std::size_t n_energy = energy_.size();
62,848✔
43
  close_dataset(dset);
62,848✔
44

45
  // Get outgoing energy distribution data
46
  dset = open_dataset(group, "distribution");
62,848✔
47
  vector<int> offsets;
62,848✔
48
  vector<int> interp;
62,848✔
49
  vector<int> n_discrete;
62,848✔
50
  read_attribute(dset, "offsets", offsets);
62,848✔
51
  read_attribute(dset, "interpolation", interp);
62,848✔
52
  read_attribute(dset, "n_discrete_lines", n_discrete);
62,848✔
53

54
  tensor::Tensor<double> eout;
62,848✔
55
  read_dataset(dset, eout);
62,848✔
56
  close_dataset(dset);
62,848✔
57

58
  for (int i = 0; i < n_energy; ++i) {
1,280,098✔
59
    // Determine number of outgoing energies
60
    int j = offsets[i];
1,217,250✔
61
    int n;
62
    if (i < n_energy - 1) {
1,217,250✔
63
      n = offsets[i + 1] - j;
1,154,402✔
64
    } else {
65
      n = eout.shape(1) - j;
62,848✔
66
    }
67

68
    // Assign interpolation scheme and number of discrete lines
69
    KMTable d;
1,217,250✔
70
    d.interpolation = int2interp(interp[i]);
1,217,250✔
71
    d.n_discrete = n_discrete[i];
1,217,250✔
72

73
    // Copy data
74
    d.e_out = eout.slice(0, tensor::range(j, j + n));
1,217,250✔
75
    d.p = eout.slice(1, tensor::range(j, j + n));
1,217,250✔
76
    d.c = eout.slice(2, tensor::range(j, j + n));
1,217,250✔
77
    d.r = eout.slice(3, tensor::range(j, j + n));
1,217,250✔
78
    d.a = eout.slice(4, tensor::range(j, j + n));
1,217,250✔
79

80
    // To get answers that match ACE data, for now we still use the tabulated
81
    // CDF values that were passed through to the HDF5 library. At a later
82
    // time, we can remove the CDF values from the HDF5 library and
83
    // reconstruct them using the PDF
84
    if (false) {
85
      // Calculate cumulative distribution function -- discrete portion
86
      for (int k = 0; k < d.n_discrete; ++k) {
87
        if (k == 0) {
88
          d.c[k] = d.p[k];
89
        } else {
90
          d.c[k] = d.c[k - 1] + d.p[k];
91
        }
92
      }
93

94
      // Continuous portion
95
      for (int k = d.n_discrete; k < n; ++k) {
96
        if (k == d.n_discrete) {
97
          d.c[k] = d.c[k - 1] + d.p[k];
98
        } else {
99
          if (d.interpolation == Interpolation::histogram) {
100
            d.c[k] = d.c[k - 1] + d.p[k - 1] * (d.e_out[k] - d.e_out[k - 1]);
101
          } else if (d.interpolation == Interpolation::lin_lin) {
102
            d.c[k] = d.c[k - 1] + 0.5 * (d.p[k - 1] + d.p[k]) *
103
                                    (d.e_out[k] - d.e_out[k - 1]);
104
          }
105
        }
106
      }
107

108
      // Normalize density and distribution functions
109
      d.p /= d.c[n - 1];
110
      d.c /= d.c[n - 1];
111
    }
112

113
    distribution_.push_back(std::move(d));
1,217,250✔
114
  } // incoming energies
1,217,250✔
115
}
62,848✔
116

117
void KalbachMann::sample(
4,765,220✔
118
  double E_in, double& E_out, double& mu, uint64_t* seed) const
119
{
120
  // Find energy bin and calculate interpolation factor
121
  int i;
122
  double r;
123
  get_energy_index(energy_, E_in, i, r);
4,765,220✔
124

125
  // Sample between the ith and [i+1]th bin
126
  int l = r > prn(seed) ? i + 1 : i;
4,765,220✔
127

128
  // Interpolation for energy E1 and EK
129
  int n_energy_out = distribution_[i].e_out.size();
4,765,220✔
130
  int n_discrete = distribution_[i].n_discrete;
4,765,220✔
131
  double E_i_1 = distribution_[i].e_out[n_discrete];
4,765,220✔
132
  double E_i_K = distribution_[i].e_out[n_energy_out - 1];
4,765,220✔
133

134
  n_energy_out = distribution_[i + 1].e_out.size();
4,765,220✔
135
  n_discrete = distribution_[i + 1].n_discrete;
4,765,220✔
136
  double E_i1_1 = distribution_[i + 1].e_out[n_discrete];
4,765,220✔
137
  double E_i1_K = distribution_[i + 1].e_out[n_energy_out - 1];
4,765,220✔
138

139
  double E_1 = E_i_1 + r * (E_i1_1 - E_i_1);
4,765,220✔
140
  double E_K = E_i_K + r * (E_i1_K - E_i_K);
4,765,220✔
141

142
  // Determine outgoing energy bin
143
  n_energy_out = distribution_[l].e_out.size();
4,765,220✔
144
  n_discrete = distribution_[l].n_discrete;
4,765,220✔
145
  double r1 = prn(seed);
4,765,220✔
146
  double c_k = distribution_[l].c[0];
4,765,220✔
147
  int k = 0;
4,765,220✔
148
  int end = n_energy_out - 2;
4,765,220✔
149

150
  // Discrete portion
151
  for (int j = 0; j < n_discrete; ++j) {
4,765,220!
UNCOV
152
    k = j;
×
153
    c_k = distribution_[l].c[k];
×
154
    if (r1 < c_k) {
×
155
      end = j;
×
156
      break;
×
157
    }
158
  }
159

160
  // Continuous portion
161
  double c_k1;
162
  for (int j = n_discrete; j < end; ++j) {
122,277,815✔
163
    k = j;
122,096,791✔
164
    c_k1 = distribution_[l].c[k + 1];
122,096,791✔
165
    if (r1 < c_k1)
122,096,791✔
166
      break;
4,584,196✔
167
    k = j + 1;
117,512,595✔
168
    c_k = c_k1;
117,512,595✔
169
  }
170

171
  double E_l_k = distribution_[l].e_out[k];
4,765,220✔
172
  double p_l_k = distribution_[l].p[k];
4,765,220✔
173
  double km_r, km_a;
174
  if (distribution_[l].interpolation == Interpolation::histogram) {
4,765,220✔
175
    // Histogram interpolation
176
    if (p_l_k > 0.0 && k >= n_discrete) {
4,747,260!
177
      E_out = E_l_k + (r1 - c_k) / p_l_k;
4,747,260✔
178
    } else {
UNCOV
179
      E_out = E_l_k;
×
180
    }
181

182
    // Determine Kalbach-Mann parameters
183
    km_r = distribution_[l].r[k];
4,747,260✔
184
    km_a = distribution_[l].a[k];
4,747,260✔
185

186
  } else {
187
    // Linear-linear interpolation
188
    double E_l_k1 = distribution_[l].e_out[k + 1];
17,960✔
189
    double p_l_k1 = distribution_[l].p[k + 1];
17,960✔
190

191
    double frac = (p_l_k1 - p_l_k) / (E_l_k1 - E_l_k);
17,960✔
192
    if (frac == 0.0) {
17,960!
UNCOV
193
      E_out = E_l_k + (r1 - c_k) / p_l_k;
×
194
    } else {
195
      E_out =
17,960✔
196
        E_l_k +
17,960✔
197
        (std::sqrt(std::max(0.0, p_l_k * p_l_k + 2.0 * frac * (r1 - c_k))) -
17,960✔
198
          p_l_k) /
17,960✔
199
          frac;
200
    }
201

202
    // Determine Kalbach-Mann parameters
203
    km_r = distribution_[l].r[k] +
17,960✔
204
           (E_out - E_l_k) / (E_l_k1 - E_l_k) *
35,920✔
205
             (distribution_[l].r[k + 1] - distribution_[l].r[k]);
17,960✔
206
    km_a = distribution_[l].a[k] +
17,960✔
207
           (E_out - E_l_k) / (E_l_k1 - E_l_k) *
35,920✔
208
             (distribution_[l].a[k + 1] - distribution_[l].a[k]);
17,960✔
209
  }
210

211
  // Now interpolate between incident energy bins i and i + 1
212
  if (k >= n_discrete) {
4,765,220!
213
    if (l == i) {
4,765,220✔
214
      E_out = E_1 + (E_out - E_i_1) * (E_K - E_1) / (E_i_K - E_i_1);
2,373,828✔
215
    } else {
216
      E_out = E_1 + (E_out - E_i1_1) * (E_K - E_1) / (E_i1_K - E_i1_1);
2,391,392✔
217
    }
218
  }
219

220
  // Sampled correlated angle from Kalbach-Mann parameters
221
  if (prn(seed) > km_r) {
4,765,220✔
222
    double T = uniform_distribution(-1., 1., seed) * std::sinh(km_a);
4,687,081✔
223
    mu = std::log(T + std::sqrt(T * T + 1.0)) / km_a;
4,687,081✔
224
  } else {
225
    double r1 = prn(seed);
78,139✔
226
    mu = std::log(r1 * std::exp(km_a) + (1.0 - r1) * std::exp(-km_a)) / km_a;
78,139✔
227
  }
228
}
4,765,220✔
229

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