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

openmc-dev / openmc / 20925680857

12 Jan 2026 03:51PM UTC coverage: 82.058% (-0.1%) from 82.198%
20925680857

push

github

web-flow
Source biasing capabilities (#3460)

Co-authored-by: Paul Romano <paul.k.romano@gmail.com>

17208 of 23885 branches covered (72.05%)

Branch coverage included in aggregate %.

479 of 626 new or added lines in 12 files covered. (76.52%)

8 existing lines in 3 files now uncovered.

55640 of 64891 relevant lines covered (85.74%)

43208543.16 hits per line

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

64.81
/src/distribution_multi.cpp
1
#include "openmc/distribution_multi.h"
2

3
#include <algorithm> // for move
4
#include <cmath>     // for sqrt, sin, cos, max
5

6
#include "openmc/constants.h"
7
#include "openmc/error.h"
8
#include "openmc/math_functions.h"
9
#include "openmc/random_dist.h"
10
#include "openmc/random_lcg.h"
11
#include "openmc/xml_interface.h"
12

13
namespace openmc {
14

15
unique_ptr<UnitSphereDistribution> UnitSphereDistribution::create(
3,356✔
16
  pugi::xml_node node)
17
{
18
  // Check for type of angular distribution
19
  std::string type;
3,356✔
20
  if (check_for_node(node, "type"))
3,356!
21
    type = get_node_value(node, "type", true, true);
3,356✔
22
  if (type == "isotropic") {
3,356✔
23
    return UPtrAngle {new Isotropic(node)};
256✔
24
  } else if (type == "monodirectional") {
3,100✔
25
    return UPtrAngle {new Monodirectional(node)};
3,062✔
26
  } else if (type == "mu-phi") {
38!
27
    return UPtrAngle {new PolarAzimuthal(node)};
38✔
28
  } else {
29
    fatal_error(fmt::format(
×
30
      "Invalid angular distribution for external source: {}", type));
31
  }
32
}
3,356✔
33

34
//==============================================================================
35
// UnitSphereDistribution implementation
36
//==============================================================================
37

38
UnitSphereDistribution::UnitSphereDistribution(pugi::xml_node node)
3,356✔
39
{
40
  // Read reference directional unit vector
41
  if (check_for_node(node, "reference_uvw")) {
3,356✔
42
    auto u_ref = get_node_array<double>(node, "reference_uvw");
3,100✔
43
    if (u_ref.size() != 3)
3,100!
44
      fatal_error("Angular distribution reference direction must have "
×
45
                  "three parameters specified.");
46
    u_ref_ = Direction(u_ref.data());
3,100✔
47
  }
3,100✔
48
}
3,356✔
49

50
//==============================================================================
51
// PolarAzimuthal implementation
52
//==============================================================================
53

54
PolarAzimuthal::PolarAzimuthal(Direction u, UPtrDist mu, UPtrDist phi)
×
55
  : UnitSphereDistribution {u}, mu_ {std::move(mu)}, phi_ {std::move(phi)}
×
56
{}
×
57

58
PolarAzimuthal::PolarAzimuthal(pugi::xml_node node)
38✔
59
  : UnitSphereDistribution {node}
38✔
60
{
61
  // Read reference directional unit vector
62
  if (check_for_node(node, "reference_vwu")) {
38!
63
    auto v_ref = get_node_array<double>(node, "reference_vwu");
38✔
64
    if (v_ref.size() != 3)
38!
65
      fatal_error("Angular distribution reference v direction must have "
×
66
                  "three parameters specified.");
67
    v_ref_ = Direction(v_ref.data());
38✔
68
  }
38✔
69
  w_ref_ = u_ref_.cross(v_ref_);
38✔
70
  if (check_for_node(node, "mu")) {
38!
71
    pugi::xml_node node_dist = node.child("mu");
38✔
72
    mu_ = distribution_from_xml(node_dist);
38✔
73
  } else {
74
    mu_ = UPtrDist {new Uniform(-1., 1.)};
×
75
  }
76

77
  if (check_for_node(node, "phi")) {
38!
78
    pugi::xml_node node_dist = node.child("phi");
38✔
79
    phi_ = distribution_from_xml(node_dist);
38✔
80
  } else {
81
    phi_ = UPtrDist {new Uniform(0.0, 2.0 * PI)};
×
82
  }
83
}
38✔
84

85
std::pair<Direction, double> PolarAzimuthal::sample(uint64_t* seed) const
25,300✔
86
{
87
  return sample_impl(seed, false);
25,300✔
88
}
89

NEW
90
std::pair<Direction, double> PolarAzimuthal::sample_as_bias(
×
91
  uint64_t* seed) const
92
{
NEW
93
  return sample_impl(seed, true);
×
94
}
95

96
std::pair<Direction, double> PolarAzimuthal::sample_impl(
25,300✔
97
  uint64_t* seed, bool return_pdf) const
98
{
99
  // Sample cosine of polar angle
100
  auto [mu, mu_wgt] = mu_->sample(seed);
25,300✔
101

102
  // Sample azimuthal angle
103
  auto [phi, phi_wgt] = phi_->sample(seed);
25,300✔
104

105
  // Compute either the PDF value or the importance weight
106
  double weight =
107
    return_pdf ? (mu_->evaluate(mu) * phi_->evaluate(phi)) : (mu_wgt * phi_wgt);
25,300!
108

109
  if (mu == 1.0)
25,300✔
110
    return {u_ref_, weight};
759✔
111
  if (mu == -1.0)
24,541✔
112
    return {-u_ref_, weight};
1,628✔
113

114
  double f = std::sqrt(1 - mu * mu);
22,913✔
115
  return {mu * u_ref_ + f * std::cos(phi) * v_ref_ + f * std::sin(phi) * w_ref_,
45,826✔
116
    weight};
22,913✔
117
}
118

119
//==============================================================================
120
// Isotropic implementation
121
//==============================================================================
122

123
Isotropic::Isotropic(pugi::xml_node node) : UnitSphereDistribution {node}
256✔
124
{
125
  if (check_for_node(node, "bias")) {
256!
NEW
126
    pugi::xml_node bias_node = node.child("bias");
×
NEW
127
    std::string bias_type = get_node_value(bias_node, "type", true, true);
×
NEW
128
    if (bias_type != "mu-phi") {
×
NEW
129
      openmc::fatal_error(
×
130
        "Isotropic distributions may only be biased by a PolarAzimuthal.");
131
    }
NEW
132
    auto bias = std::make_unique<PolarAzimuthal>(bias_node);
×
NEW
133
    if (bias->mu()->bias() || bias->phi()->bias()) {
×
NEW
134
      openmc::fatal_error(
×
135
        "Attempted to bias Isotropic distribution with a biased PolarAzimuthal "
136
        "distribution. Please ensure bias distributions are unbiased.");
137
    }
NEW
138
    this->set_bias(std::move(bias));
×
NEW
139
  }
×
140
}
256✔
141

142
Direction isotropic_direction(uint64_t* seed)
113,011,130✔
143
{
144
  double phi = uniform_distribution(0., 2.0 * PI, seed);
113,011,130✔
145
  double mu = uniform_distribution(-1., 1., seed);
113,011,130✔
146
  return {mu, std::sqrt(1.0 - mu * mu) * std::cos(phi),
113,011,130✔
147
    std::sqrt(1.0 - mu * mu) * std::sin(phi)};
113,011,130✔
148
}
149

150
std::pair<Direction, double> Isotropic::sample(uint64_t* seed) const
19,560,810✔
151
{
152
  if (bias()) {
19,560,810!
NEW
153
    auto [val, eval] = bias()->sample_as_bias(seed);
×
NEW
154
    return {val, 1.0 / (4.0 * PI * eval)};
×
155
  } else {
156
    return {isotropic_direction(seed), 1.0};
19,560,810✔
157
  }
158
}
159

160
//==============================================================================
161
// Monodirectional implementation
162
//==============================================================================
163

164
std::pair<Direction, double> Monodirectional::sample(uint64_t* seed) const
14,335,854✔
165
{
166
  return {u_ref_, 1.0};
14,335,854✔
167
}
168

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