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

openmc-dev / openmc / 20470828925

23 Dec 2025 08:24PM UTC coverage: 81.898% (-0.04%) from 81.94%
20470828925

Pull #3550

github

web-flow
Merge b067e7f24 into 3f06a42ab
Pull Request #3550: [Point Detector] Add distribution get_pdf functionality

17078 of 23776 branches covered (71.83%)

Branch coverage included in aggregate %.

48 of 212 new or added lines in 13 files covered. (22.64%)

4 existing lines in 2 files now uncovered.

55208 of 64487 relevant lines covered (85.61%)

43370386.44 hits per line

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

89.32
/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 "xtensor/xarray.hpp"
9
#include "xtensor/xview.hpp"
10

11
#include "openmc/hdf5_interface.h"
12
#include "openmc/math_functions.h"
13
#include "openmc/random_dist.h"
14
#include "openmc/random_lcg.h"
15
#include "openmc/search.h"
16
#include "openmc/vector.h"
17

18
namespace openmc {
19

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

24
KalbachMann::KalbachMann(hid_t group)
66,598✔
25
{
26
  // Open incoming energy dataset
27
  hid_t dset = open_dataset(group, "energy");
66,598✔
28

29
  // Get interpolation parameters
30
  xt::xarray<int> temp;
66,598✔
31
  read_attribute(dset, "interpolation", temp);
66,598✔
32

33
  auto temp_b = xt::view(temp, 0); // view of breakpoints
66,598✔
34
  auto temp_i = xt::view(temp, 1); // view of interpolation parameters
66,598✔
35

36
  std::copy(temp_b.begin(), temp_b.end(), std::back_inserter(breakpoints_));
66,598✔
37
  for (const auto i : temp_i)
133,196✔
38
    interpolation_.push_back(int2interp(i));
66,598✔
39
  n_region_ = breakpoints_.size();
66,598✔
40

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

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

55
  xt::xarray<double> eout;
66,598✔
56
  read_dataset(dset, eout);
66,598✔
57
  close_dataset(dset);
66,598✔
58

59
  for (int i = 0; i < n_energy; ++i) {
1,357,207✔
60
    // Determine number of outgoing energies
61
    int j = offsets[i];
1,290,609✔
62
    int n;
63
    if (i < n_energy - 1) {
1,290,609✔
64
      n = offsets[i + 1] - j;
1,224,011✔
65
    } else {
66
      n = eout.shape()[1] - j;
66,598✔
67
    }
68

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

74
    // Copy data
75
    d.e_out = xt::view(eout, 0, xt::range(j, j + n));
1,290,609✔
76
    d.p = xt::view(eout, 1, xt::range(j, j + n));
1,290,609✔
77
    d.c = xt::view(eout, 2, xt::range(j, j + n));
1,290,609✔
78
    d.r = xt::view(eout, 3, xt::range(j, j + n));
1,290,609✔
79
    d.a = xt::view(eout, 4, xt::range(j, j + n));
1,290,609✔
80

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

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

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

114
    distribution_.push_back(std::move(d));
1,290,609✔
115
  } // incoming energies
1,290,609✔
116
}
66,598✔
117

118
void KalbachMann::sample_params(
4,964,423✔
119
  double E_in, double& E_out, double& km_a, double& km_r, uint64_t* seed) const
120
{
121
  // Find energy bin and calculate interpolation factor
122
  int i;
123
  double r;
124
  get_energy_index(energy_, E_in, i, r);
4,964,423✔
125

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

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

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

140
  double E_1 = E_i_1 + r * (E_i1_1 - E_i_1);
4,964,423✔
141
  double E_K = E_i_K + r * (E_i1_K - E_i_K);
4,964,423✔
142

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

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

161
  // Continuous portion
162
  double c_k1;
163
  for (int j = n_discrete; j < end; ++j) {
124,806,087✔
164
    k = j;
124,613,426✔
165
    c_k1 = distribution_[l].c[k + 1];
124,613,426✔
166
    if (r1 < c_k1)
124,613,426✔
167
      break;
4,771,762✔
168
    k = j + 1;
119,841,664✔
169
    c_k = c_k1;
119,841,664✔
170
  }
171

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

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

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

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

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

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

221
void KalbachMann::sample(
4,964,423✔
222
  double E_in, double& E_out, double& mu, uint64_t* seed) const
223
{
224
  double km_r, km_a;
225
  sample_params(E_in, E_out, km_a, km_r, seed);
4,964,423✔
226

227
  // Sampled correlated angle from Kalbach-Mann parameters
228
  if (prn(seed) > km_r) {
4,964,423✔
229
    double T = uniform_distribution(-1., 1., seed) * std::sinh(km_a);
4,884,915✔
230
    mu = std::log(T + std::sqrt(T * T + 1.0)) / km_a;
4,884,915✔
231
  } else {
232
    double r1 = prn(seed);
79,508✔
233
    mu = std::log(r1 * std::exp(km_a) + (1.0 - r1) * std::exp(-km_a)) / km_a;
79,508✔
234
  }
235
}
4,964,423✔
NEW
236
double KalbachMann::sample_energy_and_pdf(
×
237
  double E_in, double mu, double& E_out, uint64_t* seed) const
238
{
239
  double km_r, km_a;
NEW
240
  sample_params(E_in, E_out, km_a, km_r, seed);
×
241

242
  // https://docs.openmc.org/en/latest/methods/neutron_physics.html#equation-KM-pdf-angle
NEW
243
  return km_a / (2 * std::sinh(km_a)) *
×
NEW
244
         (std::cosh(km_a * mu) + km_r * std::sinh(km_a * mu));
×
245
}
246

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