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

openmc-dev / openmc / 23910205296

02 Apr 2026 04:14PM UTC coverage: 81.224% (-0.3%) from 81.567%
23910205296

Pull #3766

github

web-flow
Merge 264dcb1ef into 8223099ed
Pull Request #3766: Approximate multigroup velocity

17579 of 25426 branches covered (69.14%)

Branch coverage included in aggregate %.

24 of 25 new or added lines in 4 files covered. (96.0%)

710 existing lines in 29 files now uncovered.

58015 of 67642 relevant lines covered (85.77%)

31291841.44 hits per line

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

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

3
#include <algorithm> // for move, clamp
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(
1,854✔
16
  pugi::xml_node node)
17
{
18
  // Check for type of angular distribution
19
  std::string type;
1,854✔
20
  if (check_for_node(node, "type"))
1,854!
21
    type = get_node_value(node, "type", true, true);
1,854✔
22
  if (type == "isotropic") {
1,854✔
23
    return UPtrAngle {new Isotropic(node)};
135✔
24
  } else if (type == "monodirectional") {
1,719✔
25
    return UPtrAngle {new Monodirectional(node)};
1,699✔
26
  } else if (type == "mu-phi") {
20!
27
    return UPtrAngle {new PolarAzimuthal(node)};
20✔
28
  } else {
29
    fatal_error(fmt::format(
×
30
      "Invalid angular distribution for external source: {}", type));
31
  }
32
}
1,854✔
33

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

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

51
//==============================================================================
52
// PolarAzimuthal implementation
53
//==============================================================================
54

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

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

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

87
std::pair<Direction, double> PolarAzimuthal::sample(uint64_t* seed) const
13,800✔
88
{
89
  return sample_impl(seed, false);
13,800✔
90
}
91

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

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

104
  // Sample azimuthal angle
105
  auto [phi, phi_wgt] = phi_->sample(seed);
13,800!
106

107
  // Compute either the PDF value or the importance weight
108
  double weight =
13,800✔
109
    return_pdf ? (mu_->evaluate(mu) * phi_->evaluate(phi)) : (mu_wgt * phi_wgt);
13,800!
110

111
  if (mu == 1.0)
13,800✔
112
    return {u_ref_, weight};
414✔
113
  if (mu == -1.0)
13,386✔
114
    return {-u_ref_, weight};
888✔
115

116
  double f = std::sqrt(1 - mu * mu);
12,498✔
117
  return {mu * u_ref_ + f * std::cos(phi) * v_ref_ + f * std::sin(phi) * w_ref_,
12,498✔
118
    weight};
12,498✔
119
}
120

UNCOV
121
double PolarAzimuthal::evaluate(Direction u) const
×
122
{
UNCOV
123
  double mu = std::clamp(u.dot(u_ref_), -1.0, 1.0);
×
UNCOV
124
  double phi = 0.0;
×
UNCOV
125
  double sin_theta_sq = std::max(0.0, 1.0 - mu * mu);
×
126
  if (sin_theta_sq > 0.0) {
×
127
    double sin_theta = std::sqrt(sin_theta_sq);
×
128
    double cos_phi = u.dot(v_ref_) / sin_theta;
×
129
    double sin_phi = u.dot(w_ref_) / sin_theta;
×
UNCOV
130
    phi = std::atan2(sin_phi, cos_phi);
×
UNCOV
131
    if (phi < 0.0)
×
132
      phi += 2.0 * PI;
×
133
  }
134
  return mu_->evaluate(mu) * phi_->evaluate(phi);
×
135
}
136

137
//==============================================================================
138
// Isotropic implementation
139
//==============================================================================
140

141
Isotropic::Isotropic(pugi::xml_node node) : UnitSphereDistribution {node}
135✔
142
{
143
  if (check_for_node(node, "bias")) {
135!
UNCOV
144
    pugi::xml_node bias_node = node.child("bias");
×
UNCOV
145
    std::string bias_type = get_node_value(bias_node, "type", true, true);
×
UNCOV
146
    if (bias_type != "mu-phi") {
×
UNCOV
147
      openmc::fatal_error(
×
148
        "Isotropic distributions may only be biased by a PolarAzimuthal.");
149
    }
UNCOV
150
    auto bias = std::make_unique<PolarAzimuthal>(bias_node);
×
UNCOV
151
    if (bias->mu()->bias() || bias->phi()->bias()) {
×
UNCOV
152
      openmc::fatal_error(
×
153
        "Attempted to bias Isotropic distribution with a biased PolarAzimuthal "
154
        "distribution. Please ensure bias distributions are unbiased.");
155
    }
UNCOV
156
    this->set_bias(std::move(bias));
×
UNCOV
157
  }
×
158
}
135✔
159

160
Direction isotropic_direction(uint64_t* seed)
63,020,412✔
161
{
162
  double phi = uniform_distribution(0., 2.0 * PI, seed);
63,020,412✔
163
  double mu = uniform_distribution(-1., 1., seed);
63,020,412✔
164
  return {mu, std::sqrt(1.0 - mu * mu) * std::cos(phi),
63,020,412✔
165
    std::sqrt(1.0 - mu * mu) * std::sin(phi)};
63,020,412✔
166
}
167

168
std::pair<Direction, double> Isotropic::sample(uint64_t* seed) const
12,134,731✔
169
{
170
  if (bias()) {
12,134,731!
UNCOV
171
    auto [val, eval] = bias()->sample_as_bias(seed);
×
UNCOV
172
    return {val, 1.0 / (4.0 * PI * eval)};
×
173
  } else {
174
    return {isotropic_direction(seed), 1.0};
12,134,731✔
175
  }
176
}
177

UNCOV
178
double Isotropic::evaluate(Direction u) const
×
179
{
UNCOV
180
  return 1.0 / (4.0 * PI);
×
181
}
182

183
//==============================================================================
184
// Monodirectional implementation
185
//==============================================================================
186

187
std::pair<Direction, double> Monodirectional::sample(uint64_t* seed) const
7,823,744✔
188
{
189
  return {u_ref_, 1.0};
7,823,744✔
190
}
191

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